diff --git a/.gitignore b/.gitignore index 08e36c5644..ad0057e107 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .project .settings .checkstyle +.idea bin target /build @@ -9,3 +10,5 @@ target *.class .idea/ orekit.iml +hs_err_pid* +replay_pid* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4eb27d2cd2..92d7a22484 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -132,7 +132,7 @@ performance: needs: - job: deploy:artifacts artifacts: false - trigger: evanward1/orekit-performance + trigger: orekit/orekit-performance only: - develop@orekit/orekit diff --git a/NOTICE.txt b/NOTICE.txt index 474d411d82..d3583b1785 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ OREKIT -Copyright 2002-2022 CS GROUP +Copyright 2002-2023 CS GROUP This product includes software developed by CS GROUP (https://www.csgroup.eu/) @@ -7,6 +7,9 @@ CS GROUP (https://www.csgroup.eu/) This product includes software developed by Bruce R. Bowman (HQ AFSPC, Space Analysis Division) +This product includes software developed by +Luc Maisonobe (Thales Alenia Space) + This product includes software translated from original work developed by David A. Vallado, Paul Crawford, Richard Hujsak, T.S. Kelso diff --git a/README.md b/README.md index a4a9c5ac2f..a97b047858 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ handle them (conversions, propagations, pointing, events detection, orbit determ [![](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) [![](https://sonar.orekit.org/api/project_badges/measure?project=orekit%3Aorekit&metric=alert_status)](https://sonar.orekit.org/dashboard?id=orekit%3Aorekit) [![](https://sonar.orekit.org/api/project_badges/measure?project=orekit%3Aorekit&metric=coverage)](https://sonar.orekit.org/dashboard?id=orekit%3Aorekit) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7249096.svg)](https://doi.org/10.5281/zenodo.7249096) + + ## Download @@ -72,7 +75,7 @@ all released under business friendly FOSS licenses. ### Compile-time/run-time dependencies * [Hipparchus](https://hipparchus.org/), a mathematics library released under - the Apache License, version 2.1. + the Apache License, version 2.0. ### Test-time dependencies diff --git a/build.xml b/build.xml index a9f8b528db..811831194a 100644 --- a/build.xml +++ b/build.xml @@ -2,69 +2,69 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - + + - + diff --git a/checkstyle.xml b/checkstyle.xml index e62fa816a7..c77c5ab684 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -59,7 +59,15 @@ - + + + @@ -74,9 +82,16 @@ MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN"/> + + + + + + - + diff --git a/orekit-eclipse-formatter.xml b/orekit-eclipse-formatter.xml new file mode 100644 index 0000000000..f99532f697 --- /dev/null +++ b/orekit-eclipse-formatter.xml @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 13714b1cb0..c9506f1149 100644 --- a/pom.xml +++ b/pom.xml @@ -5,13 +5,13 @@ org.orekit orekit jar - 11.3.3 - ORbit Extrapolation KIT + 12.0 + OREKIT http://www.orekit.org/ 2002 - OREKIT (ORbits Extrapolation KIT) is a low level space dynamics library. + OREKIT is a low level space dynamics library. It provides basic elements (orbits, dates, attitude, frames ...) and various algorithms to handle them (conversions, analytical and numerical propagation, pointing ...). @@ -20,40 +20,42 @@ UTF-8 UTF-8 - + 4.2.3 - 0.8.8 - 5.1.8 + 0.8.11 + 5.1.9 2.12.1 - 3.2.0 - + 3.2.2 + 9.3 - 3.2.0 - 3.10.1 - 3.4.1 + 3.3.2 + 3.11.0 + 3.6.0 3.3.0 - 3.3.0 + 3.3.1 1.2 - 1.2022.8 - 3.4.1 - 3.3.0 + 1.2023.12 + 3.3.1 3.12.1 + 3.4.5 3.5.2 - 3.2.1 + 3.3.0 2.22.2 - 3.0.0-M6 - 4.8.1 + 3.2.1 + + 4.11.0 1.2.12 - 3.3.0 + 3.4.0 1.6.13 - 3.0.1 - 3.0.1 + 3.1.0 + 3.1.1 <script type="text/x-mathjax-config">MathJax.Hub.Config({ TeX: { extensions: ["autoload.js"]}});</script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_CHTML"></script> - 2.3 - 5.9.1 + 3.0 + 5.10.0 2.2 1.8 1.8 @@ -205,6 +207,13 @@ developer + + Mark Rutten + markrutten + + developer + + @@ -241,6 +250,9 @@ Paul Cefola + + Elisabet Cid-Borobia + Francesco Coccoluto @@ -256,9 +268,18 @@ Christine Fernandez-Martin + + Alberto Ferrero + Mikael Fillastre + + Alberto Fossà + + + Lucas Girodet + Andrew Goetz @@ -289,6 +310,9 @@ Lukas Matt + + Tanner Mills + Vincent Mouraux @@ -322,9 +346,6 @@ Chiara Rusconi - - Mark Rutten - Beatriz Salazar García @@ -462,6 +483,7 @@ true -Xlint:deprecation + -Xlint:unchecked -Xplugin:dataContextPlugin @@ -486,6 +508,7 @@ -Xlint:deprecation + -Xlint:unchecked @@ -616,6 +639,7 @@ maven-surefire-plugin ${orekit.maven-surefire-plugin.version} + @{argLine} -Xmx2048m ${tools.jar.path} @@ -1078,6 +1102,7 @@ 1C + @{argLine} -Xmx2048m diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 20243fcd6a..00cf749f46 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -1,5 +1,5 @@ - ParsedUnitsBehavior + OpmParser --> Opm: "build" + + } + + package utils #CBDBC8 { + package lexical #CCCCC7 { + interface LexicalAnalyzer + enum TokenType { + +START + +ENTRY + +STOP + +RAW_LINE + } + class ParseToken { + +getName() + +getType() + +getContent() + +processAsDouble(scaling, doubleConsumer) + +processAsDate(dateConsumer, contextBinding) + +processAsTimeSystem(timeSystemConsumer) + +processAs...(...Consumer, extraParams) + } + interface "MessageParser" as MessageParser { + +parseMessage(Datasource) + +reset(FileFormat) + +process(ParseToken) + +T build() + } + } + + package parsing #CCCCC7 { + abstract class "AbstractMessageParser" as AbstractMessageParser { + #reset(fileFormat, initialState) + +prepareHeader() + +inHeader() + +finalizeHeader() + +prepareMetadata() + +inMetadata() + +finalizeMetadata() + +prepareData() + +inData() + +finalizeData() + +setFallback(ProcessingState) + } + interface ProcessingState { + +boolean processToken(ParseToken) + } + } + + enum FileFormat { + +KVN + +XML + } } } @@ -141,12 +138,9 @@ AbstractMessageParser ..|> MessageParser ProcessingState <--o AbstractMessageParser : "current" ProcessingState <--o AbstractMessageParser : "fallback" - OpmParser --> Opm : "build" HeaderProcessingState ..|> ProcessingState KvnStructureProcessingState ..|> ProcessingState XmlStructureProcessingState ..|> ProcessingState - ProcessingState <|.. OpmParser - ParserBuilder -right-> OpmParser : build - ParserBuilder o--> ParsedUnitsBehavior + OpmParser .right.|> ProcessingState @enduml diff --git a/src/design/ccsds-structure-class-diagram.puml b/src/design/ccsds-structure-class-diagram.puml index b911071da9..859f521b03 100644 --- a/src/design/ccsds-structure-class-diagram.puml +++ b/src/design/ccsds-structure-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/ccsds-writing-class-diagram.puml b/src/design/ccsds-writing-class-diagram.puml index 93379bd5b2..ee08ad9441 100644 --- a/src/design/ccsds-writing-class-diagram.puml +++ b/src/design/ccsds-writing-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -35,7 +35,7 @@ +getSatellites() } - interface EphemerisFile.EphemerisSegment { + interface "EphemerisFile.EphemerisSegment" as EphemerisSegment { +getMu() +getFrame() +getInertialFrame() @@ -47,7 +47,7 @@ +getPropagator() } - interface EphemerisFile.SatelliteEphemeris { + interface "EphemerisFile.SatelliteEphemeris" as SatelliteEphemeris { +getId() +getMu() +getSegments() @@ -122,8 +122,8 @@ FileFormat <-left- Generator MessageWriter <|.. OpmWriter MessageWriter <|.. OemWriter - EphemerisFile.EphemerisSegment <--o EphemerisFile.SatelliteEphemeris - EphemerisFile.SatelliteEphemeris <--* EphemerisFile + EphemerisSegment <--o SatelliteEphemeris + SatelliteEphemeris <--* EphemerisFile EphemerisFile <-- EphemerisFileWriter OemWriter ..|> EphemerisFileWriter StreamingOemWriter o-right-> OemWriter diff --git a/src/design/celestial-bodies-class-diagram.puml b/src/design/celestial-bodies-class-diagram.puml index d64b1e710a..b70fa17626 100644 --- a/src/design/celestial-bodies-class-diagram.puml +++ b/src/design/celestial-bodies-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/continuous-maneuver-class-diagram.puml b/src/design/continuous-maneuver-class-diagram.puml index aa985863ea..712a4a7501 100644 --- a/src/design/continuous-maneuver-class-diagram.puml +++ b/src/design/continuous-maneuver-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -36,9 +36,6 @@ package forces #DDEBD8 { interface ForceModel - abstract class AbstractForceModel - - ForceModel <|.. AbstractForceModel package maneuvers #CBDBC8 { @@ -52,7 +49,7 @@ interface Maneuver - AbstractForceModel <|-- Maneuver + ForceModel <|.. Maneuver AttitudeProvider <--o Maneuver : "optional attitude override" Maneuver o--> PropulsionModel Maneuver o--> ManeuverTriggers @@ -61,6 +58,6 @@ } - } + } @enduml diff --git a/src/design/custom-parser-class-diagram.puml b/src/design/custom-parser-class-diagram.puml index fc8bf3fd9a..43c9d0a7b2 100644 --- a/src/design/custom-parser-class-diagram.puml +++ b/src/design/custom-parser-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/data-context-class-diagram.puml b/src/design/data-context-class-diagram.puml index e58e075c85..e8d3eb84e6 100644 --- a/src/design/data-context-class-diagram.puml +++ b/src/design/data-context-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/data-filtering-class-diagram.puml b/src/design/data-filtering-class-diagram.puml index 899859e065..a2db1c3abc 100644 --- a/src/design/data-filtering-class-diagram.puml +++ b/src/design/data-filtering-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -46,7 +46,7 @@ DataSource filter(DataSource original) } - interface DataSource.Opener { + interface "DataSource.Opener" as Opener { +boolean rawDataIsBinary() +InputStream openStreamOnce() +Reader openReaderOnce() @@ -59,7 +59,7 @@ FiltersManager "1" o--> "*" DataFilter: applies DataSource <-down- DataFilter : filters - DataSource --> DataSource.Opener + DataSource --> Opener DataFilter <|-- GzipFilter DataFilter <|-- UnixFilter @@ -68,7 +68,7 @@ } - package gnss #DDEBD8 { + package files.rinex #DDEBD8 { class HatanakaCompressFilter DataFilter <|-- HatanakaCompressFilter DataSource.Opener <-left- HatanakaCompressFilter : creates diff --git a/src/design/data-filtering-sequence-diagram.puml b/src/design/data-filtering-sequence-diagram.puml index 4fa76c70f3..13abeb2863 100644 --- a/src/design/data-filtering-sequence-diagram.puml +++ b/src/design/data-filtering-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/data-loaders-data-providers-class-diagram.puml b/src/design/data-loaders-data-providers-class-diagram.puml index 1288e3b96b..abf19b7990 100644 --- a/src/design/data-loaders-data-providers-class-diagram.puml +++ b/src/design/data-loaders-data-providers-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/dsst-partial-derivatives-class-diagram.puml b/src/design/dsst-partial-derivatives-class-diagram.puml index 87803fd819..b91042158b 100644 --- a/src/design/dsst-partial-derivatives-class-diagram.puml +++ b/src/design/dsst-partial-derivatives-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/dsst-propagation-sequence-diagram.puml b/src/design/dsst-propagation-sequence-diagram.puml index 9408eebb12..e32ab229c7 100644 --- a/src/design/dsst-propagation-sequence-diagram.puml +++ b/src/design/dsst-propagation-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/dsst-propagator-class-diagram.puml b/src/design/dsst-propagator-class-diagram.puml index 6ee41fb43a..d4620fc6b7 100644 --- a/src/design/dsst-propagator-class-diagram.puml +++ b/src/design/dsst-propagator-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -62,6 +62,7 @@ DSSTForceModel -right-> AuxiliaryElements DSSTForceModel <|-- DSSTZonal + DSSTForceModel <|-- DSSTJ2SquaredClosedForm DSSTForceModel <|-- DSSTTesseral DSSTForceModel <|-- DSSTThirdBody DSSTForceModel <|-- AbstractGaussianContribution diff --git a/src/design/dsst-tesseral-contribution-class-diagram.puml b/src/design/dsst-tesseral-contribution-class-diagram.puml index fd4582fb48..b158fae2f6 100644 --- a/src/design/dsst-tesseral-contribution-class-diagram.puml +++ b/src/design/dsst-tesseral-contribution-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/dsst-third-body-class-diagram.puml b/src/design/dsst-third-body-class-diagram.puml index 6099d05106..9c0eb86631 100644 --- a/src/design/dsst-third-body-class-diagram.puml +++ b/src/design/dsst-third-body-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/dsst-zonal-contribution-class-diagram.puml b/src/design/dsst-zonal-contribution-class-diagram.puml index fa54f63141..9b8ed0e91a 100644 --- a/src/design/dsst-zonal-contribution-class-diagram.puml +++ b/src/design/dsst-zonal-contribution-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -34,9 +34,13 @@ Package forces #CBDBC8 { interface DSSTForceModel interface ShortPeriodTerms + interface J2SquaredModel DSSTForceModel <|-- DSSTZonal DSSTZonal --> ZonalShortPeriodicCoefficients ShortPeriodTerms <|-- ZonalShortPeriodicCoefficients + DSSTForceModel <|-- DSSTJ2SquaredClosedForm + J2SquaredModel "1" <--* DSSTJ2SquaredClosedForm + J2SquaredModel <|-- ZeisModel } package utilities #CBDBC8 { @@ -51,6 +55,7 @@ package forces.gravity.potential #DDEBD8 { interface UnnormalizedSphericalHarmonicsProvider UnnormalizedSphericalHarmonicsProvider "1" <--* DSSTZonal + UnnormalizedSphericalHarmonicsProvider "1" <--* DSSTJ2SquaredClosedForm } } diff --git a/src/design/ephemeris-generation-sequence-diagram.puml b/src/design/ephemeris-generation-sequence-diagram.puml index 0d1b14bb80..6c87e76224 100644 --- a/src/design/ephemeris-generation-sequence-diagram.puml +++ b/src/design/ephemeris-generation-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/events-class-diagram.puml b/src/design/events-class-diagram.puml index cdbe23612b..c66c4527a9 100644 --- a/src/design/events-class-diagram.puml +++ b/src/design/events-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -39,10 +39,10 @@ package handlers #CBDBC8 { - interface "EventHandler" as EventHandler_T_ { - +init(SpacecraftState s0, AbsoluteDate target, T detector) - +Action eventOccurred(SpacecraftState s, T detector, boolean increasing) - +SpacecraftState resetState(T detector, SpacecraftState oldState) + interface EventHandler { + +init(SpacecraftState s0, AbsoluteDate target, EventDetector detector) + +Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) + +SpacecraftState resetState(EventDetector detector, SpacecraftState oldState) } enum Action { @@ -52,10 +52,10 @@ +CONTINUE } - ContinueOnEvent --|> EventHandler_T_ - StopOnEvent --|> EventHandler_T_ - StopOnDecreasingEvent --|> EventHandler_T_ - StopOnIncreasingEvent --|> EventHandler_T_ + ContinueOnEvent --|> EventHandler + StopOnEvent --|> EventHandler + StopOnDecreasingEvent --|> EventHandler + StopOnIncreasingEvent --|> EventHandler } @@ -77,12 +77,12 @@ } EventDetector <--* "1" EventShifter - class "AbstractDetector" as AbstractDetector_T_ { + class "AbstractDetector>" as AbstractDetector_T_ { +T withMaxCheck(double maxCheck) +T withThreshold(double threshold) +T withMaxIter(int maxIter) - +T withHandler(EventHandler handler) - +EventHandler getHandler() + +T withHandler(EventHandler handler) + +EventHandler getHandler() } enum FilterType { @@ -107,19 +107,19 @@ AbstractDetector_T_ <|-- EventSlopeFilter AbstractDetector_T_ <|-- EventEnablingPredicateFilter - Action <-left- EventHandler_T_ + Action <-left- EventHandler EventDetector <|.. AbstractDetector_T_ AbstractDetector_T_ <|-- EclipseDetector AbstractDetector_T_ <|-- ElevationDetector class "...Detector" as DummyDetector - AbstractDetector_T_ <|-- DummyDetector + DummyDetector --|> AbstractDetector_T_ note top many implementations not displayed for the sake of diagram clarity end note - EventHandler_T_ "1" <--o AbstractDetector_T_ : handler + EventHandler "1" <--o AbstractDetector_T_ : handler } diff --git a/src/design/events-sequence-diagram.puml b/src/design/events-sequence-diagram.puml index cbc202d890..aee010f7a3 100644 --- a/src/design/events-sequence-diagram.puml +++ b/src/design/events-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/extended-semi-analytical-kalman-filter-diagram.puml b/src/design/extended-semi-analytical-kalman-filter-diagram.puml index 841c5b6bd3..7653fa495c 100644 --- a/src/design/extended-semi-analytical-kalman-filter-diagram.puml +++ b/src/design/extended-semi-analytical-kalman-filter-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -121,14 +121,14 @@ - computeOsculatingElements() } - class EskfMeasurementHandler + class SemiAnalyticalMeasurementHandler - OrekitStepHandler <|.. EskfMeasurementHandler + OrekitStepHandler <|.. SemiAnalyticalMeasurementHandler SemiAnalyticalKalmanEstimator <-- SemiAnalyticalKalmanEstimatorBuilder NonLinearProcess <|.. SemiAnalyticalKalmanModel SemiAnalyticalKalmanModel <-- SemiAnalyticalKalmanEstimator ExtendedKalmanFilter <-- SemiAnalyticalKalmanEstimator - EskfMeasurementHandler <-- SemiAnalyticalKalmanModel + SemiAnalyticalMeasurementHandler <-- SemiAnalyticalKalmanModel } diff --git a/src/design/field.puml b/src/design/field.puml index c5858064bf..cbab46dbe3 100644 --- a/src/design/field.puml +++ b/src/design/field.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/frames-class-diagram.puml b/src/design/frames-class-diagram.puml index f9a5a0e5cd..3b00bab134 100644 --- a/src/design/frames-class-diagram.puml +++ b/src/design/frames-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/generic-ephemeris-writing-class-diagram.puml b/src/design/generic-ephemeris-writing-class-diagram.puml index abfa86f1a4..c9d1451432 100644 --- a/src/design/generic-ephemeris-writing-class-diagram.puml +++ b/src/design/generic-ephemeris-writing-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -73,7 +73,7 @@ +getSatellites() } - interface EphemerisFile.SatelliteEphemeris { + interface "EphemerisFile.SatelliteEphemeris" as SatelliteEphemeris { +getId() +getMu() +getStart() @@ -82,7 +82,7 @@ +getPropagator() } - interface EphemerisFile.EphemerisSegment { + interface "EphemerisFile.EphemerisSegment" as EphemerisSegment { +getMu() +getFrame() +getInertialFrame() @@ -98,8 +98,8 @@ CPF ..|> EphemerisFile CPFWriter <--o StreamingCpfWriter EphemerisFileWriter <|.. CPFWriter - EphemerisFile.SatelliteEphemeris <--* EphemerisFile - EphemerisFile.SatelliteEphemeris o--> EphemerisFile.EphemerisSegment + SatelliteEphemeris <--* EphemerisFile + SatelliteEphemeris o--> EphemerisSegment DataSource <-- EphemerisFileParser EphemerisFile <|.. OrekitEphemerisFile @@ -132,8 +132,8 @@ interface Propagator interface BoundedPropagator Propagator <|.. BoundedPropagator - BoundedPropagator <-left- EphemerisFile.SatelliteEphemeris - BoundedPropagator <-left- EphemerisFile.EphemerisSegment + BoundedPropagator <-left- SatelliteEphemeris + BoundedPropagator <-left- EphemerisSegment package sampling #CBDBC8 { interface OrekitFixedStepHandler diff --git a/src/design/gnss-antenna-class-diagram.puml b/src/design/gnss-antenna-class-diagram.puml index 313868a6f5..a297683e93 100644 --- a/src/design/gnss-antenna-class-diagram.puml +++ b/src/design/gnss-antenna-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/gnss-rinex-class-diagram.puml b/src/design/gnss-rinex-class-diagram.puml index 85197bd06d..7b3619ae35 100644 --- a/src/design/gnss-rinex-class-diagram.puml +++ b/src/design/gnss-rinex-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -28,41 +28,8 @@ skinparam linetype ortho package org.orekit.gnss #ECEBD8 { - - class RinexObservationLoader { - +List getObservationDataSets() - } - - enum SatelliteSystem { - +GPS - +GLONASS - +GALILEO - +BEIDOU - +QZSS - +IRNSS - +SBAS - +MIXED - } - - enum ObservationType { - +C1 - +C5 - +C6 - ... - +L1C - ... - +S8X - +getMeasurementType() - +getFrequency(SatelliteSystem) - } - - enum MeasurementType { - +PSEUDO_RANGE - +CARRIER_PHASE - +DOPPLER - +SIGNAL_STRENGTH - } - + class SatInSystem + enum Frequency { +G01 +... @@ -74,29 +41,64 @@ +double getRatio() +double getMHzFrequency() } + } - class ObservationData { - +ObservationType getObservationType() - +double getValue() + package org.orekit.files.rinex #ECEBD8 { + + class "RinexFile" as RinexFile { + +T getHeader() + +List getComments() } - class ObservationDataSet { - +RinexHeader getHeader() - +AbsoluteDate getDate() - +SatelliteSystem getSatelliteSystem() - +int getPrnNumber() - +double getRcvrClkOffset() - +List getObservationData() + package observation #DDEBD8 { + + class RinexObservationParser + + enum ObservationType { + +C1 + +C5 + +C6 + ... + +L1C + ... + +S8X + +getMeasurementType() + +getFrequency(SatelliteSystem) + } + + enum MeasurementType { + +PSEUDO_RANGE + +CARRIER_PHASE + +DOPPLER + +SIGNAL_STRENGTH + +COMBINED_RANGE_PHASE + } + + class ObservationData { + +ObservationType getObservationType() + +double getValue() + } + + class ObservationDataSet { + +AbsoluteDate getDate() + +SatInSystem getSatellite() + +int getEventFlag() + +double getRcvrClkOffset() + +List getObservationData() + } + + ObservationDataSet *-left-> "1" SatInSystem + RinexObservationHeader *--> "1" SatInSystem + RinexObservationHeader "*" <--* ObservationDataSet + RinexObservation *--> "*" ObservationDataSet + ObservationDataSet *--> "*" ObservationData + ObservationType "1" <-left-* ObservationData + ObservationType *--> "1" MeasurementType + ObservationType *--> "*" Frequency + RinexObservation --|> RinexFile + } - ObservationDataSet *-left-> "1" SatelliteSystem - RinexObservationHeader *--> "1" SatelliteSystem - RinexObservationHeader "*" <--* ObservationDataSet - RinexObservationLoader *--> "*" ObservationDataSet - ObservationDataSet *--> "*" ObservationData - ObservationType "1" <-left-* ObservationData - ObservationType *--> "1" MeasurementType - ObservationType *--> "*" Frequency } @enduml diff --git a/src/design/kalman-overview-class-diagram.puml b/src/design/kalman-overview-class-diagram.puml index b9e4a5be4e..15d7316400 100644 --- a/src/design/kalman-overview-class-diagram.puml +++ b/src/design/kalman-overview-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/maneuver-triggers-class-diagram.puml b/src/design/maneuver-triggers-class-diagram.puml index 75612582b4..ae6f56d21b 100644 --- a/src/design/maneuver-triggers-class-diagram.puml +++ b/src/design/maneuver-triggers-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -60,7 +60,7 @@ EventDetector <--o ManeuverTriggers FieldEventDetector <--o ManeuverTriggers - ManeuverTriggers <|.. AbstractManeuverTriggers + AbstractManeuverTriggers ..|> ManeuverTriggers AbstractManeuverTriggers <|-- IntervalEventTrigger_T AbstractManeuverTriggers <|-- StartStopEventsTrigger_A_O IntervalEventTrigger_T <|-- DateBasedManeuverTriggers diff --git a/src/design/measurements-generation-class-diagram.puml b/src/design/measurements-generation-class-diagram.puml index c6ddcfdd90..5f38a445e5 100644 --- a/src/design/measurements-generation-class-diagram.puml +++ b/src/design/measurements-generation-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -33,16 +33,39 @@ package org.orekit #ECEBD8 { - package propagation.events #DDEBD8 { - interface EventsDetector - note top - ground visibility, - ground at night, - sunlit satellite, - inter sat direct view, - boolean combination... - end note + package propagation #DDEBD8 { + + package events #CBDBC8 { + interface EventsDetector + note top + ground visibility, + ground at night, + sunlit satellite, + inter sat direct view, + boolean combination... + end note + } + + interface Propagator + class PropagatorsParallelizer { + propagate() + } + Propagator <-- PropagatorsParallelizer + + } + + package time #DDEBD8 { + interface DatesSelector { + +selectDates(interval) + } + class FixedStepSelector + class BurstSelector + + + DatesSelector <|.. FixedStepSelector + DatesSelector <|.. BurstSelector } + package estimation.measurements #DDEBD8 { package generation #CBDBC8 { @@ -50,10 +73,10 @@ interface "MeasurementBuilder" as MeasurementBuilder_T_ { +addModifier(estimationModifier) +T build(spacecraftStates) - } + } class "...MeasurementBuilder" as XXXMeasurementBuilder - note right + note left one for each measurement type end note @@ -61,8 +84,9 @@ class Generator { +ObservableSatellite addPropagator(Propagator) +Propagator getPropagator(index) - +addScheduler(Scheduler) - +SortedSet generate(startDate, endDate) + +addScheduler(scheduler) + +addSubscriber(subscriber) + +generate(startDate, endDate) } enum SignSemantic { @@ -73,24 +97,39 @@ class "ContinuousScheduler" as ContinuousScheduler_T_ interface "Scheduler" as Scheduler_T_ { + +MeasurementBuilder_T_ getBuilder() +SortedSet generate(interpolators) } + interface GeneratedMeasurementSubscriber { + +init(start, end) + +handleGeneratedMeasurement(measurement) + } + + class GatheringSubscriber { + +SortedSet getGeneratedMeasurements() + } + XXXMeasurementBuilder ..|> MeasurementBuilder_T_ MeasurementBuilder_T_ "1" <--* Scheduler_T_ - XXXMeasurementBuilder -up-> RandomVectorGenerator + RandomVectorGenerator <-- XXXMeasurementBuilder SignSemantic "1" <--* EventBasedScheduler_T_ Scheduler_T_ <|.left. EventBasedScheduler_T_ - ContinuousScheduler_T_ .right.|> Scheduler_T_ + Scheduler_T_ <|.. ContinuousScheduler_T_ Scheduler_T_ "*" <--* Generator + Generator *--> GeneratedMeasurementSubscriber + GeneratedMeasurementSubscriber <|-- GatheringSubscriber + EventsDetector <--* EventBasedScheduler_T_ - EventBasedScheduler_T_ *-right-> "1" EventsDetector + Generator --> PropagatorsParallelizer + Propagator "*" <--* Generator + } interface "EstimationModifier" as EstimationModifier_T_ interface "ObservedMeasurement" as ObservedMeasurement_T_ class "...Measurement" as XXXMeasurement - note bottom + note top this box represents any measurement type (range, range-rate, ...) @@ -99,40 +138,12 @@ EstimationModifier_T_ "*" <-left-* ObservedMeasurement_T_ ObservedMeasurement_T_ <|.. XXXMeasurement - XXXMeasurementBuilder --> XXXMeasurement + XXXMeasurement <-left- XXXMeasurementBuilder EstimationModifier_T_ "*" <--* XXXMeasurementBuilder + Scheduler_T_ *--> "1" DatesSelector } - - package propagation #DDEBD8 { - interface Propagator - class PropagatorsParallelizer { - propagate() - } - Generator --> PropagatorsParallelizer - Generator *--> "*" Propagator - Propagator <-left- PropagatorsParallelizer - } - - package time #DDEBD8 { - class FixedStepSelector - class BurstSelector - interface DatesSelector { - +selectDates(interval) - } - - - FixedStepSelector --|> DatesSelector - BurstSelector --|> DatesSelector - Scheduler_T_ *--> "1" DatesSelector - } - - } - - package mission.specific #C4D2C5 { - class "CustomScheduler" as CustomScheduler_T_ #D5E0D5/E2EBE2 - Scheduler_T_ <|.up. CustomScheduler_T_ } @enduml diff --git a/src/design/metric-ntrip-class-diagram.puml b/src/design/metric-ntrip-class-diagram.puml index 7f0b47c13b..2ade33d229 100644 --- a/src/design/metric-ntrip-class-diagram.puml +++ b/src/design/metric-ntrip-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/metric-parser-class-diagram.puml b/src/design/metric-parser-class-diagram.puml index 3e91d35af9..4e72d24d88 100644 --- a/src/design/metric-parser-class-diagram.puml +++ b/src/design/metric-parser-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -60,6 +60,13 @@ class RtcmEphemerisMessage } + package correction #DDEBD8 { + class Rtcm1060 + class Rtcm1066 + class RtcmYYYY + class RtcmCorrectionMessage + } + class RtcmMessage { + List getData() } @@ -78,9 +85,13 @@ SsrIm201 --|> SsrMessage RtcmMessage --|> ParsedMessage RtcmEphemerisMessage --|> RtcmMessage + RtcmCorrectionMessage --|> RtcmMessage Rtcm1019 --|> RtcmEphemerisMessage Rtcm1020 --|> RtcmEphemerisMessage RtcmXXXX --|> RtcmEphemerisMessage + Rtcm1060 --|> RtcmCorrectionMessage + Rtcm1066 --|> RtcmCorrectionMessage + RtcmYYYY --|> RtcmCorrectionMessage } @@ -105,6 +116,8 @@ +RTCM_1042 +RTCM_1044 +RTCM_1045 + +... + +RTCM_1243 {static} +MessageType getMessageType(String messageNumber) } diff --git a/src/design/numerical-propagation-sequence-diagram.puml b/src/design/numerical-propagation-sequence-diagram.puml index f37a7ed043..f3ea8a153e 100644 --- a/src/design/numerical-propagation-sequence-diagram.puml +++ b/src/design/numerical-propagation-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/orbit-determination-measurements-class-diagram.puml b/src/design/orbit-determination-measurements-class-diagram.puml index 3ab6891815..3c95300789 100644 --- a/src/design/orbit-determination-measurements-class-diagram.puml +++ b/src/design/orbit-determination-measurements-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/orbit-determination-overview-class-diagram.puml b/src/design/orbit-determination-overview-class-diagram.puml index d717a3561d..96a1e1bd1d 100644 --- a/src/design/orbit-determination-overview-class-diagram.puml +++ b/src/design/orbit-determination-overview-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/orbit-determination-parameters-class-diagram.puml b/src/design/orbit-determination-parameters-class-diagram.puml index d899ceb243..2c2a982136 100644 --- a/src/design/orbit-determination-parameters-class-diagram.puml +++ b/src/design/orbit-determination-parameters-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/orbits-class-diagram.puml b/src/design/orbits-class-diagram.puml index 261b9d4069..26570094ee 100644 --- a/src/design/orbits-class-diagram.puml +++ b/src/design/orbits-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -51,7 +51,7 @@ +Orbit mapArrayToOrbit(...) } - enum PositionAngle { + enum PositionAngleType { +MEAN +ECCENTRIC +TRUE @@ -64,14 +64,14 @@ +double getKeplerianPeriod() +double getKeplerianMeanMotion() +double get...() - +void getJacobianWrtCartesian(PositionAngle type, double[][] jacobian) - +void getJacobianWrtParameters(PositionAngle type, double[][] jacobian) + +void getJacobianWrtCartesian(PositionAngleType type, double[][] jacobian) + +void getJacobianWrtParameters(PositionAngleType type, double[][] jacobian) } OrbitType --> Orbit : convert OrbitType <--* Orbit - PositionAngle <-- Orbit - OrbitType --> PositionAngle + PositionAngleType <-- Orbit + OrbitType --> PositionAngleType Orbit <|-- KeplerianOrbit Orbit <|-- CircularOrbit Orbit <|-- CartesianOrbit diff --git a/src/design/partial-derivatives-class-diagram.puml b/src/design/partial-derivatives-class-diagram.puml index 55ac49ccae..bd3102d449 100644 --- a/src/design/partial-derivatives-class-diagram.puml +++ b/src/design/partial-derivatives-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/propagation-class-diagram.puml b/src/design/propagation-class-diagram.puml index 2c2c3406bd..75b821a0ea 100644 --- a/src/design/propagation-class-diagram.puml +++ b/src/design/propagation-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/propagator-conversion-class-diagram.puml b/src/design/propagator-conversion-class-diagram.puml index 28500eadf2..62fece80a2 100644 --- a/src/design/propagator-conversion-class-diagram.puml +++ b/src/design/propagator-conversion-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/propulsion-class-diagram.puml b/src/design/propulsion-class-diagram.puml index 24018c12c8..422f3db032 100644 --- a/src/design/propulsion-class-diagram.puml +++ b/src/design/propulsion-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -33,6 +33,10 @@ interface AttitudeProvider } + package utils #DDEBD8 { + class TimeSpanMap + } + package forces.maneuvers.propulsion #DDEBD8 { interface PropulsionModel { @@ -41,6 +45,8 @@ +getAcceleration() +getMassDerivatives() +getParametersDrivers() + +getEventsDetectors() + +getFieldEventsDetectors() } interface ThrustPropulsionModel { @@ -58,19 +64,30 @@ abstract class AbstractConstantThrustPropulsionModel class BasicConstantThrustPropulsionModel class ScaledConstantThrustPropulsionModel + class ProfileThrustPropulsionModel + class PolynomialThrustSegment class ThrustDirectionAndAttitudeProvider PropulsionModel <|.. ThrustPropulsionModel ThrustPropulsionModel <|-- AbstractConstantThrustPropulsionModel + ThrustPropulsionModel <|-right- ProfileThrustPropulsionModel AbstractConstantThrustPropulsionModel <|-- BasicConstantThrustPropulsionModel AbstractConstantThrustPropulsionModel <|-- ScaledConstantThrustPropulsionModel AttitudeProvider <|.. ThrustDirectionAndAttitudeProvider ThrustDirectionAndAttitudeProvider <-- "1" ThrustDirectionProvider + TimeSpanMap <-- ProfileThrustPropulsionModel + TimeSpanMap --> PolynomialThrustSegment + } - } + } + + package org.hipparchus.analysis.polynomials #ECEBD8 { + class PolynomialFunction + PolynomialThrustSegment -right-> PolynomialFunction + } @enduml diff --git a/src/design/sampling-class-diagram.puml b/src/design/sampling-class-diagram.puml index 2bb40cea27..76b1e484f4 100644 --- a/src/design/sampling-class-diagram.puml +++ b/src/design/sampling-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/small-maneuver-class-diagram.puml b/src/design/small-maneuver-class-diagram.puml index 723a5b1436..1e8711f93d 100644 --- a/src/design/small-maneuver-class-diagram.puml +++ b/src/design/small-maneuver-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/small-maneuver-sequence-diagram.puml b/src/design/small-maneuver-sequence-diagram.puml index 6e39ec1525..47c7652680 100644 --- a/src/design/small-maneuver-sequence-diagram.puml +++ b/src/design/small-maneuver-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/tessellation-class-diagram.puml b/src/design/tessellation-class-diagram.puml index fea85dda7a..1a40d17bd3 100644 --- a/src/design/tessellation-class-diagram.puml +++ b/src/design/tessellation-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/time-class-diagram.puml b/src/design/time-class-diagram.puml index f823f33878..995d4ca865 100644 --- a/src/design/time-class-diagram.puml +++ b/src/design/time-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/top-packages.puml b/src/design/top-packages.puml index 2f91c46035..b7a6397a35 100644 --- a/src/design/top-packages.puml +++ b/src/design/top-packages.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. @@ -48,6 +48,8 @@ } package propagation #DDEBD8 { } + package ssa #DDEBD8 { + } package estimation #DDEBD8 { } package time #DDEBD8 { diff --git a/src/design/unscented-kalman-filter-diagram.puml b/src/design/unscented-kalman-filter-diagram.puml index 10c4343fb4..0ae9f410d4 100644 --- a/src/design/unscented-kalman-filter-diagram.puml +++ b/src/design/unscented-kalman-filter-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/unscented-semi-analytical-kalman-filter-diagram.puml b/src/design/unscented-semi-analytical-kalman-filter-diagram.puml index c121c7d5ae..94dc9c0abe 100644 --- a/src/design/unscented-semi-analytical-kalman-filter-diagram.puml +++ b/src/design/unscented-semi-analytical-kalman-filter-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/utils-class-diagram.puml b/src/design/utils-class-diagram.puml index 67ed37a1b5..fc0d7d9bb6 100644 --- a/src/design/utils-class-diagram.puml +++ b/src/design/utils-class-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/with-step-handlers-sequence-diagram.puml b/src/design/with-step-handlers-sequence-diagram.puml index 7dd12820e2..b6a4466ceb 100644 --- a/src/design/with-step-handlers-sequence-diagram.puml +++ b/src/design/with-step-handlers-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/design/without-step-handlers-sequence-diagram.puml b/src/design/without-step-handlers-sequence-diagram.puml index 2d8c7c096e..66455c4758 100644 --- a/src/design/without-step-handlers-sequence-diagram.puml +++ b/src/design/without-step-handlers-sequence-diagram.puml @@ -1,4 +1,4 @@ -' Copyright 2002-2022 CS GROUP +' Copyright 2002-2023 CS GROUP ' Licensed to CS GROUP (CS) under one or more ' contributor license agreements. See the NOTICE file distributed with ' this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/annotation/package-info.java b/src/main/java/org/orekit/annotation/package-info.java index 95442051f5..8c6b06b685 100644 --- a/src/main/java/org/orekit/annotation/package-info.java +++ b/src/main/java/org/orekit/annotation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java b/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java index 039328d0c7..f2793c1001 100644 --- a/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java +++ b/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,6 +22,8 @@ import java.util.TreeMap; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; @@ -100,6 +102,19 @@ public > FieldAttitude getAttitude(final Fi } /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + return getAttitudeProvider(date).getAttitudeRotation(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return getAttitudeProvider(date.toAbsoluteDate()).getAttitudeRotation(pvProv, date, frame); + } + @Override public AbsoluteDate getMinDate() { return providers.firstEntry().getValue().getMinDate(); diff --git a/src/main/java/org/orekit/attitudes/Attitude.java b/src/main/java/org/orekit/attitudes/Attitude.java index f6aa1a98aa..5c3b519023 100644 --- a/src/main/java/org/orekit/attitudes/Attitude.java +++ b/src/main/java/org/orekit/attitudes/Attitude.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,9 +17,6 @@ package org.orekit.attitudes; import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; @@ -27,11 +24,9 @@ import org.orekit.frames.Frame; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeInterpolable; import org.orekit.time.TimeShiftable; import org.orekit.time.TimeStamped; import org.orekit.utils.AngularCoordinates; -import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.TimeStampedAngularCoordinates; @@ -53,7 +48,7 @@ */ public class Attitude - implements TimeStamped, TimeShiftable, TimeInterpolable, Serializable { + implements TimeStamped, TimeShiftable, Serializable { /** Serializable UID. */ private static final long serialVersionUID = 20140611L; @@ -195,24 +190,4 @@ public Vector3D getRotationAcceleration() { return orientation.getRotationAcceleration(); } - /** {@inheritDoc} - *

- * The interpolated instance is created by polynomial Hermite interpolation - * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation. - *

- *

- * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

- */ - public Attitude interpolate(final AbsoluteDate interpolationDate, final Stream sample) { - final List datedPV = - sample.map(attitude -> attitude.orientation).collect(Collectors.toList()); - final TimeStampedAngularCoordinates interpolated = - TimeStampedAngularCoordinates.interpolate(interpolationDate, AngularDerivativesFilter.USE_RR, datedPV); - return new Attitude(referenceFrame, interpolated); - } - } diff --git a/src/main/java/org/orekit/attitudes/AttitudeBuilder.java b/src/main/java/org/orekit/attitudes/AttitudeBuilder.java index 442201b709..4b09b1f397 100644 --- a/src/main/java/org/orekit/attitudes/AttitudeBuilder.java +++ b/src/main/java/org/orekit/attitudes/AttitudeBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/AttitudeInterpolator.java b/src/main/java/org/orekit/attitudes/AttitudeInterpolator.java new file mode 100644 index 0000000000..d9103ede45 --- /dev/null +++ b/src/main/java/org/orekit/attitudes/AttitudeInterpolator.java @@ -0,0 +1,93 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import org.orekit.frames.Frame; +import org.orekit.time.AbstractTimeInterpolator; +import org.orekit.time.TimeInterpolator; +import org.orekit.utils.TimeStampedAngularCoordinates; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Class for attitude interpolation. + *

+ * The type of interpolation used is defined by given time stamped angular coordinates interpolator at construction. + * + * @author Vincent Cucchietti + * @see TimeStampedAngularCoordinates + * @see TimeInterpolator + */ +public class AttitudeInterpolator extends AbstractTimeInterpolator { + + /** Reference frame from which attitude is defined. */ + private final Frame referenceFrame; + + /** Time stamped angular coordinates interpolator. */ + private final TimeInterpolator interpolator; + + /** + * Constructor. + * + * @param referenceFrame reference frame from which attitude is defined + * @param interpolator time stamped angular coordinates interpolator + */ + public AttitudeInterpolator(final Frame referenceFrame, + final TimeInterpolator interpolator) { + super(interpolator.getNbInterpolationPoints(), interpolator.getExtrapolationThreshold()); + this.referenceFrame = referenceFrame; + this.interpolator = interpolator; + } + + /** Get reference frame from which attitude is defined. + * @return reference frame from which attitude is defined + */ + public Frame getReferenceFrame() { + return referenceFrame; + } + + /** Get time stamped angular coordinates interpolator. + * @return time stamped angular coordinates interpolator + */ + public TimeInterpolator getAngularInterpolator() { + return interpolator; + } + + /** {@inheritDoc} */ + @Override + protected Attitude interpolate(final InterpolationData interpolationData) { + + // Convert sample to stream + final Stream sample = interpolationData.getNeighborList().stream(); + + // Express all attitudes in the same reference frame + final Stream consistentSample = + sample.map(attitude -> attitude.withReferenceFrame(referenceFrame)); + + // Map time stamped angular coordinates + final List angularSample = + consistentSample.map(Attitude::getOrientation).collect(Collectors.toList()); + + // Interpolate + final TimeStampedAngularCoordinates interpolated = interpolator.interpolate(interpolationData.getInterpolationDate(), + angularSample); + + return new Attitude(referenceFrame, interpolated); + } +} diff --git a/src/main/java/org/orekit/attitudes/AttitudeProvider.java b/src/main/java/org/orekit/attitudes/AttitudeProvider.java index 0e05c1962c..9b9645ff15 100644 --- a/src/main/java/org/orekit/attitudes/AttitudeProvider.java +++ b/src/main/java/org/orekit/attitudes/AttitudeProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,8 @@ package org.orekit.attitudes; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -35,7 +37,7 @@ public interface AttitudeProvider { * @param pvProv local position-velocity provider around current date * @param date current date * @param frame reference frame from which attitude is computed - * @return attitude attitude on the specified date and position-velocity state + * @return attitude on the specified date and position-velocity state */ Attitude getAttitude(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame); @@ -44,10 +46,36 @@ public interface AttitudeProvider { * @param date current date * @param frame reference frame from which attitude is computed * @param type of the field elements - * @return attitude attitude on the specified date and position-velocity state + * @return attitude on the specified date and position-velocity state * @since 9.0 */ > FieldAttitude getAttitude(FieldPVCoordinatesProvider pvProv, - FieldAbsoluteDate date, - Frame frame); + FieldAbsoluteDate date, + Frame frame); + + /** Compute the attitude-related rotation corresponding to an orbital state. + * @param pvProv local position-velocity provider around current date + * @param date current date + * @param frame reference frame from which attitude is computed + * @return attitude-related rotation on the specified date and position-velocity state + * @since 12.0 + */ + default Rotation getAttitudeRotation(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame) { + return getAttitude(pvProv, date, frame).getRotation(); + } + + /** Compute the attitude-related rotation corresponding to an orbital state. + * @param pvProv local position-velocity provider around current date + * @param date current date + * @param frame reference frame from which attitude is computed + * @param type of the field elements + * @return rotation on the specified date and position-velocity state + * @since 12.0 + */ + default > FieldRotation getAttitudeRotation(FieldPVCoordinatesProvider pvProv, + FieldAbsoluteDate date, + Frame frame) { + return getAttitude(pvProv, date, frame).getRotation(); + } + } diff --git a/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java b/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java index 8c894bbe87..b20aec74f7 100644 --- a/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java +++ b/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/AttitudesSequence.java b/src/main/java/org/orekit/attitudes/AttitudesSequence.java index 9b54a3bfb9..3daab4b3dc 100644 --- a/src/main/java/org/orekit/attitudes/AttitudesSequence.java +++ b/src/main/java/org/orekit/attitudes/AttitudesSequence.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,6 +22,8 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.ode.events.Action; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -31,17 +33,25 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.AdaptableInterval; import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldAdaptableInterval; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.TimeInterpolator; import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.FieldPVCoordinatesProvider; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedFieldAngularCoordinates; +import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator; /** This classes manages a sequence of different attitude providers that are activated * in turn according to switching events. @@ -84,13 +94,13 @@ public class AttitudesSequence implements AttitudeProvider { private transient TimeSpanMap activated; /** Switching events list. */ - private final List> switches; + private final List switches; /** Constructor for an initially empty sequence. */ public AttitudesSequence() { activated = null; - switches = new ArrayList>(); + switches = new ArrayList<>(); } /** Reset the active provider. @@ -102,7 +112,7 @@ public AttitudesSequence() { * @param provider provider to activate */ public void resetActiveProvider(final AttitudeProvider provider) { - activated = new TimeSpanMap(provider); + activated = new TimeSpanMap<>(provider); } /** Register all wrapped switch events to the propagator. @@ -116,7 +126,7 @@ public void resetActiveProvider(final AttitudeProvider provider) { * @param propagator propagator that will handle the events */ public void registerSwitchEvents(final Propagator propagator) { - for (final Switch s : switches) { + for (final Switch s : switches) { propagator.addEventDetector(s); } } @@ -134,7 +144,7 @@ public void registerSwitchEvents(final Propagator propagator) { * @param type of the field elements */ public > void registerSwitchEvents(final Field field, final FieldPropagator propagator) { - for (final Switch sw : switches) { + for (final Switch sw : switches) { propagator.addEventDetector(new FieldEventDetector() { /** {@inheritDoc} */ @@ -158,8 +168,8 @@ public T getThreshold() { /** {@inheritDoc} */ @Override - public T getMaxCheckInterval() { - return field.getZero().add(sw.getMaxCheckInterval()); + public FieldAdaptableInterval getMaxCheckInterval() { + return s -> sw.getMaxCheckInterval().currentInterval(s.toSpacecraftState()); } /** {@inheritDoc} */ @@ -170,14 +180,23 @@ public int getMaxIterationCount() { /** {@inheritDoc} */ @Override - public Action eventOccurred(final FieldSpacecraftState s, final boolean increasing) { - return sw.eventOccurred(s.toSpacecraftState(), increasing); - } - - /** {@inheritDoc} */ - @Override - public FieldSpacecraftState resetState(final FieldSpacecraftState oldState) { - return new FieldSpacecraftState<>(field, sw.resetState(oldState.toSpacecraftState())); + public FieldEventHandler getHandler() { + return new FieldEventHandler() { + /** {@inheritDoc} */ + @Override + public Action eventOccurred(final FieldSpacecraftState s, + final FieldEventDetector detector, + final boolean increasing) { + return sw.eventOccurred(s.toSpacecraftState(), sw, increasing); + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState resetState(final FieldEventDetector detector, + final FieldSpacecraftState oldState) { + return new FieldSpacecraftState<>(field, sw.resetState(sw, oldState.toSpacecraftState())); + } + }; } }); @@ -281,31 +300,44 @@ public void addSwitchingCondition(final AttitudeProvid } // add the switching condition - switches.add(new Switch(switchEvent, switchOnIncrease, switchOnDecrease, - past, future, transitionTime, transitionFilter, handler)); + switches.add(new Switch(switchEvent, switchOnIncrease, switchOnDecrease, + past, future, transitionTime, transitionFilter, handler)); } /** {@inheritDoc} */ + @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { return activated.get(date).getAttitude(pvProv, date, frame); } /** {@inheritDoc} */ + @Override public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { return activated.get(date.toAbsoluteDate()).getAttitude(pvProv, date, frame); } - /** Switch specification. - * @param class type for the generic version - */ - private class Switch implements EventDetector { + /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + return activated.get(date).getAttitudeRotation(pvProv, date, frame); + } + + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return activated.get(date.toAbsoluteDate()).getAttitudeRotation(pvProv, date, frame); + } + + /** Switch specification. */ + private class Switch implements EventDetector, EventHandler { /** Event. */ - private final T event; + private final EventDetector event; /** Event direction triggering the switch. */ private final boolean switchOnIncrease; @@ -343,7 +375,7 @@ private class Switch implements EventDetector { * should match past and future attitude laws * @param switchHandler handler to call for notifying when switch occurs (may be null) */ - Switch(final T event, + Switch(final EventDetector event, final boolean switchOnIncrease, final boolean switchOnDecrease, final AttitudeProvider past, final AttitudeProvider future, final double transitionTime, final AngularDerivativesFilter transitionFilter, @@ -366,7 +398,7 @@ public double getThreshold() { /** {@inheritDoc} */ @Override - public double getMaxCheckInterval() { + public AdaptableInterval getMaxCheckInterval() { return event.getMaxCheckInterval(); } @@ -377,11 +409,10 @@ public int getMaxIterationCount() { } /** {@inheritDoc} */ - public void init(final SpacecraftState s0, - final AbsoluteDate t) { + public void init(final SpacecraftState s0, final AbsoluteDate t) { // reset the transition parameters (this will be done once for each switch, - // despite doing it only once would have sufficient; its not really a problem) + // despite doing it only once would have sufficient; it's not really a problem) forward = t.durationFrom(s0.getDate()) >= 0.0; if (activated.getSpansNumber() > 1) { // remove transitions that will be overridden during upcoming propagation @@ -403,7 +434,12 @@ public double g(final SpacecraftState s) { } /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final boolean increasing) { + public EventHandler getHandler() { + return this; + } + + /** {@inheritDoc} */ + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { final AbsoluteDate date = s.getDate(); if (activated.get(date) == (forward ? past : future) && @@ -423,7 +459,7 @@ public Action eventOccurred(final SpacecraftState s, final boolean increasing) { switchHandler.switchOccurred(past, future, s); } - return event.eventOccurred(s, increasing); + return event.getHandler().eventOccurred(s, event, increasing); } else { @@ -446,22 +482,22 @@ public Action eventOccurred(final SpacecraftState s, final boolean increasing) { switchHandler.switchOccurred(future, past, sState); } - return event.eventOccurred(sState, increasing); + return event.getHandler().eventOccurred(sState, event, increasing); } } else { // trigger the underlying event despite no attitude switch occurred - return event.eventOccurred(s, increasing); + return event.getHandler().eventOccurred(s, event, increasing); } } /** {@inheritDoc} */ @Override - public SpacecraftState resetState(final SpacecraftState oldState) { + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { // delegate to underlying event - return event.resetState(oldState); + return event.getHandler().resetState(event, oldState); } /** Provider for transition phases. @@ -488,14 +524,19 @@ private class TransitionProvider implements AttitudeProvider { public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { - // interpolate between the two boundary attitudes + // Create sample final TimeStampedAngularCoordinates start = - transitionPreceding.withReferenceFrame(frame).getOrientation(); + transitionPreceding.withReferenceFrame(frame).getOrientation(); final TimeStampedAngularCoordinates end = - future.getAttitude(pvProv, transitionEnd, frame).getOrientation(); - final TimeStampedAngularCoordinates interpolated = - TimeStampedAngularCoordinates.interpolate(date, transitionFilter, - Arrays.asList(start, end)); + future.getAttitude(pvProv, transitionEnd, frame).getOrientation(); + final List sample = Arrays.asList(start, end); + + // Create interpolator + final TimeInterpolator interpolator = + new TimeStampedAngularCoordinatesHermiteInterpolator(sample.size(), transitionFilter); + + // interpolate between the two boundary attitudes + final TimeStampedAngularCoordinates interpolated = interpolator.interpolate(date, sample); return new Attitude(frame, interpolated); @@ -506,17 +547,22 @@ public > FieldAttitude getAttitude(final Fi final FieldAbsoluteDate date, final Frame frame) { - // interpolate between the two boundary attitudes + // create sample final TimeStampedFieldAngularCoordinates start = - new TimeStampedFieldAngularCoordinates<>(date.getField(), - transitionPreceding.withReferenceFrame(frame).getOrientation()); + new TimeStampedFieldAngularCoordinates<>(date.getField(), + transitionPreceding.withReferenceFrame(frame).getOrientation()); final TimeStampedFieldAngularCoordinates end = - future.getAttitude(pvProv, - new FieldAbsoluteDate<>(date.getField(), transitionEnd), - frame).getOrientation(); - final TimeStampedFieldAngularCoordinates interpolated = - TimeStampedFieldAngularCoordinates.interpolate(date, transitionFilter, - Arrays.asList(start, end)); + future.getAttitude(pvProv, + new FieldAbsoluteDate<>(date.getField(), transitionEnd), + frame).getOrientation(); + final List> sample = Arrays.asList(start, end); + + // create interpolator + final FieldTimeInterpolator, S> interpolator = + new TimeStampedFieldAngularCoordinatesHermiteInterpolator<>(sample.size(), transitionFilter); + + // interpolate between the two boundary attitudes + final TimeStampedFieldAngularCoordinates interpolated = interpolator.interpolate(date, sample); return new FieldAttitude<>(frame, interpolated); } diff --git a/src/main/java/org/orekit/attitudes/BodyCenterPointing.java b/src/main/java/org/orekit/attitudes/BodyCenterPointing.java index cccd5a8aed..2114387a92 100644 --- a/src/main/java/org/orekit/attitudes/BodyCenterPointing.java +++ b/src/main/java/org/orekit/attitudes/BodyCenterPointing.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java b/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java index badcfaa403..c17140b4ff 100644 --- a/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java +++ b/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java b/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java index 3d7b911d24..01497f8422 100644 --- a/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java +++ b/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/FieldAttitude.java b/src/main/java/org/orekit/attitudes/FieldAttitude.java index 832d188eb2..40816cbe77 100644 --- a/src/main/java/org/orekit/attitudes/FieldAttitude.java +++ b/src/main/java/org/orekit/attitudes/FieldAttitude.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,10 +16,6 @@ */ package org.orekit.attitudes; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldRotation; @@ -30,10 +26,8 @@ import org.orekit.frames.Frame; import org.orekit.frames.Transform; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.time.FieldTimeInterpolable; import org.orekit.time.FieldTimeShiftable; import org.orekit.time.FieldTimeStamped; -import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.FieldAngularCoordinates; import org.orekit.utils.TimeStampedFieldAngularCoordinates; @@ -53,10 +47,11 @@ * @see org.orekit.orbits.Orbit * @see AttitudeProvider * @author Véronique Pommier-Maurussane + * @param type of the field elements */ public class FieldAttitude> - implements FieldTimeStamped, FieldTimeShiftable, T>, FieldTimeInterpolable, T> { + implements FieldTimeStamped, FieldTimeShiftable, T> { /** Reference frame. */ @@ -234,29 +229,6 @@ public FieldVector3D getRotationAcceleration() { return orientation.getRotationAcceleration(); } - /** Get an interpolated instance. - *

- * The interpolated instance is created by polynomial Hermite interpolation - * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation. - *

- *

- * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

- * @param interpolationDate interpolation date - * @param sample sample points on which interpolation should be done - * @return a new instance, interpolated at specified date - */ - public FieldAttitude interpolate(final FieldAbsoluteDate interpolationDate, - final Stream> sample) { - final List> datedPV = - sample.map(attitude -> attitude.orientation).collect(Collectors.toList()); - final TimeStampedFieldAngularCoordinates interpolated = - TimeStampedFieldAngularCoordinates.interpolate(interpolationDate, AngularDerivativesFilter.USE_RR, datedPV); - return new FieldAttitude<>(referenceFrame, interpolated); - } /** * Converts to an Attitude instance. * @return Attitude with same properties diff --git a/src/main/java/org/orekit/attitudes/FieldAttitudeInterpolator.java b/src/main/java/org/orekit/attitudes/FieldAttitudeInterpolator.java new file mode 100644 index 0000000000..f83a2cb785 --- /dev/null +++ b/src/main/java/org/orekit/attitudes/FieldAttitudeInterpolator.java @@ -0,0 +1,97 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.frames.Frame; +import org.orekit.time.AbstractFieldTimeInterpolator; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.utils.TimeStampedFieldAngularCoordinates; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Class for attitude interpolation. + *

+ * The type of interpolation used is defined by given time stamped angular coordinates interpolator at construction. + * + * @param type of the field element + * + * @author Vincent Cucchietti + * @see TimeStampedFieldAngularCoordinates + * @see FieldTimeInterpolator + */ +public class FieldAttitudeInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** Reference frame from which attitude is defined. */ + private final Frame referenceFrame; + + /** Time stamped angular coordinates interpolator. */ + private final FieldTimeInterpolator, KK> interpolator; + + /** + * Constructor. + * + * @param referenceFrame reference frame from which attitude is defined + * @param interpolator time stamped angular coordinates interpolator + */ + public FieldAttitudeInterpolator(final Frame referenceFrame, + final FieldTimeInterpolator, KK> interpolator) { + super(interpolator.getNbInterpolationPoints(), interpolator.getExtrapolationThreshold()); + this.referenceFrame = referenceFrame; + this.interpolator = interpolator; + } + + /** Get reference frame from which attitude is defined. + * @return reference frame from which attitude is defined + */ + public Frame getReferenceFrame() { + return referenceFrame; + } + + /** Get time stamped angular coordinates interpolator. + * @return time stamped angular coordinates interpolator + */ + public FieldTimeInterpolator, KK> getAngularInterpolator() { + return interpolator; + } + + /** {@inheritDoc} */ + @Override + protected FieldAttitude interpolate(final InterpolationData interpolationData) { + + // Convert sample to stream + final Stream> sample = interpolationData.getNeighborList().stream(); + + // Express all attitudes in the same reference frame + final Stream> consistentSample = + sample.map(attitude -> attitude.withReferenceFrame(referenceFrame)); + + // Map time stamped angular coordinates + final List> angularSample = + consistentSample.map(FieldAttitude::getOrientation).collect(Collectors.toList()); + + // Interpolate + final TimeStampedFieldAngularCoordinates interpolated = + interpolator.interpolate(interpolationData.getInterpolationDate(), angularSample); + + return new FieldAttitude<>(referenceFrame, interpolated); + } +} diff --git a/src/main/java/org/orekit/attitudes/FieldInertia.java b/src/main/java/org/orekit/attitudes/FieldInertia.java new file mode 100644 index 0000000000..f51c8a42ee --- /dev/null +++ b/src/main/java/org/orekit/attitudes/FieldInertia.java @@ -0,0 +1,116 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; + +/** Container for inertia of a 3D object. + *

+ * Instances of this class are immutable + *

+ * @param type of the field elements + * @author Luc Maisonobe + * @since 12.0 + */ +public class FieldInertia> { + + /** Inertia along first axis. */ + private final FieldInertiaAxis iA1; + + /** Inertia along second axis. */ + private final FieldInertiaAxis iA2; + + /** Inertia along third axis. */ + private final FieldInertiaAxis iA3; + + /** Simple constructor from principal axes. + * @param iA1 inertia along first axis + * @param iA2 inertia along second axis + * @param iA3 inertia along third axis + */ + FieldInertia(final FieldInertiaAxis iA1, final FieldInertiaAxis iA2, final FieldInertiaAxis iA3) { + this.iA1 = iA1; + this.iA2 = iA2; + this.iA3 = iA3; + } + + /** Swap axes 1 and 2. + *

+ * The instance is unchanged. + *

+ * @return inertia with swapped axes + */ + public FieldInertia swap12() { + return new FieldInertia<>(iA2, iA1, iA3.negate()); + } + + /** Swap axes 1 and 3. + *

+ * The instance is unchanged. + *

+ * @return inertia with swapped axes + */ + public FieldInertia swap13() { + return new FieldInertia<>(iA3, iA2.negate(), iA1); + } + + /** Swap axes 2 and 3. + *

+ * The instance is unchanged. + *

+ * @return inertia with swapped axes + */ + public FieldInertia swap23() { + return new FieldInertia<>(iA1.negate(), iA3, iA2); + } + + /** Get inertia along first axis. + * @return inertia along first axis + */ + public FieldInertiaAxis getInertiaAxis1() { + return iA1; + } + + /** Get inertia along second axis. + * @return inertia along second axis + */ + public FieldInertiaAxis getInertiaAxis2() { + return iA2; + } + + /** Get inertia along third axis. + * @return inertia along third axis + */ + public FieldInertiaAxis getInertiaAxis3() { + return iA3; + } + + /** Compute angular momentum. + * @param rotationRate rotation rate in body frame. + * @return angular momentum in body frame + */ + public FieldVector3D momentum(final FieldVector3D rotationRate) { + final FieldVector3D a1 = iA1.getA(); + final FieldVector3D a2 = iA2.getA(); + final FieldVector3D a3 = iA3.getA(); + return new FieldVector3D<>(iA1.getI().multiply(FieldVector3D.dotProduct(rotationRate, a1)), a1, + iA2.getI().multiply(FieldVector3D.dotProduct(rotationRate, a2)), a2, + iA3.getI().multiply(FieldVector3D.dotProduct(rotationRate, a3)), a3); + } + +} diff --git a/src/main/java/org/orekit/attitudes/FieldInertiaAxis.java b/src/main/java/org/orekit/attitudes/FieldInertiaAxis.java new file mode 100644 index 0000000000..47d311bbd4 --- /dev/null +++ b/src/main/java/org/orekit/attitudes/FieldInertiaAxis.java @@ -0,0 +1,69 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; + + +/** Container for inertial axis. + *

+ * Instances of this class are immutable + *

+ * @param type fof the field elements + * @author Luc Maisonobe + * @since 12.0 + */ +public class FieldInertiaAxis> { + + /** Moment of inertia. */ + private final T i; + + /** Inertia axis. */ + private final FieldVector3D a; + + /** Simple constructor to pair a moment of inertia with its associated axis. + * @param i moment of inertia + * @param a inertia axis + */ + public FieldInertiaAxis(final T i, final FieldVector3D a) { + this.i = i; + this.a = a; + } + + /** Reverse the inertia axis. + * @return new container with reversed axis + */ + public FieldInertiaAxis negate() { + return new FieldInertiaAxis<>(i, a.negate()); + } + + /** Get the moment of inertia. + * @return moment of inertia + */ + public T getI() { + return i; + } + + /** Get the inertia axis. + * @return inertia axis + */ + public FieldVector3D getA() { + return a; + } + +} diff --git a/src/main/java/org/orekit/attitudes/FixedFrameBuilder.java b/src/main/java/org/orekit/attitudes/FixedFrameBuilder.java index 18bf8f0836..5dd61d97a0 100644 --- a/src/main/java/org/orekit/attitudes/FixedFrameBuilder.java +++ b/src/main/java/org/orekit/attitudes/FixedFrameBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/FixedRate.java b/src/main/java/org/orekit/attitudes/FixedRate.java index 8de8590cba..04eebc4d36 100644 --- a/src/main/java/org/orekit/attitudes/FixedRate.java +++ b/src/main/java/org/orekit/attitudes/FixedRate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/InertialProvider.java b/src/main/java/org/orekit/attitudes/FrameAlignedProvider.java similarity index 71% rename from src/main/java/org/orekit/attitudes/InertialProvider.java rename to src/main/java/org/orekit/attitudes/FrameAlignedProvider.java index 13b97bd1c5..793076bdeb 100644 --- a/src/main/java/org/orekit/attitudes/InertialProvider.java +++ b/src/main/java/org/orekit/attitudes/FrameAlignedProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,12 +17,13 @@ package org.orekit.attitudes; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; -import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.Transform; +import org.orekit.frames.FieldTransform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinatesProvider; @@ -31,11 +32,10 @@ /** * This class handles an attitude provider aligned with a frame or a fixed offset to it. - * Contrary to the name the frame need not be an inertial frame. *

Instances of this class are guaranteed to be immutable.

* @author Luc Maisonobe */ -public class InertialProvider implements AttitudeProvider { +public class FrameAlignedProvider implements AttitudeProvider { /** Fixed satellite frame. */ private final Frame satelliteFrame; @@ -45,10 +45,10 @@ public class InertialProvider implements AttitudeProvider { *

This constructor uses the {@link DataContext#getDefault() default data context}. * * @param rotation rotation from EME2000 to the desired satellite frame - * @see #InertialProvider(Rotation, Frame) + * @see #FrameAlignedProvider(Rotation, Frame) */ @DefaultDataContext - public InertialProvider(final Rotation rotation) { + public FrameAlignedProvider(final Rotation rotation) { this(rotation, DataContext.getDefault().getFrames().getEME2000()); } @@ -57,7 +57,7 @@ public InertialProvider(final Rotation rotation) { * * @param frame the reference frame for the attitude. */ - public InertialProvider(final Frame frame) { + public FrameAlignedProvider(final Frame frame) { // it is faster to use the frame directly here rather than call the other // constructor because of the == shortcut in frame.getTransformTo this.satelliteFrame = frame; @@ -70,16 +70,15 @@ public InertialProvider(final Frame frame) { * @param reference frame for {@code rotation}. * @since 10.1 */ - public InertialProvider(final Rotation rotation, - final Frame reference) { + public FrameAlignedProvider(final Rotation rotation, + final Frame reference) { satelliteFrame = new Frame(reference, - new Transform(AbsoluteDate.ARBITRARY_EPOCH, rotation), null, false); + new Transform(AbsoluteDate.ARBITRARY_EPOCH, rotation), null, false); } /** - * Creates an attitude provider aligned with the given frame. The frame does not need - * to be inertial. + * Creates an attitude provider aligned with the given frame. * *

This attitude provider returned by this method is designed to be as fast as * possible for when attitude is irrelevant while still being a valid implementation @@ -92,21 +91,39 @@ public InertialProvider(final Rotation rotation, * @since 11.0 */ public static AttitudeProvider of(final Frame satelliteFrame) { - return new InertialProvider(satelliteFrame); + return new FrameAlignedProvider(satelliteFrame); } /** {@inheritDoc} */ + @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, - final AbsoluteDate date, final Frame frame) { + final AbsoluteDate date, + final Frame frame) { final Transform t = frame.getTransformTo(satelliteFrame, date); return new Attitude(date, frame, t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()); } /** {@inheritDoc} */ + @Override public >FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { final FieldTransform t = frame.getTransformTo(satelliteFrame, date); return new FieldAttitude<>(date, frame, t.getRotation(), t.getRotationRate(), t.getRotationAcceleration()); } + /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + return frame.getStaticTransformTo(satelliteFrame, date).getRotation(); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return frame.getStaticTransformTo(satelliteFrame, date).getRotation(); + } + } diff --git a/src/main/java/org/orekit/attitudes/GroundPointing.java b/src/main/java/org/orekit/attitudes/GroundPointing.java index f92d487bfd..07a3956d63 100644 --- a/src/main/java/org/orekit/attitudes/GroundPointing.java +++ b/src/main/java/org/orekit/attitudes/GroundPointing.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,12 @@ package org.orekit.attitudes; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -87,34 +91,54 @@ public Frame getBodyFrame() { } /** Compute the target point position/velocity in specified frame. - *

- * This method is {@code public} only to allow users to subclass this - * abstract class from other packages. It is not intended to - * be used directly. - *

* @param pvProv provider for PV coordinates * @param date date at which target point is requested * @param frame frame in which observed ground point should be provided * @return observed ground point position (element 0) and velocity (at index 1) * in specified frame */ - public abstract TimeStampedPVCoordinates getTargetPV(PVCoordinatesProvider pvProv, - AbsoluteDate date, Frame frame); + protected abstract TimeStampedPVCoordinates getTargetPV(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame); /** Compute the target point position/velocity in specified frame. * @param pvProv provider for PV coordinates * @param date date at which target point is requested * @param frame frame in which observed ground point should be provided - * @param type of the fiels elements + * @param type of the field elements * @return observed ground point position (element 0) and velocity (at index 1) * in specified frame * @since 9.0 */ - public abstract > TimeStampedFieldPVCoordinates getTargetPV(FieldPVCoordinatesProvider pvProv, - FieldAbsoluteDate date, - Frame frame); + protected abstract > TimeStampedFieldPVCoordinates getTargetPV(FieldPVCoordinatesProvider pvProv, + FieldAbsoluteDate date, + Frame frame); + + /** Compute the target point position in specified frame. + * @param pvProv provider for PV coordinates + * @param date date at which target point is requested + * @param frame frame in which observed ground point should be provided + * @return observed ground point position in specified frame + * @since 12.0 + */ + protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + return getTargetPV(pvProv, date, frame).getPosition(); + } + + /** Compute the target point position in specified frame. + * @param pvProv provider for PV coordinates + * @param date date at which target point is requested + * @param frame frame in which observed ground point should be provided + * @param type of the field elements + * @return observed ground point position in specified frame + * @since 12.0 + */ + protected > FieldVector3D getTargetPosition(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return getTargetPV(pvProv, date, frame).getPosition(); + } /** {@inheritDoc} */ + @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { @@ -155,9 +179,10 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDa } /** {@inheritDoc} */ + @Override public >FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { // satellite-target relative vector final FieldPVCoordinates pva = pvProv.getPVCoordinates(date, inertialFrame); @@ -205,4 +230,67 @@ public >FieldAttitude getAttitude(final Fie } + /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, + final Frame frame) { + + // satellite-target relative vector + final PVCoordinates pva = pvProv.getPVCoordinates(date, inertialFrame); + final Vector3D targetPosition = getTargetPosition(pvProv, date, inertialFrame); + final Vector3D deltaPosition = targetPosition.subtract(pva.getPosition()); + + // spacecraft and target should be away from each other to define a pointing direction + if (deltaPosition.getNorm() == 0.0) { + throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET); + } + + final Vector3D los = deltaPosition.normalize(); + final Vector3D normal = Vector3D.crossProduct(los, pva.getVelocity()).normalize(); + + final Rotation rotation = new Rotation(los, normal, PLUS_K.getPosition(), PLUS_J.getPosition()); + + if (frame != inertialFrame) { + // prepend transform from specified frame to inertial frame + return rotation.compose(frame.getStaticTransformTo(inertialFrame, date).getRotation(), + RotationConvention.VECTOR_OPERATOR); + } + + return rotation; + + } + + + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + // satellite-target relative vector + final FieldPVCoordinates pva = pvProv.getPVCoordinates(date, inertialFrame); + final FieldVector3D targetPosition = getTargetPosition(pvProv, date, inertialFrame); + final FieldVector3D deltaPosition = targetPosition.subtract(pva.getPosition()); + + // spacecraft and target should be away from each other to define a pointing direction + if (deltaPosition.getNorm().getReal() == 0.0) { + throw new OrekitException(OrekitMessages.SATELLITE_COLLIDED_WITH_TARGET); + } + + final FieldVector3D los = deltaPosition.normalize(); + final FieldVector3D normal = FieldVector3D.crossProduct(los, pva.getVelocity()).normalize(); + + final Field field = date.getField(); + final FieldRotation rotation = new FieldRotation<>(los, normal, + new FieldVector3D<>(field, PLUS_K.getPosition()), new FieldVector3D<>(field, PLUS_J.getPosition())); + + if (frame != inertialFrame) { + // prepend transform from specified frame to inertial frame + return rotation.compose(frame.getStaticTransformTo(inertialFrame, date).getRotation(), + RotationConvention.VECTOR_OPERATOR); + } + + return rotation; + + } + } diff --git a/src/main/java/org/orekit/attitudes/Inertia.java b/src/main/java/org/orekit/attitudes/Inertia.java new file mode 100644 index 0000000000..fa9d643599 --- /dev/null +++ b/src/main/java/org/orekit/attitudes/Inertia.java @@ -0,0 +1,114 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; + +/** Container for inertia of a 3D object. + *

+ * Instances of this class are immutable + *

+ * @author Luc Maisonobe + * @since 12.0 + */ +public class Inertia { + + /** Inertia along first axis. */ + private final InertiaAxis iA1; + + /** Inertia along second axis. */ + private final InertiaAxis iA2; + + /** Inertia along third axis. */ + private final InertiaAxis iA3; + + /** Simple constructor from principal axes. + * @param iA1 inertia along first axis + * @param iA2 inertia along second axis + * @param iA3 inertia along third axis + */ + Inertia(final InertiaAxis iA1, final InertiaAxis iA2, final InertiaAxis iA3) { + this.iA1 = iA1; + this.iA2 = iA2; + this.iA3 = iA3; + } + + /** Swap axes 1 and 2. + *

+ * The instance is unchanged. + *

+ * @return inertia with swapped axes + */ + public Inertia swap12() { + return new Inertia(iA2, iA1, iA3.negate()); + } + + /** Swap axes 1 and 3. + *

+ * The instance is unchanged. + *

+ * @return inertia with swapped axes + */ + public Inertia swap13() { + return new Inertia(iA3, iA2.negate(), iA1); + } + + /** Swap axes 2 and 3. + *

+ * The instance is unchanged. + *

+ * @return inertia with swapped axes + */ + public Inertia swap23() { + return new Inertia(iA1.negate(), iA3, iA2); + } + + /** Get inertia along first axis. + * @return inertia along first axis + */ + public InertiaAxis getInertiaAxis1() { + return iA1; + } + + /** Get inertia along second axis. + * @return inertia along second axis + */ + public InertiaAxis getInertiaAxis2() { + return iA2; + } + + /** Get inertia along third axis. + * @return inertia along third axis + */ + public InertiaAxis getInertiaAxis3() { + return iA3; + } + + /** Compute angular momentum. + * @param rotationRate rotation rate in body frame. + * @return angular momentum in body frame + */ + public Vector3D momentum(final Vector3D rotationRate) { + final Vector3D a1 = iA1.getA(); + final Vector3D a2 = iA2.getA(); + final Vector3D a3 = iA3.getA(); + return new Vector3D(iA1.getI() * Vector3D.dotProduct(rotationRate, a1), a1, + iA2.getI() * Vector3D.dotProduct(rotationRate, a2), a2, + iA3.getI() * Vector3D.dotProduct(rotationRate, a3), a3); + } + +} diff --git a/src/main/java/org/orekit/attitudes/InertiaAxis.java b/src/main/java/org/orekit/attitudes/InertiaAxis.java new file mode 100644 index 0000000000..40b6ffc94d --- /dev/null +++ b/src/main/java/org/orekit/attitudes/InertiaAxis.java @@ -0,0 +1,66 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; + +/** Container for inertial axis. + *

+ * Instances of this class are immutable + *

+ * @author Luc Maisonobe + * @since 12.0 + */ +public class InertiaAxis { + + /** Moment of inertia. */ + private final double i; + + /** Inertia axis. */ + private final Vector3D a; + + /** Simple constructor to pair a moment of inertia with its associated axis. + * @param i moment of inertia + * @param a inertia axis + */ + InertiaAxis(final double i, final Vector3D a) { + this.i = i; + this.a = a; + } + + /** Reverse the inertia axis. + * @return new container with reversed axis + */ + public InertiaAxis negate() { + return new InertiaAxis(i, a.negate()); + } + + /** Get the moment of inertia. + * @return moment of inertia + */ + public double getI() { + return i; + } + + /** Get the inertia axis. + * @return inertia axis + */ + public Vector3D getA() { + return a; + } + +} diff --git a/src/main/java/org/orekit/attitudes/LofOffset.java b/src/main/java/org/orekit/attitudes/LofOffset.java index ff77266c6f..e1b1f4cb61 100644 --- a/src/main/java/org/orekit/attitudes/LofOffset.java +++ b/src/main/java/org/orekit/attitudes/LofOffset.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.attitudes; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; @@ -25,7 +26,7 @@ import org.orekit.errors.OrekitMessages; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; -import org.orekit.frames.LOFType; +import org.orekit.frames.LOF; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -45,8 +46,8 @@ */ public class LofOffset implements AttitudeProvider { - /** Type of Local Orbital Frame. */ - private LOFType type; + /** Local Orbital Frame. */ + private final LOF lof; /** Rotation from local orbital frame. */ private final Rotation offset; @@ -57,13 +58,13 @@ public class LofOffset implements AttitudeProvider { /** Create a LOF-aligned attitude. *

* Calling this constructor is equivalent to call - * {@code LofOffset(inertialFrame, LOFType, RotationOrder.XYZ, 0, 0, 0)} + * {@code LofOffset(inertialFrame, LOF, RotationOrder.XYZ, 0, 0, 0)} *

* @param inertialFrame inertial frame with respect to which orbit should be computed - * @param type type of Local Orbital Frame + * @param lof local orbital frame */ - public LofOffset(final Frame inertialFrame, final LOFType type) { - this(inertialFrame, type, RotationOrder.XYZ, 0, 0, 0); + public LofOffset(final Frame inertialFrame, final LOF lof) { + this(inertialFrame, lof, RotationOrder.XYZ, 0, 0, 0); } /** Creates new instance. @@ -76,9 +77,9 @@ public LofOffset(final Frame inertialFrame, final LOFType type) { * to use {@link RotationConvention#FRAME_TRANSFORM} as in the following code snippet: *

*
-     *   LofOffset law          = new LofOffset(inertial, lofType, order, alpha1, alpha2, alpha3);
+     *   LofOffset law          = new LofOffset(inertial, LOF, order, alpha1, alpha2, alpha3);
      *   Rotation  offsetAtt    = law.getAttitude(orbit).getRotation();
-     *   Rotation  alignedAtt   = new LofOffset(inertial, lofType).getAttitude(orbit).getRotation();
+     *   Rotation  alignedAtt   = new LofOffset(inertial, LOF).getAttitude(orbit).getRotation();
      *   Rotation  offsetProper = offsetAtt.compose(alignedAtt.revert(), RotationConvention.VECTOR_OPERATOR);
      *
      *   // note the call to revert and the conventions in the following statement
@@ -94,16 +95,16 @@ public LofOffset(final Frame inertialFrame, final LOFType type) {
      *   System.out.format(Locale.US, "%f == %f%n", alpha3, anglesF[2]);
      * 
* @param inertialFrame inertial frame with respect to which orbit should be computed - * @param type type of Local Orbital Frame + * @param lof local orbital frame * @param order order of rotations to use for (alpha1, alpha2, alpha3) composition * @param alpha1 angle of the first elementary rotation * @param alpha2 angle of the second elementary rotation * @param alpha3 angle of the third elementary rotation */ - public LofOffset(final Frame inertialFrame, final LOFType type, + public LofOffset(final Frame inertialFrame, final LOF lof, final RotationOrder order, final double alpha1, final double alpha2, final double alpha3) { - this.type = type; + this.lof = lof; this.offset = new Rotation(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3).revert(); if (!inertialFrame.isPseudoInertial()) { throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, @@ -114,12 +115,13 @@ public LofOffset(final Frame inertialFrame, final LOFType type, /** {@inheritDoc} */ + @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { // construction of the local orbital frame, using PV from inertial frame final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame); - final Transform inertialToLof = type.transformFromInertial(date, pv); + final Transform inertialToLof = lof.transformFromInertial(date, pv); // take into account the specified start frame (which may not be an inertial one) final Transform frameToInertial = frame.getTransformTo(inertialFrame, date); @@ -134,13 +136,14 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, } /** {@inheritDoc} */ + @Override public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { // construction of the local orbital frame, using PV from inertial frame final FieldPVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame); - final FieldTransform inertialToLof = type.transformFromInertial(date, pv); + final FieldTransform inertialToLof = lof.transformFromInertial(date, pv); // take into account the specified start frame (which may not be an inertial one) final FieldTransform frameToInertial = frame.getTransformTo(inertialFrame, date); @@ -153,4 +156,39 @@ public > FieldAttitude getAttitude(final Fi FieldRotation.applyTo(offset, frameToLof.getRotationAcceleration())); } + + /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + // construction of the local orbital frame, using PV from inertial frame + final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame); + final Rotation inertialToLof = lof.rotationFromInertial(date, pv); + + // take into account the specified start frame (which may not be an inertial one) + final RotationConvention rotationConvention = RotationConvention.FRAME_TRANSFORM; + final Rotation frameToInertial = frame.getStaticTransformTo(inertialFrame, date).getRotation(); + final Rotation frameToLof = frameToInertial.compose(inertialToLof, rotationConvention); + + // compose with offset rotation + return frameToLof.compose(offset, rotationConvention); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + // construction of the local orbital frame, using PV from inertial frame + final FieldPVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame); + final Field field = date.getField(); + final FieldRotation inertialToLof = lof.rotationFromInertial(field, date, pv); + + // take into account the specified start frame (which may not be an inertial one) + final RotationConvention rotationConvention = RotationConvention.FRAME_TRANSFORM; + final FieldRotation frameToInertial = frame.getStaticTransformTo(inertialFrame, date).getRotation(); + final FieldRotation frameToLof = frameToInertial.compose(inertialToLof, rotationConvention); + + // compose with offset rotation + return frameToLof.compose(offset, rotationConvention); + } } diff --git a/src/main/java/org/orekit/attitudes/LofOffsetPointing.java b/src/main/java/org/orekit/attitudes/LofOffsetPointing.java index b59f4b2d70..07fec02910 100644 --- a/src/main/java/org/orekit/attitudes/LofOffsetPointing.java +++ b/src/main/java/org/orekit/attitudes/LofOffsetPointing.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,24 +24,30 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Line; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.orekit.bodies.BodyShape; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.TimeInterpolator; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinatesProvider; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedPVCoordinates; - +import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; /** * This class provides a default attitude provider. @@ -49,7 +55,7 @@ *

* The attitude pointing law is defined by an attitude provider and * the satellite axis vector chosen for pointing. - *

+ *

* @author Véronique Pommier-Maurussane */ public class LofOffsetPointing extends GroundPointing { @@ -88,11 +94,26 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, /** {@inheritDoc} */ @Override public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, final Frame frame) { + final FieldAbsoluteDate date, final Frame frame) { return attitudeLaw.getAttitude(pvProv, date, frame); } /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, final Frame frame) { + return attitudeLaw.getAttitudeRotation(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, final Frame frame) { + return attitudeLaw.getAttitudeRotation(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { @@ -109,10 +130,10 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, shifted, StaticTransform.of( shifted, - pvProv.getPVCoordinates(shifted, frame).getPosition().negate()), + pvProv.getPosition(shifted, frame).negate()), StaticTransform.of( shifted, - attitudeLaw.getAttitude(pvProv, shifted, frame).getRotation())); + attitudeLaw.getAttitudeRotation(pvProv, shifted, frame))); // transform from specified reference frame to body frame final StaticTransform refToBody; @@ -126,9 +147,13 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, } + // create interpolator + final TimeInterpolator interpolator = + new TimeStampedPVCoordinatesHermiteInterpolator(sample.size(), CartesianDerivativesFilter.USE_P); + // use interpolation to compute properly the time-derivatives final TimeStampedPVCoordinates targetBody = - TimeStampedPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_P, sample); + interpolator.interpolate(date, sample); // convert back to caller specified frame return centralRefToBody.getInverse().transformPVCoordinates(targetBody); @@ -136,9 +161,10 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, } /** {@inheritDoc} */ + @Override public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { // sample intersection points in current date neighborhood final double h = 0.1; @@ -149,24 +175,34 @@ public > TimeStampedFieldPVCoordinates getT final FieldAbsoluteDate shifted = date.shiftedBy(i * h); // transform from specified reference frame to spacecraft frame - final FieldTransform refToSc = - new FieldTransform<>(shifted, - new FieldTransform<>(shifted, pvProv.getPVCoordinates(shifted, frame).negate()), - new FieldTransform<>(shifted, attitudeLaw.getAttitude(pvProv, shifted, frame).getOrientation())); + final FieldStaticTransform refToSc = FieldStaticTransform.compose( + shifted, + FieldStaticTransform.of( + shifted, + pvProv.getPVCoordinates(shifted, frame).getPosition().negate()), + FieldStaticTransform.of( + shifted, + attitudeLaw.getAttitudeRotation(pvProv, shifted, frame))); // transform from specified reference frame to body frame - final FieldTransform refToBody = frame.getTransformTo(shape.getBodyFrame(), shifted); + final FieldStaticTransform refToBody; if (i == 0) { - centralRefToBody = refToBody; + refToBody = centralRefToBody = frame.getTransformTo(shape.getBodyFrame(), shifted); + } else { + refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), shifted); } - sample.add(losIntersectionWithBody(new FieldTransform<>(shifted, refToSc.getInverse(), refToBody))); + sample.add(losIntersectionWithBody(FieldStaticTransform.compose(shifted, refToSc.getInverse(), refToBody))); } + // create interpolator + final FieldTimeInterpolator, T> interpolator = + new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(sample.size(), CartesianDerivativesFilter.USE_P); + // use interpolation to compute properly the time-derivatives final TimeStampedFieldPVCoordinates targetBody = - TimeStampedFieldPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_P, sample); + interpolator.interpolate(date, sample); // convert back to caller specified frame return centralRefToBody.getInverse().transformPVCoordinates(targetBody); @@ -212,7 +248,7 @@ private TimeStampedPVCoordinates losIntersectionWithBody(final StaticTransform s * @param type of the field elements * @return intersection point in body frame (only the position is set!) */ - private > TimeStampedFieldPVCoordinates losIntersectionWithBody(final FieldTransform scToBody) { + private > TimeStampedFieldPVCoordinates losIntersectionWithBody(final FieldStaticTransform scToBody) { // compute satellite pointing axis and position/velocity in body frame final FieldVector3D pointingBodyFrame = scToBody.transformVector(satPointingVector); @@ -227,7 +263,7 @@ private > TimeStampedFieldPVCoordinates los // Intersection with body shape final FieldGeodeticPoint gpIntersection = - shape.getIntersectionPoint(pointingLine, pBodyFrame, shape.getBodyFrame(), scToBody.getFieldDate()); + shape.getIntersectionPoint(pointingLine, pBodyFrame, shape.getBodyFrame(), new FieldAbsoluteDate<>(pBodyFrame.getX().getField(), scToBody.getDate())); final FieldVector3D pIntersection = (gpIntersection == null) ? null : shape.transform(gpIntersection); @@ -237,7 +273,7 @@ private > TimeStampedFieldPVCoordinates los throw new OrekitException(OrekitMessages.ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND); } - final FieldVector3D zero = FieldVector3D.getZero(scToBody.getFieldDate().getField()); + final FieldVector3D zero = FieldVector3D.getZero(pBodyFrame.getX().getField()); return new TimeStampedFieldPVCoordinates<>(scToBody.getDate(), pIntersection, zero, zero); diff --git a/src/main/java/org/orekit/attitudes/NadirPointing.java b/src/main/java/org/orekit/attitudes/NadirPointing.java index 2d9d944af3..9f28620005 100644 --- a/src/main/java/org/orekit/attitudes/NadirPointing.java +++ b/src/main/java/org/orekit/attitudes/NadirPointing.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,17 +25,22 @@ import org.orekit.bodies.BodyShape; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.TimeInterpolator; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.FieldPVCoordinatesProvider; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; /** * This class handles nadir pointing attitude provider. @@ -81,15 +86,22 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+h), frame), refToBody.staticShiftedBy(+h))); sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+2 * h), frame), refToBody.staticShiftedBy(+2 * h))); + // create interpolator + final TimeInterpolator interpolator = + new TimeStampedPVCoordinatesHermiteInterpolator(sample.size(), CartesianDerivativesFilter.USE_P); + // use interpolation to compute properly the time-derivatives - return TimeStampedPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_P, sample); + return interpolator.interpolate(date, sample); } /** {@inheritDoc} */ public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { + + // zero + final T zero = date.getField().getZero(); // transform from specified reference frame to body frame final FieldTransform refToBody = frame.getTransformTo(shape.getBodyFrame(), date); @@ -97,14 +109,18 @@ public > TimeStampedFieldPVCoordinates getT // sample intersection points in current date neighborhood final double h = 0.01; final List> sample = new ArrayList<>(); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-2 * h), frame), refToBody.shiftedBy(-2 * h))); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-h), frame), refToBody.shiftedBy(-h))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-2 * h), frame), refToBody.staticShiftedBy(zero.add(-2 * h)))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-h), frame), refToBody.staticShiftedBy(zero.add(-h)))); sample.add(nadirRef(pvProv.getPVCoordinates(date, frame), refToBody)); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+h), frame), refToBody.shiftedBy(+h))); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+2 * h), frame), refToBody.shiftedBy(+2 * h))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+h), frame), refToBody.staticShiftedBy(zero.add(+h)))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+2 * h), frame), refToBody.staticShiftedBy(zero.add(+2 * h)))); + + // create interpolator + final FieldTimeInterpolator, T> interpolator = + new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(sample.size(), CartesianDerivativesFilter.USE_P); // use interpolation to compute properly the time-derivatives - return TimeStampedFieldPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_P, sample); + return interpolator.interpolate(date, sample); } @@ -142,7 +158,7 @@ private TimeStampedPVCoordinates nadirRef(final TimeStampedPVCoordinates scRef, * @since 9.0 */ private > TimeStampedFieldPVCoordinates nadirRef(final TimeStampedFieldPVCoordinates scRef, - final FieldTransform refToBody) { + final FieldStaticTransform refToBody) { final FieldVector3D satInBodyFrame = refToBody.transformPosition(scRef.getPosition()); diff --git a/src/main/java/org/orekit/attitudes/SpinStabilized.java b/src/main/java/org/orekit/attitudes/SpinStabilized.java index bca0c43403..d91de8e459 100644 --- a/src/main/java/org/orekit/attitudes/SpinStabilized.java +++ b/src/main/java/org/orekit/attitudes/SpinStabilized.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/TabulatedLofOffset.java b/src/main/java/org/orekit/attitudes/TabulatedLofOffset.java index 6e2e752010..54776ee3b1 100644 --- a/src/main/java/org/orekit/attitudes/TabulatedLofOffset.java +++ b/src/main/java/org/orekit/attitudes/TabulatedLofOffset.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,10 +24,12 @@ import org.orekit.errors.OrekitMessages; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; -import org.orekit.frames.LOFType; +import org.orekit.frames.LOF; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.TimeInterpolator; import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.FieldPVCoordinatesProvider; @@ -35,7 +37,9 @@ import org.orekit.utils.PVCoordinates; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedFieldAngularCoordinates; +import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator; /** * This class handles an attitude provider interpolating from a predefined table @@ -51,8 +55,8 @@ public class TabulatedLofOffset implements BoundedAttitudeProvider { /** Inertial frame with respect to which orbit should be computed. */ private final Frame inertialFrame; - /** Type of Local Orbital Frame. */ - private LOFType type; + /** Local Orbital Frame. */ + private final LOF type; /** Cached attitude table. */ private final transient ImmutableTimeStampedCache table; @@ -71,20 +75,20 @@ public class TabulatedLofOffset implements BoundedAttitudeProvider { * This constructor uses the first and last point samples as the min and max dates. *

* @param inertialFrame inertial frame with respect to which orbit should be computed - * @param type type of Local Orbital Frame + * @param lof local orbital frame * @param table tabulated attitudes * @param n number of attitude to use for interpolation * @param filter filter for derivatives from the sample to use in interpolation */ - public TabulatedLofOffset(final Frame inertialFrame, final LOFType type, + public TabulatedLofOffset(final Frame inertialFrame, final LOF lof, final List table, final int n, final AngularDerivativesFilter filter) { - this(inertialFrame, type, table, n, filter, table.get(0).getDate(), table.get(table.size() - 1).getDate()); + this(inertialFrame, lof, table, n, filter, table.get(0).getDate(), table.get(table.size() - 1).getDate()); } /** Creates new instance. * @param inertialFrame inertial frame with respect to which orbit should be computed - * @param type type of Local Orbital Frame + * @param lof local orbital frame * @param table tabulated attitudes * @param n number of attitude to use for interpolation * @param minDate min date to use @@ -92,7 +96,7 @@ public TabulatedLofOffset(final Frame inertialFrame, final LOFType type, * @param filter filter for derivatives from the sample to use in interpolation * @since 11.0 */ - public TabulatedLofOffset(final Frame inertialFrame, final LOFType type, + public TabulatedLofOffset(final Frame inertialFrame, final LOF lof, final List table, final int n, final AngularDerivativesFilter filter, final AbsoluteDate minDate, final AbsoluteDate maxDate) { @@ -101,7 +105,7 @@ public TabulatedLofOffset(final Frame inertialFrame, final LOFType type, inertialFrame.getName()); } this.inertialFrame = inertialFrame; - this.type = type; + this.type = lof; this.table = new ImmutableTimeStampedCache(n, table); this.filter = filter; this.minDate = minDate; @@ -122,9 +126,12 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, // get attitudes sample on which interpolation will be performed final List sample = table.getNeighbors(date).collect(Collectors.toList()); + // create interpolator + final TimeInterpolator interpolator = + new TimeStampedAngularCoordinatesHermiteInterpolator(sample.size(), filter); + // interpolate - final TimeStampedAngularCoordinates interpolated = - TimeStampedAngularCoordinates.interpolate(date, filter, sample); + final TimeStampedAngularCoordinates interpolated = interpolator.interpolate(date, sample); // construction of the local orbital frame, using PV from inertial frame final PVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame); @@ -151,9 +158,12 @@ public > FieldAttitude getAttitude(final Fi map(ac -> new TimeStampedFieldAngularCoordinates<>(date.getField(), ac)). collect(Collectors.toList()); + // create interpolator + final FieldTimeInterpolator, T> interpolator = + new TimeStampedFieldAngularCoordinatesHermiteInterpolator<>(sample.size(), filter); + // interpolate - final TimeStampedFieldAngularCoordinates interpolated = - TimeStampedFieldAngularCoordinates.interpolate(date, filter, sample); + final TimeStampedFieldAngularCoordinates interpolated = interpolator.interpolate(date, sample); // construction of the local orbital frame, using PV from inertial frame final FieldPVCoordinates pv = pvProv.getPVCoordinates(date, inertialFrame); diff --git a/src/main/java/org/orekit/attitudes/TabulatedProvider.java b/src/main/java/org/orekit/attitudes/TabulatedProvider.java index 6f7980f164..ae5878afa6 100644 --- a/src/main/java/org/orekit/attitudes/TabulatedProvider.java +++ b/src/main/java/org/orekit/attitudes/TabulatedProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,13 +23,16 @@ import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.TimeInterpolator; import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.FieldPVCoordinatesProvider; import org.orekit.utils.ImmutableTimeStampedCache; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedFieldAngularCoordinates; - +import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator; /** * This class handles an attitude provider interpolating from a predefined table. @@ -98,9 +101,12 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, // get attitudes sample on which interpolation will be performed final List sample = table.getNeighbors(date).collect(Collectors.toList()); + // create interpolator + final TimeInterpolator interpolator = + new TimeStampedAngularCoordinatesHermiteInterpolator(sample.size(), filter); + // interpolate - final TimeStampedAngularCoordinates interpolated = - TimeStampedAngularCoordinates.interpolate(date, filter, sample); + final TimeStampedAngularCoordinates interpolated = interpolator.interpolate(date, sample); // build the attitude return builder.build(frame, pvProv, interpolated); @@ -119,9 +125,12 @@ public > FieldAttitude getAttitude(final Fi map(ac -> new TimeStampedFieldAngularCoordinates<>(date.getField(), ac)). collect(Collectors.toList()); + // create interpolator + final FieldTimeInterpolator, T> interpolator = + new TimeStampedFieldAngularCoordinatesHermiteInterpolator<>(sample.size(), filter); + // interpolate - final TimeStampedFieldAngularCoordinates interpolated = - TimeStampedFieldAngularCoordinates.interpolate(date, filter, sample); + final TimeStampedFieldAngularCoordinates interpolated = interpolator.interpolate(date, sample); // build the attitude return builder.build(frame, pvProv, interpolated); diff --git a/src/main/java/org/orekit/attitudes/TargetPointing.java b/src/main/java/org/orekit/attitudes/TargetPointing.java index 8fcc918c44..4b1b82f9d7 100644 --- a/src/main/java/org/orekit/attitudes/TargetPointing.java +++ b/src/main/java/org/orekit/attitudes/TargetPointing.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/TorqueFree.java b/src/main/java/org/orekit/attitudes/TorqueFree.java new file mode 100644 index 0000000000..ec85154cf4 --- /dev/null +++ b/src/main/java/org/orekit/attitudes/TorqueFree.java @@ -0,0 +1,712 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.attitudes; + +import java.util.HashMap; +import java.util.Map; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.DenseOutputModel; +import org.hipparchus.ode.FieldDenseOutputModel; +import org.hipparchus.ode.FieldExpandableODE; +import org.hipparchus.ode.FieldODEState; +import org.hipparchus.ode.FieldOrdinaryDifferentialEquation; +import org.hipparchus.ode.ODEState; +import org.hipparchus.ode.OrdinaryDifferentialEquation; +import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; +import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; +import org.hipparchus.special.elliptic.jacobi.CopolarN; +import org.hipparchus.special.elliptic.jacobi.FieldCopolarN; +import org.hipparchus.special.elliptic.jacobi.FieldJacobiElliptic; +import org.hipparchus.special.elliptic.jacobi.JacobiElliptic; +import org.hipparchus.special.elliptic.jacobi.JacobiEllipticBuilder; +import org.hipparchus.special.elliptic.legendre.LegendreEllipticIntegral; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedFieldAngularCoordinates; + + +/** This class handles torque-free motion of a general (non-symmetrical) body. + *

+ * This attitude model is analytical, it can be called at any arbitrary date + * before or after the date of the initial attitude. Despite being an analytical + * model, it is not an approximation. It provides the attitude exactly + * in O(1) time. + *

+ *

+ * The equations are based on Landau and Lifchitz Course of Theoretical Physics, + * Mechanics vol 1, chapter 37. Some adaptations have been made to Landau and + * Lifchitz equations: + *

+ *
    + *
  • inertia can be in any order
  • + *
  • initial conditions can be arbitrary
  • + *
  • signs of several equations have been fixed to work for all initial conditions
  • + *
  • equations have been rewritten to work in all octants
  • + *
  • the φ angle model is based on a precomputed quadrature over one period computed + * at construction (the Landau and Lifchitz equations 37.17 to 37.20 seem to be wrong)
  • + *
+ *

+ * The precomputed quadrature is performed numerically, but as it is performed only once at + * construction and the full integrated model over one period is saved, it can be applied + * analytically later on for any number of periods, hence we consider this attitude mode + * to be analytical. + *

+ * @author Luc Maisonobe + * @author Lucas Girodet + * @since 12.0 + */ +public class TorqueFree implements AttitudeProvider { + + /** Initial attitude. */ + private final Attitude initialAttitude; + + /** Spacecraft inertia. */ + private final Inertia inertia; + + /** Regular model for primitive double arguments. */ + private final DoubleModel doubleModel; + + /** Cached field-based models. */ + private final transient Map>, FieldModel>> cachedModels; + + /** Simple constructor. + * @param initialAttitude initial attitude + * @param inertia spacecraft inertia + */ + public TorqueFree(final Attitude initialAttitude, final Inertia inertia) { + + this.initialAttitude = initialAttitude; + this.inertia = inertia; + + // prepare the regular model + this.doubleModel = new DoubleModel(); + + // set an empty cache for field-based models that will be lazily build + this.cachedModels = new HashMap<>(); + + } + + /** Get the initial attitude. + * @return initial attitude + */ + public Attitude getInitialAttitude() { + return initialAttitude; + } + + /** Get the spacecraft inertia. + * @return spacecraft inertia + */ + public Inertia getInertia() { + return inertia; + } + + /** {@inheritDoc} */ + public Attitude getAttitude(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, final Frame frame) { + + // attitude from model + final Attitude attitude = + new Attitude(initialAttitude.getReferenceFrame(), doubleModel.evaluate(date)); + + // fix frame + return attitude.withReferenceFrame(frame); + + } + + /** {@inheritDoc} */ + public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + + // get the model for specified field + @SuppressWarnings("unchecked") + FieldModel fm = (FieldModel) cachedModels.get(date.getField()); + if (fm == null) { + // create a model for this field + fm = new FieldModel<>(date.getField()); + cachedModels.put(date.getField(), fm); + } + + // attitude from model + final FieldAttitude attitude = + new FieldAttitude<>(initialAttitude.getReferenceFrame(), fm.evaluate(date)); + + // fix frame + return attitude.withReferenceFrame(frame); + + } + + /** Torque-free model. */ + private class DoubleModel { + + /** Inertia sorted to get a motion about axis 3. */ + private final Inertia sortedInertia; + + /** State scaling factor. */ + private final double o1Scale; + + /** State scaling factor. */ + private final double o2Scale; + + /** State scaling factor. */ + private final double o3Scale; + + /** Jacobi elliptic function. */ + private final JacobiElliptic jacobi; + + /** Time scaling factor. */ + private final double tScale; + + /** Time reference for rotation rate. */ + private final AbsoluteDate tRef; + + /** Offset rotation between initial inertial frame and the frame with moment vector and Z axis aligned. */ + private final Rotation inertToAligned; + + /** Rotation to switch to the converted axes frame. */ + private final Rotation sortedToBody; + + /** Period of rotation rate. */ + private final double period; + + /** Slope of the linear part of the phi model. */ + private final double phiSlope; + + /** DenseOutputModel of phi. */ + private final DenseOutputModel phiQuadratureModel; + + /** Integral part of quadrature model over one period. */ + private final double integOnePeriod; + + /** Simple constructor. + */ + DoubleModel() { + + // build inertia tensor + final double i1 = inertia.getInertiaAxis1().getI(); + final Vector3D a1 = inertia.getInertiaAxis1().getA(); + final double i2 = inertia.getInertiaAxis2().getI(); + final Vector3D a2 = inertia.getInertiaAxis2().getA(); + final double i3 = inertia.getInertiaAxis3().getI(); + final Vector3D a3 = inertia.getInertiaAxis3().getA(); + final Vector3D n1 = a1.normalize(); + final Vector3D n2 = a2.normalize(); + final Vector3D n3 = Vector3D.dotProduct(Vector3D.crossProduct(a1, a2), a3) > 0 ? + a3.normalize() : a3.normalize().negate(); + + final Vector3D omega0 = initialAttitude.getSpin(); + final Vector3D m0 = new Vector3D(i1 * Vector3D.dotProduct(omega0, n1), n1, + i2 * Vector3D.dotProduct(omega0, n2), n2, + i3 * Vector3D.dotProduct(omega0, n3), n3); + + // sort axes in increasing moments of inertia order + Inertia tmpInertia = new Inertia(new InertiaAxis(i1, n1), new InertiaAxis(i2, n2), new InertiaAxis(i3, n3)); + if (tmpInertia.getInertiaAxis1().getI() > tmpInertia.getInertiaAxis2().getI()) { + tmpInertia = tmpInertia.swap12(); + } + if (tmpInertia.getInertiaAxis2().getI() > tmpInertia.getInertiaAxis3().getI()) { + tmpInertia = tmpInertia.swap23(); + } + if (tmpInertia.getInertiaAxis1().getI() > tmpInertia.getInertiaAxis2().getI()) { + tmpInertia = tmpInertia.swap12(); + } + + // in order to simplify implementation, we want the motion to be about axis 3 + // which is either the minimum or the maximum inertia axis + final double o1 = Vector3D.dotProduct(omega0, n1); + final double o2 = Vector3D.dotProduct(omega0, n2); + final double o3 = Vector3D.dotProduct(omega0, n3); + final double o12 = o1 * o1; + final double o22 = o2 * o2; + final double o32 = o3 * o3; + final double twoE = i1 * o12 + i2 * o22 + i3 * o32; + final double m2 = i1 * i1 * o12 + i2 * i2 * o22 + i3 * i3 * o32; + final double separatrixInertia = (twoE == 0) ? 0.0 : m2 / twoE; + final boolean clockwise; + if (separatrixInertia < tmpInertia.getInertiaAxis2().getI()) { + // motion is about minimum inertia axis + // we swap axes to put them in decreasing moments order + // motion will be clockwise about axis 3 + clockwise = true; + tmpInertia = tmpInertia.swap13(); + } else { + // motion is about maximum inertia axis + // we keep axes in increasing moments order + // motion will be counter-clockwise about axis 3 + clockwise = false; + } + sortedInertia = tmpInertia; + + final double i1C = tmpInertia.getInertiaAxis1().getI(); + final double i2C = tmpInertia.getInertiaAxis2().getI(); + final double i3C = tmpInertia.getInertiaAxis3().getI(); + final double i32 = i3C - i2C; + final double i31 = i3C - i1C; + final double i21 = i2C - i1C; + + // convert initial conditions to Euler angles such the M is aligned with Z in sorted computation frame + sortedToBody = new Rotation(Vector3D.PLUS_I, Vector3D.PLUS_J, + tmpInertia.getInertiaAxis1().getA(), tmpInertia.getInertiaAxis2().getA()); + final Vector3D omega0Sorted = sortedToBody.applyInverseTo(omega0); + final Vector3D m0Sorted = sortedToBody.applyInverseTo(m0); + final double phi0 = 0; // this angle can be set arbitrarily, so 0 is a fair value (Eq. 37.13 - 37.14) + final double theta0 = FastMath.acos(m0Sorted.getZ() / m0Sorted.getNorm()); + final double psi0 = FastMath.atan2(m0Sorted.getX(), m0Sorted.getY()); // it is really atan2(x, y), not atan2(y, x) as usual! + + // compute offset rotation between inertial frame aligned with momentum and regular inertial frame + final Rotation alignedToSorted0 = new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + phi0, theta0, psi0); + inertToAligned = alignedToSorted0. + applyInverseTo(sortedToBody.applyInverseTo(initialAttitude.getRotation())); + + // Ω is always o1Scale * cn((t-tref) * tScale), o2Scale * sn((t-tref) * tScale), o3Scale * dn((t-tref) * tScale) + tScale = FastMath.copySign(FastMath.sqrt(i32 * (m2 - twoE * i1C) / (i1C * i2C * i3C)), + clockwise ? -omega0Sorted.getZ() : omega0Sorted.getZ()); + o1Scale = FastMath.sqrt((twoE * i3C - m2) / (i1C * i31)); + o2Scale = FastMath.sqrt((twoE * i3C - m2) / (i2C * i32)); + o3Scale = FastMath.copySign(FastMath.sqrt((m2 - twoE * i1C) / (i3C * i31)), omega0Sorted.getZ()); + + final double k2 = (twoE == 0) ? 0.0 : i21 * (twoE * i3C - m2) / (i32 * (m2 - twoE * i1C)); + jacobi = JacobiEllipticBuilder.build(k2); + period = 4 * LegendreEllipticIntegral.bigK(k2) / tScale; + + final double dtRef; + if (o1Scale == 0) { + // special case where twoE * i3C = m2, then o2Scale is also zero + // motion is exactly along one axis + dtRef = 0; + } else { + if (FastMath.abs(omega0Sorted.getX()) >= FastMath.abs(omega0Sorted.getY())) { + if (omega0Sorted.getX() >= 0) { + // omega is roughly towards +I + dtRef = -jacobi.arcsn(omega0Sorted.getY() / o2Scale) / tScale; + } else { + // omega is roughly towards -I + dtRef = jacobi.arcsn(omega0Sorted.getY() / o2Scale) / tScale - 0.5 * period; + } + } else { + if (omega0Sorted.getY() >= 0) { + // omega is roughly towards +J + dtRef = -jacobi.arccn(omega0Sorted.getX() / o1Scale) / tScale; + } else { + // omega is roughly towards -J + dtRef = jacobi.arccn(omega0Sorted.getX() / o1Scale) / tScale; + } + } + } + tRef = initialAttitude.getDate().shiftedBy(dtRef); + + phiSlope = FastMath.sqrt(m2) / i3C; + phiQuadratureModel = computePhiQuadratureModel(dtRef); + integOnePeriod = phiQuadratureModel.getInterpolatedState(phiQuadratureModel.getFinalTime()).getPrimaryState()[0]; + + } + + /** Compute the model for φ angle. + * @param dtRef start time + * @return model for φ angle + */ + private DenseOutputModel computePhiQuadratureModel(final double dtRef) { + + final double i1C = sortedInertia.getInertiaAxis1().getI(); + final double i2C = sortedInertia.getInertiaAxis2().getI(); + final double i3C = sortedInertia.getInertiaAxis3().getI(); + + final double i32 = i3C - i2C; + final double i31 = i3C - i1C; + final double i21 = i2C - i1C; + + // coefficients for φ model + final double b = phiSlope * i32 * i31; + final double c = i1C * i32; + final double d = i3C * i21; + + // integrate the quadrature phi term over one period + final DormandPrince853Integrator integ = new DormandPrince853Integrator(1.0e-6 * period, 1.0e-2 * period, + phiSlope * period * 1.0e-13, 1.0e-13); + final DenseOutputModel model = new DenseOutputModel(); + integ.addStepHandler(model); + + integ.integrate(new OrdinaryDifferentialEquation() { + + /** {@inheritDoc} */ + @Override + public int getDimension() { + return 1; + } + + /** {@inheritDoc} */ + @Override + public double[] computeDerivatives(final double t, final double[] y) { + final double sn = jacobi.valuesN((t - dtRef) * tScale).sn(); + return new double[] { + b / (c + d * sn * sn) + }; + } + + }, new ODEState(0, new double[1]), period); + + return model; + + } + + /** Evaluate torque-free motion model. + * @param date evaluation date + * @return body orientation at date + */ + public TimeStampedAngularCoordinates evaluate(final AbsoluteDate date) { + + // angular velocity + final CopolarN valuesN = jacobi.valuesN(date.durationFrom(tRef) * tScale); + final Vector3D omegaSorted = new Vector3D(o1Scale * valuesN.cn(), o2Scale * valuesN.sn(), o3Scale * valuesN.dn()); + final Vector3D omegaBody = sortedToBody.applyTo(omegaSorted); + + // acceleration + final Vector3D accelerationSorted = new Vector3D(o1Scale * tScale * valuesN.cn() * valuesN.dn(), + o2Scale * tScale * -valuesN.sn() * valuesN.dn(), + o3Scale * tScale * -jacobi.getM() * valuesN.sn() * valuesN.cn()); + final Vector3D accelerationBody = sortedToBody.applyTo(accelerationSorted); + + // first Euler angles are directly linked to angular velocity + final double dt = date.durationFrom(initialAttitude.getDate()); + final double psi = FastMath.atan2(sortedInertia.getInertiaAxis1().getI() * omegaSorted.getX(), + sortedInertia.getInertiaAxis2().getI() * omegaSorted.getY()); + final double theta = FastMath.acos(omegaSorted.getZ() / phiSlope); + final double phiLinear = phiSlope * dt; + + // third Euler angle results from a quadrature + final int nbPeriods = (int) FastMath.floor(dt / period); + final double tStartInteg = nbPeriods * period; + final double integPartial = phiQuadratureModel.getInterpolatedState(dt - tStartInteg).getPrimaryState()[0]; + final double phiQuadrature = nbPeriods * integOnePeriod + integPartial; + final double phi = phiLinear + phiQuadrature; + + // rotation between computation frame (aligned with momentum) and sorted computation frame + // (it is simply the angles equations provided by Landau & Lifchitz) + final Rotation alignedToSorted = new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + phi, theta, psi); + + // combine with offset rotation to get back from regular inertial frame to body frame + final Rotation inertToBody = sortedToBody.applyTo(alignedToSorted.applyTo(inertToAligned)); + + return new TimeStampedAngularCoordinates(date, inertToBody, omegaBody, accelerationBody); + } + + } + + /** Torque-free model. + * @param type of the field elements + */ + private class FieldModel > { + + /** Inertia sorted to get a motion about axis 3. */ + private final FieldInertia sortedInertia; + + /** State scaling factor. */ + private final T o1Scale; + + /** State scaling factor. */ + private final T o2Scale; + + /** State scaling factor. */ + private final T o3Scale; + + /** Jacobi elliptic function. */ + private final FieldJacobiElliptic jacobi; + + /** Time scaling factor. */ + private final T tScale; + + /** Time reference for rotation rate. */ + private final FieldAbsoluteDate tRef; + + /** Offset rotation between initial inertial frame and the frame with moment vector and Z axis aligned. */ + private final FieldRotation inertToAligned; + + /** Rotation to switch to the converted axes frame. */ + private final FieldRotation sortedToBody; + + /** Period of rotation rate. */ + private final T period; + + /** Slope of the linear part of the phi model. */ + private final T phiSlope; + + /** DenseOutputModel of phi. */ + private final FieldDenseOutputModel phiQuadratureModel; + + /** Integral part of quadrature model over one period. */ + private final T integOnePeriod; + + /** Simple constructor. + * @param field field to which elements belong + */ + FieldModel(final Field field) { + + final double i1 = inertia.getInertiaAxis1().getI(); + final Vector3D a1 = inertia.getInertiaAxis1().getA(); + final double i2 = inertia.getInertiaAxis2().getI(); + final Vector3D a2 = inertia.getInertiaAxis2().getA(); + final double i3 = inertia.getInertiaAxis3().getI(); + final Vector3D a3 = inertia.getInertiaAxis3().getA(); + + final T zero = field.getZero(); + final T fI1 = zero.newInstance(i1); + final FieldVector3D fA1 = new FieldVector3D<>(field, a1); + final T fI2 = zero.newInstance(i2); + final FieldVector3D fA2 = new FieldVector3D<>(field, a2); + final T fI3 = zero.newInstance(i3); + final FieldVector3D fA3 = new FieldVector3D<>(field, a3); + + // build inertia tensor + final FieldVector3D n1 = fA1.normalize(); + final FieldVector3D n2 = fA2.normalize(); + final FieldVector3D n3 = Vector3D.dotProduct(Vector3D.crossProduct(a1, a2), a3) > 0 ? + fA3.normalize() : fA3.normalize().negate(); + + final FieldVector3D omega0 = new FieldVector3D<>(field, initialAttitude.getSpin()); + final FieldVector3D m0 = new FieldVector3D<>(fI1.multiply(FieldVector3D.dotProduct(omega0, n1)), n1, + fI2.multiply(FieldVector3D.dotProduct(omega0, n2)), n2, + fI3.multiply(FieldVector3D.dotProduct(omega0, n3)), n3); + + // sort axes in increasing moments of inertia order + FieldInertia tmpInertia = new FieldInertia<>(new FieldInertiaAxis<>(fI1, n1), + new FieldInertiaAxis<>(fI2, n2), + new FieldInertiaAxis<>(fI3, n3)); + if (tmpInertia.getInertiaAxis1().getI().subtract(tmpInertia.getInertiaAxis2().getI()).getReal() > 0) { + tmpInertia = tmpInertia.swap12(); + } + if (tmpInertia.getInertiaAxis2().getI().subtract(tmpInertia.getInertiaAxis3().getI()).getReal() > 0) { + tmpInertia = tmpInertia.swap23(); + } + if (tmpInertia.getInertiaAxis1().getI().subtract(tmpInertia.getInertiaAxis2().getI()).getReal() > 0) { + tmpInertia = tmpInertia.swap12(); + } + + // in order to simplify implementation, we want the motion to be about axis 3 + // which is either the minimum or the maximum inertia axis + final T o1 = FieldVector3D.dotProduct(omega0, n1); + final T o2 = FieldVector3D.dotProduct(omega0, n2); + final T o3 = FieldVector3D.dotProduct(omega0, n3); + final T o12 = o1.multiply(o1); + final T o22 = o2.multiply(o2); + final T o32 = o3.multiply(o3); + final T twoE = fI1.multiply(o12).add(fI2.multiply(o22)).add(fI3.multiply(o32)); + final T m2 = fI1.multiply(fI1).multiply(o12).add(fI2.multiply(fI2).multiply(o22)).add(fI3.multiply(fI3).multiply(o32)); + final T separatrixInertia = (twoE.isZero()) ? zero : m2.divide(twoE); + final boolean clockwise; + if (separatrixInertia.subtract(tmpInertia.getInertiaAxis2().getI()).getReal() < 0) { + // motion is about minimum inertia axis + // we swap axes to put them in decreasing moments order + // motion will be clockwise about axis 3 + clockwise = true; + tmpInertia = tmpInertia.swap13(); + } else { + // motion is about maximum inertia axis + // we keep axes in increasing moments order + // motion will be counter-clockwise about axis 3 + clockwise = false; + } + sortedInertia = tmpInertia; + + final T i1C = tmpInertia.getInertiaAxis1().getI(); + final T i2C = tmpInertia.getInertiaAxis2().getI(); + final T i3C = tmpInertia.getInertiaAxis3().getI(); + final T i32 = i3C.subtract(i2C); + final T i31 = i3C.subtract(i1C); + final T i21 = i2C.subtract(i1C); + + // convert initial conditions to Euler angles such the M is aligned with Z in sorted computation frame + sortedToBody = new FieldRotation<>(FieldVector3D.getPlusI(field), + FieldVector3D.getPlusJ(field), + tmpInertia.getInertiaAxis1().getA(), + tmpInertia.getInertiaAxis2().getA()); + final FieldVector3D omega0Sorted = sortedToBody.applyInverseTo(omega0); + final FieldVector3D m0Sorted = sortedToBody.applyInverseTo(m0); + final T phi0 = zero; // this angle can be set arbitrarily, so 0 is a fair value (Eq. 37.13 - 37.14) + final T theta0 = FastMath.acos(m0Sorted.getZ().divide(m0Sorted.getNorm())); + final T psi0 = FastMath.atan2(m0Sorted.getX(), m0Sorted.getY()); // it is really atan2(x, y), not atan2(y, x) as usual! + + // compute offset rotation between inertial frame aligned with momentum and regular inertial frame + final FieldRotation alignedToSorted0 = new FieldRotation<>(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + phi0, theta0, psi0); + inertToAligned = alignedToSorted0. + applyInverseTo(sortedToBody.applyInverseTo(new FieldRotation<>(field, initialAttitude.getRotation()))); + + // Ω is always o1Scale * cn((t-tref) * tScale), o2Scale * sn((t-tref) * tScale), o3Scale * dn((t-tref) * tScale) + tScale = FastMath.copySign(FastMath.sqrt(i32.multiply(m2.subtract(twoE.multiply(i1C))).divide(i1C.multiply(i2C).multiply(i3C))), + clockwise ? omega0Sorted.getZ().negate() : omega0Sorted.getZ()); + o1Scale = FastMath.sqrt(twoE.multiply(i3C).subtract(m2).divide(i1C.multiply(i31))); + o2Scale = FastMath.sqrt(twoE.multiply(i3C).subtract(m2).divide(i2C.multiply(i32))); + o3Scale = FastMath.copySign(FastMath.sqrt(m2.subtract(twoE.multiply(i1C)).divide(i3C.multiply(i31))), + omega0Sorted.getZ()); + + final T k2 = (twoE.isZero()) ? + zero : + i21.multiply(twoE.multiply(i3C).subtract(m2)). + divide(i32.multiply(m2.subtract(twoE.multiply(i1C)))); + jacobi = JacobiEllipticBuilder.build(k2); + period = LegendreEllipticIntegral.bigK(k2).multiply(4).divide(tScale); + + final T dtRef; + if (o1Scale.isZero()) { + // special case where twoE * i3C = m2, then o2Scale is also zero + // motion is exactly along one axis + dtRef = zero; + } else { + if (FastMath.abs(omega0Sorted.getX()).subtract(FastMath.abs(omega0Sorted.getY())).getReal() >= 0) { + if (omega0Sorted.getX().getReal() >= 0) { + // omega is roughly towards +I + dtRef = jacobi.arcsn(omega0Sorted.getY().divide(o2Scale)).divide(tScale).negate(); + } else { + // omega is roughly towards -I + dtRef = jacobi.arcsn(omega0Sorted.getY().divide(o2Scale)).divide(tScale).subtract(period.multiply(0.5)); + } + } else { + if (omega0Sorted.getY().getReal() >= 0) { + // omega is roughly towards +J + dtRef = jacobi.arccn(omega0Sorted.getX().divide(o1Scale)).divide(tScale).negate(); + } else { + // omega is roughly towards -J + dtRef = jacobi.arccn(omega0Sorted.getX().divide(o1Scale)).divide(tScale); + } + } + } + tRef = new FieldAbsoluteDate<>(field, initialAttitude.getDate()).shiftedBy(dtRef); + + phiSlope = FastMath.sqrt(m2).divide(i3C); + phiQuadratureModel = computePhiQuadratureModel(dtRef); + integOnePeriod = phiQuadratureModel.getInterpolatedState(phiQuadratureModel.getFinalTime()).getPrimaryState()[0]; + + } + + /** Compute the model for φ angle. + * @param dtRef start time + * @return model for φ angle + */ + private FieldDenseOutputModel computePhiQuadratureModel(final T dtRef) { + + final T zero = dtRef.getField().getZero(); + + final T i1C = sortedInertia.getInertiaAxis1().getI(); + final T i2C = sortedInertia.getInertiaAxis2().getI(); + final T i3C = sortedInertia.getInertiaAxis3().getI(); + + final T i32 = i3C.subtract(i2C); + final T i31 = i3C.subtract(i1C); + final T i21 = i2C.subtract(i1C); + + // coefficients for φ model + final T b = phiSlope.multiply(i32).multiply(i31); + final T c = i1C.multiply(i32); + final T d = i3C.multiply(i21); + + // integrate the quadrature phi term on one period + final DormandPrince853FieldIntegrator integ = new DormandPrince853FieldIntegrator<>(dtRef.getField(), + 1.0e-6 * period.getReal(), + 1.0e-2 * period.getReal(), + phiSlope.getReal() * period.getReal() * 1.0e-13, + 1.0e-13); + final FieldDenseOutputModel model = new FieldDenseOutputModel<>(); + integ.addStepHandler(model); + + integ.integrate(new FieldExpandableODE(new FieldOrdinaryDifferentialEquation() { + + /** {@inheritDoc} */ + @Override + public int getDimension() { + return 1; + } + + /** {@inheritDoc} */ + @Override + public T[] computeDerivatives(final T t, final T[] y) { + final T sn = jacobi.valuesN(t.subtract(dtRef).multiply(tScale)).sn(); + final T[] yDot = MathArrays.buildArray(dtRef.getField(), 1); + yDot[0] = b.divide(c.add(d.multiply(sn).multiply(sn))); + return yDot; + } + + }), new FieldODEState(zero, MathArrays.buildArray(dtRef.getField(), 1)), period); + + return model; + + } + + /** Evaluate torque-free motion model. + * @param date evaluation date + * @return body orientation at date + */ + public TimeStampedFieldAngularCoordinates evaluate(final FieldAbsoluteDate date) { + + // angular velocity + final FieldCopolarN valuesN = jacobi.valuesN(date.durationFrom(tRef).multiply(tScale)); + final FieldVector3D omegaSorted = new FieldVector3D<>(valuesN.cn().multiply(o1Scale), + valuesN.sn().multiply(o2Scale), + valuesN.dn().multiply(o3Scale)); + final FieldVector3D omegaBody = sortedToBody.applyTo(omegaSorted); + + // acceleration + final FieldVector3D accelerationSorted = + new FieldVector3D<>(o1Scale.multiply(tScale).multiply(valuesN.cn()).multiply(valuesN.dn()), + o2Scale.multiply(tScale).multiply(valuesN.sn().negate()).multiply(valuesN.dn()), + o3Scale.multiply(tScale).multiply(jacobi.getM().negate()).multiply(valuesN.sn()).multiply(valuesN.cn())); + final FieldVector3D accelerationBody = sortedToBody.applyTo(accelerationSorted); + + // first Euler angles are directly linked to angular velocity + final T dt = date.durationFrom(initialAttitude.getDate()); + final T psi = FastMath.atan2(sortedInertia.getInertiaAxis1().getI().multiply(omegaSorted.getX()), + sortedInertia.getInertiaAxis2().getI().multiply(omegaSorted.getY())); + final T theta = FastMath.acos(omegaSorted.getZ().divide(phiSlope)); + final T phiLinear = dt.multiply(phiSlope); + + // third Euler angle results from a quadrature + final int nbPeriods = (int) FastMath.floor(dt.divide(period)).getReal(); + final T tStartInteg = period.multiply(nbPeriods); + final T integPartial = phiQuadratureModel.getInterpolatedState(dt.subtract(tStartInteg)).getPrimaryState()[0]; + final T phiQuadrature = integOnePeriod.multiply(nbPeriods).add(integPartial); + final T phi = phiLinear.add(phiQuadrature); + + // rotation between computation frame (aligned with momentum) and sorted computation frame + // (it is simply the angles equations provided by Landau & Lifchitz) + final FieldRotation alignedToSorted = new FieldRotation<>(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + phi, theta, psi); + + // combine with offset rotation to get back from regular inertial frame to body frame + final FieldRotation inertToBody = sortedToBody.applyTo(alignedToSorted.applyTo(inertToAligned)); + + return new TimeStampedFieldAngularCoordinates<>(date, inertToBody, omegaBody, accelerationBody); + + } + + } + +} diff --git a/src/main/java/org/orekit/attitudes/YawCompensation.java b/src/main/java/org/orekit/attitudes/YawCompensation.java index b7d4746802..da51f02842 100644 --- a/src/main/java/org/orekit/attitudes/YawCompensation.java +++ b/src/main/java/org/orekit/attitudes/YawCompensation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/YawSteering.java b/src/main/java/org/orekit/attitudes/YawSteering.java index a57974d718..fbe2ecc619 100644 --- a/src/main/java/org/orekit/attitudes/YawSteering.java +++ b/src/main/java/org/orekit/attitudes/YawSteering.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/attitudes/package-info.java b/src/main/java/org/orekit/attitudes/package-info.java index 9d0010c369..a5f21f4b31 100644 --- a/src/main/java/org/orekit/attitudes/package-info.java +++ b/src/main/java/org/orekit/attitudes/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/BodyShape.java b/src/main/java/org/orekit/bodies/BodyShape.java index 783583615a..ae2de058a9 100644 --- a/src/main/java/org/orekit/bodies/BodyShape.java +++ b/src/main/java/org/orekit/bodies/BodyShape.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -106,7 +106,7 @@ > FieldGeodeticPoint getIntersectionPoint(F /** Transform a Cartesian point to a surface-relative point. * @param point Cartesian point - * @param type fo the filed elements + * @param type of the filed elements * @param frame frame in which Cartesian point is expressed * @param date date of the computation (used for frames conversions) * @return point at the same location but as a surface-relative point @@ -123,7 +123,7 @@ > FieldGeodeticPoint transform(FieldVector3 /** Transform a surface-relative point to a Cartesian point. * @param point surface-relative point - * @param type fo the filed elements + * @param type of the filed elements * @return point at the same location but as a Cartesian point * @since 9.0 */ diff --git a/src/main/java/org/orekit/bodies/CR3BPFactory.java b/src/main/java/org/orekit/bodies/CR3BPFactory.java index cadf4e1e8a..2a34d23c03 100644 --- a/src/main/java/org/orekit/bodies/CR3BPFactory.java +++ b/src/main/java/org/orekit/bodies/CR3BPFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/CR3BPSystem.java b/src/main/java/org/orekit/bodies/CR3BPSystem.java index 80bdf961f0..f1e24c75e7 100644 --- a/src/main/java/org/orekit/bodies/CR3BPSystem.java +++ b/src/main/java/org/orekit/bodies/CR3BPSystem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,7 +29,6 @@ import org.orekit.utils.AbsolutePVCoordinates; import org.orekit.utils.LagrangianPoints; import org.orekit.utils.PVCoordinates; -import org.orekit.utils.TimeStampedPVCoordinates; /** * Class creating, from two different celestial bodies, the corresponding system @@ -371,10 +370,10 @@ private PVCoordinates getRealPV(final PVCoordinates pv0, final AbsoluteDate date // 3. Apply the transformation to output frame final Frame primaryInertialFrame = primaryBody.getInertiallyOrientedFrame(); - final TimeStampedPVCoordinates pv21 = secondaryBody.getPVCoordinates(date, primaryInertialFrame); + final Vector3D pv21 = secondaryBody.getPosition(date, primaryInertialFrame); // Distance and Velocity to dimensionalize the state vector - final double dist12 = pv21.getPosition().getNorm(); + final double dist12 = pv21.getNorm(); final double vCircular = FastMath.sqrt(primaryBody.getGM() / dist12); // Dimensionalized state vector centered on primary body diff --git a/src/main/java/org/orekit/bodies/CelestialBody.java b/src/main/java/org/orekit/bodies/CelestialBody.java index cf555216f3..40bcaa953e 100644 --- a/src/main/java/org/orekit/bodies/CelestialBody.java +++ b/src/main/java/org/orekit/bodies/CelestialBody.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/CelestialBodyFactory.java b/src/main/java/org/orekit/bodies/CelestialBodyFactory.java index 1a42fd7719..29fea62649 100644 --- a/src/main/java/org/orekit/bodies/CelestialBodyFactory.java +++ b/src/main/java/org/orekit/bodies/CelestialBodyFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/CelestialBodyLoader.java b/src/main/java/org/orekit/bodies/CelestialBodyLoader.java index 4ea012b616..07883437dd 100644 --- a/src/main/java/org/orekit/bodies/CelestialBodyLoader.java +++ b/src/main/java/org/orekit/bodies/CelestialBodyLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/Ellipse.java b/src/main/java/org/orekit/bodies/Ellipse.java index f42755310a..0949fe3291 100644 --- a/src/main/java/org/orekit/bodies/Ellipse.java +++ b/src/main/java/org/orekit/bodies/Ellipse.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/Ellipsoid.java b/src/main/java/org/orekit/bodies/Ellipsoid.java index 7db4c2b697..8fe84f4cd6 100644 --- a/src/main/java/org/orekit/bodies/Ellipsoid.java +++ b/src/main/java/org/orekit/bodies/Ellipsoid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,8 +18,11 @@ import java.io.Serializable; +import org.hipparchus.CalculusFieldElement; import org.hipparchus.exception.MathRuntimeException; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.twod.FieldVector2D; import org.hipparchus.geometry.euclidean.twod.Vector2D; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -104,6 +107,21 @@ public boolean isInside(final Vector3D point) { return scaledX * scaledX + scaledY * scaledY + scaledZ * scaledZ <= 1.0; } + /** Check if a point is inside the ellipsoid. + * @param point point to check, in the ellipsoid frame + * @return true if the point is inside the ellipsoid + * (or exactly on ellipsoid surface) + * @param the type of the field elements + * @since 12.0 + */ + public > boolean isInside(final FieldVector3D point) { + final T scaledX = point.getX().divide(a); + final T scaledY = point.getY().divide(b); + final T scaledZ = point.getZ().divide(c); + final T d2 = scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).add(scaledZ.multiply(scaledZ)); + return d2.subtract(1.0).getReal() <= 0.0; + } + /** Compute the 2D ellipse at the intersection of the 3D ellipsoid and a plane. * @param planePoint point belonging to the plane, in the ellipsoid frame * @param planeNormal normal of the plane, in the ellipsoid frame @@ -235,6 +253,144 @@ public Ellipse getPlaneSection(final Vector3D planePoint, final Vector3D planeNo } + /** Compute the 2D ellipse at the intersection of the 3D ellipsoid and a plane. + * @param planePoint point belonging to the plane, in the ellipsoid frame + * @param planeNormal normal of the plane, in the ellipsoid frame + * @return plane section or null if there are no intersections + * @exception MathRuntimeException if the norm of planeNormal is null + * @param the type of the field elements + * @since 12.0 + */ + public > FieldEllipse getPlaneSection(final FieldVector3D planePoint, final FieldVector3D planeNormal) + throws MathRuntimeException { + + final T zero = planePoint.getX().getField().getZero(); + final T one = planePoint.getX().getField().getOne(); + + // we define the points Q in the plane using two free variables τ and υ as: + // Q = P + τ u + υ v + // where u and v are two unit vectors belonging to the plane + // Q belongs to the 3D ellipsoid so: + // (xQ / a)² + (yQ / b)² + (zQ / c)² = 1 + // combining both equations, we get: + // (xP² + 2 xP (τ xU + υ xV) + (τ xU + υ xV)²) / a² + // + (yP² + 2 yP (τ yU + υ yV) + (τ yU + υ yV)²) / b² + // + (zP² + 2 zP (τ zU + υ zV) + (τ zU + υ zV)²) / c² + // = 1 + // which can be rewritten: + // α τ² + β υ² + 2 γ τυ + 2 δ τ + 2 ε υ + ζ = 0 + // with + // α = xU² / a² + yU² / b² + zU² / c² > 0 + // β = xV² / a² + yV² / b² + zV² / c² > 0 + // γ = xU xV / a² + yU yV / b² + zU zV / c² + // δ = xP xU / a² + yP yU / b² + zP zU / c² + // ε = xP xV / a² + yP yV / b² + zP zV / c² + // ζ = xP² / a² + yP² / b² + zP² / c² - 1 + // this is the equation of a conic (here an ellipse) + // Of course, we note that if the point P belongs to the ellipsoid + // then ζ = 0 and the equation holds at point P since τ = 0 and υ = 0 + final FieldVector3D u = planeNormal.orthogonal(); + final FieldVector3D v = FieldVector3D.crossProduct(planeNormal, u).normalize(); + final T xUOa = u.getX().divide(a); + final T yUOb = u.getY().divide(b); + final T zUOc = u.getZ().divide(c); + final T xVOa = v.getX().divide(a); + final T yVOb = v.getY().divide(b); + final T zVOc = v.getZ().divide(c); + final T xPOa = planePoint.getX().divide(a); + final T yPOb = planePoint.getY().divide(b); + final T zPOc = planePoint.getZ().divide(c); + final T alpha = xUOa.multiply(xUOa).add(yUOb.multiply(yUOb)).add(zUOc.multiply(zUOc)); + final T beta = xVOa.multiply(xVOa).add(yVOb.multiply(yVOb)).add(zVOc.multiply(zVOc)); + final T gamma = alpha.linearCombination(xUOa, xVOa, yUOb, yVOb, zUOc, zVOc); + final T delta = alpha.linearCombination(xPOa, xUOa, yPOb, yUOb, zPOc, zUOc); + final T epsilon = alpha.linearCombination(xPOa, xVOa, yPOb, yVOb, zPOc, zVOc); + final T zeta = alpha.linearCombination(xPOa, xPOa, yPOb, yPOb, zPOc, zPOc, one, one.negate()); + + // reduce the general equation α τ² + β υ² + 2 γ τυ + 2 δ τ + 2 ε υ + ζ = 0 + // to canonical form (λ/l)² + (μ/m)² = 1 + // using a coordinates change + // τ = τC + λ cosθ - μ sinθ + // υ = υC + λ sinθ + μ cosθ + // or equivalently + // λ = (τ - τC) cosθ + (υ - υC) sinθ + // μ = - (τ - τC) sinθ + (υ - υC) cosθ + // τC and υC are the coordinates of the 2D ellipse center with respect to P + // 2l and 2m and are the axes lengths (major or minor depending on which one is greatest) + // θ is the angle of the 2D ellipse axis corresponding to axis with length 2l + + // choose θ in order to cancel the coupling term in λμ + // expanding the general equation, we get: A λ² + B μ² + C λμ + D λ + E μ + F = 0 + // with C = 2[(β - α) cosθ sinθ + γ (cos²θ - sin²θ)] + // hence the term is cancelled when θ = arctan(t), with γ t² + (α - β) t - γ = 0 + // As the solutions of the quadratic equation obey t₁t₂ = -1, they correspond to + // angles θ in quadrature to each other. Selecting one solution or the other simply + // exchanges the principal axes. As we don't care about which axis we want as the + // first one, we select an arbitrary solution + final T tanTheta; + if (FastMath.abs(gamma.getReal()) < Precision.SAFE_MIN) { + tanTheta = zero; + } else { + final T bMA = beta.subtract(alpha); + tanTheta = (bMA.getReal() >= 0) ? + gamma.multiply(-2).divide(bMA.add(FastMath.sqrt(bMA.multiply(bMA).add(gamma.multiply(gamma).multiply(4))))) : + gamma.multiply(-2).divide(bMA.subtract(FastMath.sqrt(bMA.multiply(bMA).add(gamma.multiply(gamma).multiply(4))))); + } + final T tan2 = tanTheta.multiply(tanTheta); + final T cos2 = tan2.add(1).reciprocal(); + final T sin2 = tan2.multiply(cos2); + final T cosSin = tanTheta.multiply(cos2); + final T cos = FastMath.sqrt(cos2); + final T sin = tanTheta.multiply(cos); + + // choose τC and υC in order to cancel the linear terms in λ and μ + // expanding the general equation, we get: A λ² + B μ² + C λμ + D λ + E μ + F = 0 + // with D = 2[ (α τC + γ υC + δ) cosθ + (γ τC + β υC + ε) sinθ] + // E = 2[-(α τC + γ υC + δ) sinθ + (γ τC + β υC + ε) cosθ] + // θ can be eliminated by combining the equations + // D cosθ - E sinθ = 2[α τC + γ υC + δ] + // E cosθ + D sinθ = 2[γ τC + β υC + ε] + // hence the terms D and E are both cancelled (regardless of θ) when + // τC = (β δ - γ ε) / (γ² - α β) + // υC = (α ε - γ δ) / (γ² - α β) + final T invDenom = gamma.linearCombination(gamma, gamma, alpha.negate(), beta).reciprocal(); + final T tauC = gamma.linearCombination(beta, delta, gamma.negate(), epsilon).multiply(invDenom); + final T nuC = gamma.linearCombination(alpha, epsilon, gamma.negate(), delta).multiply(invDenom); + + // compute l and m + // expanding the general equation, we get: A λ² + B μ² + C λμ + D λ + E μ + F = 0 + // with A = α cos²θ + β sin²θ + 2 γ cosθ sinθ + // B = α sin²θ + β cos²θ - 2 γ cosθ sinθ + // F = α τC² + β υC² + 2 γ τC υC + 2 δ τC + 2 ε υC + ζ + // hence we compute directly l = √(-F/A) and m = √(-F/B) + final T twogcs = gamma.multiply(cosSin).multiply(2); + final T bigA = alpha.multiply(cos2).add(beta.multiply(sin2)).add(twogcs); + final T bigB = alpha.multiply(sin2).add(beta.multiply(cos2)).subtract(twogcs); + final T bigFN = alpha.multiply(tauC).add(gamma.multiply(nuC).add(delta).multiply(2)).multiply(tauC). + add(beta.multiply(nuC).add(epsilon.multiply(2)).multiply(nuC)). + add(zeta). + negate(); + final T l = FastMath.sqrt(bigFN.divide(bigA)); + final T m = FastMath.sqrt(bigFN.divide(bigB)); + if (l.add(m).isNaN()) { + // the plane does not intersect the ellipsoid + return null; + } + + if (l.subtract(m).getReal() > 0) { + return new FieldEllipse<>(new FieldVector3D<>(tauC.getField().getOne(), planePoint, tauC, u, nuC, v), + new FieldVector3D<>(cos, u, sin, v), + new FieldVector3D<>(sin.negate(), u, cos, v), + l, m, frame); + } else { + return new FieldEllipse<>(new FieldVector3D<>(tauC.getField().getOne(), planePoint, tauC, u, nuC, v), + new FieldVector3D<>(sin, u, cos.negate(), v), + new FieldVector3D<>(cos, u, sin, v), + m, l, frame); + } + + } + /** Find a point on ellipsoid limb, as seen by an external observer. * @param observer observer position in ellipsoid frame * @param outside point outside ellipsoid in ellipsoid frame, defining the phase around limb @@ -346,6 +502,122 @@ public Vector3D pointOnLimb(final Vector3D observer, final Vector3D outside) // Return the limb point in the direction of the outside point return Vector3D.distance(tp1, outside) <= Vector3D.distance(tp2, outside) ? tp1 : tp2; + + } + + /** Find a point on ellipsoid limb, as seen by an external observer. + * @param observer observer position in ellipsoid frame + * @param outside point outside ellipsoid in ellipsoid frame, defining the phase around limb + * @return point on ellipsoid limb + * @exception MathRuntimeException if ellipsoid center, observer and outside + * points are aligned + * @param the type of the field elements + * @since 12.0 + */ + public > FieldVector3D pointOnLimb(final FieldVector3D observer, final FieldVector3D outside) + throws MathRuntimeException { + + // There is no limb if we are inside the ellipsoid + if (isInside(observer)) { + throw new OrekitException(OrekitMessages.POINT_INSIDE_ELLIPSOID); + } + // Cut the ellipsoid, to find an elliptical plane section + final FieldVector3D normal = FieldVector3D.crossProduct(observer, outside); + final FieldEllipse section = getPlaneSection(FieldVector3D.getZero(observer.getX().getField()), normal); + + // the point on limb is tangential to the ellipse + // if T(xt, yt) is an ellipse point at which the tangent is drawn + // if O(xo, yo) is a point outside of the ellipse belonging to the tangent at T, + // then the two following equations holds: + // (1) a² yt² + b² xt² = a² b² (T belongs to the ellipse) + // (2) a² yt yo + b² xt xo = a² b² (TP is tangent to the ellipse) + // using the second equation to eliminate yt from the first equation, we get + // b² (a² - xt xo)² + a² xt² yo² = a⁴ yo² + // (3) (a² yo² + b² xo²) xt² - 2 a² b² xo xt + a⁴ (b² - yo²) = 0 + // which can easily be solved for xt + + // To avoid numerical errors, the x and y coordinates in the ellipse plane are normalized using: + // x' = x / a and y' = y / b + // + // This gives: + // (1) y't² + x't² = 1 + // (2) y't y'o + x't x'o = 1 + // + // And finally: + // (3) (x'o² + y'o²) x't² - 2 x't x'o + 1 - y'o² = 0 + // + // Solving for x't, we get the reduced discriminant: + // delta' = beta'² - alpha' * gamma' + // + // With: + // beta' = x'o + // alpha' = x'o² + y'o² + // gamma' = 1 - y'o² + // + // Simplifying to cancel a term of x'o². + // delta' = y'o² (x'o² + y'o² - 1) = y'o² (alpha' - 1) + // + // After solving for xt1, xt2 using (3) the values are substituted into (2) to + // compute yt1, yt2. Then terms of x'o may be canceled from the expressions for + // yt1 and yt2. Additionally a point discontinuity is removed at y'o=0 from both + // yt1 and yt2. + // + // y't1 = (y'o - x'o d) / (x'o² + y'o²) + // y't2 = (x'o y'o + d) / (x + sqrt(delta')) + // + // where: + // d = sign(y'o) sqrt(alpha' - 1) + + // Get the point in ellipse plane frame (2D) + final FieldVector2D observer2D = section.toPlane(observer); + + // Normalize and compute intermediary terms + final T ap = section.getA(); + final T bp = section.getB(); + final T xpo = observer2D.getX().divide(ap); + final T ypo = observer2D.getY().divide(bp); + final T xpo2 = xpo.multiply(xpo); + final T ypo2 = ypo.multiply(ypo); + final T alphap = ypo2.add(xpo2); + final T gammap = ypo2.negate().add(1); + + // Compute the roots + // We know there are two solutions as we already checked the point is outside ellipsoid + final T sqrt = FastMath.sqrt(alphap.subtract(1)); + final T sqrtp = FastMath.abs(ypo).multiply(sqrt); + final T sqrtSigned = FastMath.copySign(sqrt, ypo); + + // Compute the roots (ordered by value) + final T xpt1; + final T xpt2; + final T ypt1; + final T ypt2; + if (xpo.getReal() > 0) { + final T s = xpo.add(sqrtp); + // xpt1 = (beta' + sqrt(delta')) / alpha' (with beta' = x'o) + xpt1 = s.divide(alphap); + // x't2 = gamma' / (beta' + sqrt(delta')) since x't1 * x't2 = gamma' / alpha' + xpt2 = gammap.divide(s); + // Get the corresponding values of y't + ypt1 = ypo.subtract(xpo.multiply(sqrtSigned)).divide(alphap); + ypt2 = xpo.multiply(ypo).add(sqrtSigned).divide(s); + } else { + final T s = xpo.subtract(sqrtp); + // x't1 and x't2 are reverted compared to previous solution + xpt1 = gammap.divide(s); + xpt2 = s.divide(alphap); + // Get the corresponding values of y't + ypt2 = ypo.add(xpo.multiply(sqrtSigned)).divide(alphap); + ypt1 = xpo.multiply(ypo).subtract(sqrtSigned).divide(s); + } + + // De-normalize and express the two solutions in 3D + final FieldVector3D tp1 = section.toSpace(new FieldVector2D<>(ap.multiply(xpt1), bp.multiply(ypt1))); + final FieldVector3D tp2 = section.toSpace(new FieldVector2D<>(ap.multiply(xpt2), bp.multiply(ypt2))); + + // Return the limb point in the direction of the outside point + return FieldVector3D.distance(tp1, outside).subtract(FieldVector3D.distance(tp2, outside)).getReal() <= 0 ? tp1 : tp2; + } } diff --git a/src/main/java/org/orekit/bodies/FieldEllipse.java b/src/main/java/org/orekit/bodies/FieldEllipse.java new file mode 100644 index 0000000000..a98733945f --- /dev/null +++ b/src/main/java/org/orekit/bodies/FieldEllipse.java @@ -0,0 +1,346 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.bodies; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.twod.FieldVector2D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.orekit.frames.Frame; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +/** + * Model of a 2D ellipse in 3D space. + *

+ * These ellipses are mainly created as plane sections of general 3D ellipsoids, + * but can be used for other purposes. + *

+ *

+ * Instances of this class are guaranteed to be immutable. + *

+ * @see Ellipsoid#getPlaneSection(FieldVector3D, FieldVector3D) + * @param the type of the field elements + * @since 12.0 + * @author Luc Maisonobe + */ +public class FieldEllipse> { + + /** Convergence limit. */ + private static final double ANGULAR_THRESHOLD = 1.0e-12; + + /** Center of the 2D ellipse. */ + private final FieldVector3D center; + + /** Unit vector along the major axis. */ + private final FieldVector3D u; + + /** Unit vector along the minor axis. */ + private final FieldVector3D v; + + /** Semi major axis. */ + private final T a; + + /** Semi minor axis. */ + private final T b; + + /** Frame in which the ellipse is defined. */ + private final Frame frame; + + /** Semi major axis radius power 2. */ + private final T a2; + + /** Semi minor axis power 2. */ + private final T b2; + + /** Eccentricity power 2. */ + private final T e2; + + /** 1 minus flatness. */ + private final T g; + + /** g * g. */ + private final T g2; + + /** Evolute factor along major axis. */ + private final T evoluteFactorX; + + /** Evolute factor along minor axis. */ + private final T evoluteFactorY; + + /** Simple constructor. + * @param center center of the 2D ellipse + * @param u unit vector along the major axis + * @param v unit vector along the minor axis + * @param a semi major axis + * @param b semi minor axis + * @param frame frame in which the ellipse is defined + */ + public FieldEllipse(final FieldVector3D center, final FieldVector3D u, + final FieldVector3D v, final T a, final T b, + final Frame frame) { + this.center = center; + this.u = u; + this.v = v; + this.a = a; + this.b = b; + this.frame = frame; + this.a2 = a.multiply(a); + this.g = b.divide(a); + this.g2 = g.multiply(g); + this.e2 = g2.negate().add(1); + this.b2 = b.multiply(b); + this.evoluteFactorX = a2.subtract(b2).divide(a2.multiply(a2)); + this.evoluteFactorY = b2.subtract(a2).divide(b2.multiply(b2)); + } + + /** Get the center of the 2D ellipse. + * @return center of the 2D ellipse + */ + public FieldVector3D getCenter() { + return center; + } + + /** Get the unit vector along the major axis. + * @return unit vector along the major axis + */ + public FieldVector3D getU() { + return u; + } + + /** Get the unit vector along the minor axis. + * @return unit vector along the minor axis + */ + public FieldVector3D getV() { + return v; + } + + /** Get the semi major axis. + * @return semi major axis + */ + public T getA() { + return a; + } + + /** Get the semi minor axis. + * @return semi minor axis + */ + public T getB() { + return b; + } + + /** Get the defining frame. + * @return defining frame + */ + public Frame getFrame() { + return frame; + } + + /** Get a point of the 2D ellipse. + * @param theta angular parameter on the ellipse (really the eccentric anomaly) + * @return ellipse point at theta, in underlying ellipsoid frame + */ + public FieldVector3D pointAt(final T theta) { + final FieldSinCos scTheta = FastMath.sinCos(theta); + return toSpace(new FieldVector2D<>(a.multiply(scTheta.cos()), b.multiply(scTheta.sin()))); + } + + /** Create a point from its ellipse-relative coordinates. + * @param p point defined with respect to ellipse + * @return point defined with respect to 3D frame + * @see #toPlane(FieldVector3D) + */ + public FieldVector3D toSpace(final FieldVector2D p) { + return new FieldVector3D(a.getField().getOne(), center, p.getX(), u, p.getY(), v); + } + + /** Project a point to the ellipse plane. + * @param p point defined with respect to 3D frame + * @return point defined with respect to ellipse + * @see #toSpace(FieldVector2D) + */ + public FieldVector2D toPlane(final FieldVector3D p) { + final FieldVector3D delta = p.subtract(center); + return new FieldVector2D<>(FieldVector3D.dotProduct(delta, u), FieldVector3D.dotProduct(delta, v)); + } + + /** Find the closest ellipse point. + * @param p point in the ellipse plane to project on the ellipse itself + * @return closest point belonging to 2D meridian ellipse + */ + public FieldVector2D projectToEllipse(final FieldVector2D p) { + + final T x = FastMath.abs(p.getX()); + final T y = p.getY(); + + if (x.getReal() <= ANGULAR_THRESHOLD * FastMath.abs(y.getReal())) { + // the point is almost on the minor axis, approximate the ellipse with + // the osculating circle whose center is at evolute cusp along minor axis + final T osculatingRadius = a2.divide(b); + final T evoluteCuspZ = FastMath.copySign(a.multiply(e2).divide(g), y.negate()); + final T deltaZ = y.subtract(evoluteCuspZ); + final T ratio = osculatingRadius.divide(FastMath.hypot(deltaZ, x)); + return new FieldVector2D<>(FastMath.copySign(ratio.multiply(x), p.getX()), + evoluteCuspZ.add(ratio.multiply(deltaZ))); + } + + if (FastMath.abs(y.getReal()) <= ANGULAR_THRESHOLD * x.getReal()) { + // the point is almost on the major axis + + final T osculatingRadius = b2.divide(a); + final T evoluteCuspR = a.multiply(e2); + final T deltaR = x.subtract(evoluteCuspR); + if (deltaR.getReal() >= 0) { + // the point is outside of the ellipse evolute, approximate the ellipse + // with the osculating circle whose center is at evolute cusp along major axis + final T ratio = osculatingRadius.divide(FastMath.hypot(y, deltaR)); + return new FieldVector2D<>(FastMath.copySign(evoluteCuspR.add(ratio.multiply(deltaR)), p.getX()), + ratio.multiply(y)); + } + + // the point is on the part of the major axis within ellipse evolute + // we can compute the closest ellipse point analytically + final T rEllipse = x.divide(e2); + return new FieldVector2D<>(FastMath.copySign(rEllipse, p.getX()), + FastMath.copySign(g.multiply(FastMath.sqrt(a2.subtract(rEllipse.multiply(rEllipse)))), y)); + + } else { + + // initial point at evolute cusp along major axis + T omegaX = a.multiply(e2); + T omegaY = a.getField().getZero(); + + T projectedX = x; + T projectedY = y; + double deltaX = Double.POSITIVE_INFINITY; + double deltaY = Double.POSITIVE_INFINITY; + int count = 0; + final double threshold = ANGULAR_THRESHOLD * ANGULAR_THRESHOLD * a2.getReal(); + while ((deltaX * deltaX + deltaY * deltaY) > threshold && count++ < 100) { // this loop usually converges in 3 iterations + + // find point at the intersection of ellipse and line going from query point to evolute point + final T dx = x.subtract(omegaX); + final T dy = y.subtract(omegaY); + final T alpha = b2.multiply(dx).multiply(dx).add(a2.multiply(dy).multiply(dy)); + final T betaPrime = b2.multiply(omegaX).multiply(dx).add(a2.multiply(omegaY).multiply(dy)); + final T gamma = b2.multiply(omegaX).multiply(omegaX).add(a2.multiply(omegaY).multiply(omegaY)).subtract(a2.multiply(b2)); + final T deltaPrime = a.linearCombination(betaPrime, betaPrime, alpha.negate(), gamma); + final T ratio = (betaPrime.getReal() <= 0) ? + FastMath.sqrt(deltaPrime).subtract(betaPrime).divide(alpha) : + gamma.negate().divide(FastMath.sqrt(deltaPrime).add(betaPrime)); + final T previousX = projectedX; + final T previousY = projectedY; + projectedX = omegaX.add(ratio.multiply(dx)); + projectedY = omegaY.add(ratio.multiply(dy)); + + // find new evolute point + omegaX = evoluteFactorX.multiply(projectedX).multiply(projectedX).multiply(projectedX); + omegaY = evoluteFactorY.multiply(projectedY).multiply(projectedY).multiply(projectedY); + + // compute convergence parameters + deltaX = projectedX.subtract(previousX).getReal(); + deltaY = projectedY.subtract(previousY).getReal(); + + } + return new FieldVector2D<>(FastMath.copySign(projectedX, p.getX()), projectedY); + } + } + + /** Project position-velocity-acceleration on an ellipse. + * @param pv position-velocity-acceleration to project, in the reference frame + * @return projected position-velocity-acceleration + */ + public TimeStampedFieldPVCoordinates projectToEllipse(final TimeStampedFieldPVCoordinates pv) { + + // find the closest point in the meridian plane + final FieldVector2Dp2D = toPlane(pv.getPosition()); + final FieldVector2De2D = projectToEllipse(p2D); + + // tangent to the ellipse + final T fx = a2.negate().multiply(e2D.getY()); + final T fy = b2.multiply(e2D.getX()); + final T f2 = fx.multiply(fx).add(fy.multiply(fy)); + final T f = FastMath.sqrt(f2); + final FieldVector2Dtangent = new FieldVector2D<>(fx.divide(f), fy.divide(f)); + + // normal to the ellipse (towards interior) + final FieldVector2Dnormal = new FieldVector2D<>(tangent.getY().negate(), tangent.getX()); + + // center of curvature + final T x2 = e2D.getX().multiply(e2D.getX()); + final T y2 = e2D.getY().multiply(e2D.getY()); + final T eX = evoluteFactorX.multiply(x2); + final T eY = evoluteFactorY.multiply(y2); + final T omegaX = eX.multiply(e2D.getX()); + final T omegaY = eY.multiply(e2D.getY()); + + // velocity projection ratio + final T rho = FastMath.hypot(e2D.getX().subtract(omegaX), e2D.getY().subtract(omegaY)); + final T d = FastMath.hypot(p2D.getX().subtract(omegaX), p2D.getY().subtract(omegaY)); + final T projectionRatio = rho.divide(d); + + // tangential velocity + final FieldVector2DpDot2D = new FieldVector2D<>(FieldVector3D.dotProduct(pv.getVelocity(), u), + FieldVector3D.dotProduct(pv.getVelocity(), v)); + final T pDotTangent = pDot2D.dotProduct(tangent); + final T pDotNormal = pDot2D.dotProduct(normal); + final T eDotTangent = projectionRatio.multiply(pDotTangent); + final FieldVector2DeDot2D = new FieldVector2D<>(eDotTangent, tangent); + final FieldVector2DtangentDot = new FieldVector2D<>(a2.multiply(b2). + multiply(e2D.getX().multiply(eDot2D.getY()). + subtract(e2D.getY().multiply(eDot2D.getX()))). + divide(f2), + normal); + + // velocity of the center of curvature in the meridian plane + final T omegaXDot = eX.multiply(eDotTangent).multiply(tangent.getX()).multiply(3); + final T omegaYDot = eY.multiply(eDotTangent).multiply(tangent.getY()).multiply(3); + + // derivative of the projection ratio + final T voz = omegaXDot.multiply(tangent.getY()).subtract(omegaYDot.multiply(tangent.getX())); + final T vsz = pDotNormal.negate(); + final T projectionRatioDot = rho.subtract(d).multiply(voz).subtract(rho.multiply(vsz)).divide(d.multiply(d)); + + // acceleration + final FieldVector2DpDotDot2D = new FieldVector2D<>(FieldVector3D.dotProduct(pv.getAcceleration(), u), + FieldVector3D.dotProduct(pv.getAcceleration(), v)); + final T pDotDotTangent = pDotDot2D.dotProduct(tangent); + final T pDotTangentDot = pDot2D.dotProduct(tangentDot); + final T eDotDotTangent = projectionRatio.multiply(pDotDotTangent.add(pDotTangentDot)). + add(projectionRatioDot.multiply(pDotTangent)); + final FieldVector2DeDotDot2D = new FieldVector2D<>(eDotDotTangent, tangent, eDotTangent, tangentDot); + + // back to 3D + final FieldVector3D e3D = toSpace(e2D); + final FieldVector3D eDot3D = new FieldVector3D(eDot2D.getX(), u, eDot2D.getY(), v); + final FieldVector3D eDotDot3D = new FieldVector3D(eDotDot2D.getX(), u, eDotDot2D.getY(), v); + + return new TimeStampedFieldPVCoordinates<>(pv.getDate(), e3D, eDot3D, eDotDot3D); + + } + + /** Find the center of curvature (point on the evolute) at the nadir of a point. + * @param point point in the ellipse plane + * @return center of curvature of the ellipse directly at point nadir + */ + public FieldVector2D getCenterOfCurvature(final FieldVector2D point) { + final FieldVector2Dprojected = projectToEllipse(point); + return new FieldVector2D<>(evoluteFactorX.multiply(projected.getX()).multiply(projected.getX()).multiply(projected.getX()), + evoluteFactorY.multiply(projected.getY()).multiply(projected.getY()).multiply(projected.getY())); + } + +} diff --git a/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java b/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java index afcd6d6560..8fbd2617aa 100644 --- a/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java +++ b/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/GeodeticPoint.java b/src/main/java/org/orekit/bodies/GeodeticPoint.java index 8ad28b611f..642bcc6d62 100644 --- a/src/main/java/org/orekit/bodies/GeodeticPoint.java +++ b/src/main/java/org/orekit/bodies/GeodeticPoint.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/IAUPole.java b/src/main/java/org/orekit/bodies/IAUPole.java index 8833e02993..0981f63a23 100644 --- a/src/main/java/org/orekit/bodies/IAUPole.java +++ b/src/main/java/org/orekit/bodies/IAUPole.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/JPLCelestialBody.java b/src/main/java/org/orekit/bodies/JPLCelestialBody.java index aa686fb54e..241e570ff7 100644 --- a/src/main/java/org/orekit/bodies/JPLCelestialBody.java +++ b/src/main/java/org/orekit/bodies/JPLCelestialBody.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.io.Serializable; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; @@ -30,6 +31,7 @@ import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; @@ -125,14 +127,14 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final /** Get the {@link FieldPVCoordinates} of the body in the selected frame. * @param date current date * @param frame the frame where to define the position - * @param type fo the field elements + * @param type of the field elements * @return time-stamped position/velocity of the body (m and m/s) */ public > TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, - final Frame frame) { + final Frame frame) { // apply the scale factor to raw position-velocity - final FieldPVCoordinates rawPV = rawPVProvider.getRawPV(date); + final FieldPVCoordinates rawPV = rawPVProvider.getRawPV(date); final TimeStampedFieldPVCoordinates scaledPV = new TimeStampedFieldPVCoordinates<>(date, scale, rawPV); // the raw PV are relative to the parent of the body centered inertially oriented frame @@ -143,6 +145,37 @@ public > TimeStampedFieldPVCoordinates getP } + /** {@inheritDoc} */ + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + + // apply the scale factor to raw position + final Vector3D rawPosition = rawPVProvider.getRawPosition(date); + final Vector3D scaledPosition = rawPosition.scalarMultiply(scale); + + // the raw position is relative to the parent of the body centered inertially oriented frame + final StaticTransform transform = getInertiallyOrientedFrame().getParent().getStaticTransformTo(frame, date); + + // convert to requested frame + return transform.transformPosition(scaledPosition); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getPosition(final FieldAbsoluteDate date, final Frame frame) { + + // apply the scale factor to raw position + final FieldVector3D rawPosition = rawPVProvider.getRawPosition(date); + final FieldVector3D scaledPosition = rawPosition.scalarMultiply(scale); + + // the raw position is relative to the parent of the body centered inertially oriented frame + final FieldStaticTransform transform = getInertiallyOrientedFrame().getParent().getStaticTransformTo(frame, date); + + // convert to requested frame + return transform.transformPosition(scaledPosition); + } + + /** Replace the instance with a data transfer object for serialization. *

* This intermediate class serializes the files supported names, the ephemeris type @@ -175,7 +208,7 @@ public Frame getBodyOrientedFrame() { return bodyFrame; } - /** Inertially oriented body centered frame. */ + /** Inertially oriented body centered frame. */ private class InertiallyOriented extends Frame { /** Serializable UID. */ @@ -213,7 +246,7 @@ public Transform getTransform(final AbsoluteDate date) { final Vector3D pole = iauPole.getPole(date); final Vector3D qNode = iauPole.getNode(date); final Transform rotation = - new Transform(date, new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I)); + new Transform(date, new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I)); // update transform from parent to self return new Transform(date, translation, rotation); @@ -237,7 +270,7 @@ public StaticTransform getStaticTransform(final AbsoluteDate date) { final Vector3D pole = iauPole.getPole(date); final Vector3D qNode = iauPole.getNode(date); final Rotation rotation = - new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I); + new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I); // update transform from parent to self return StaticTransform.of(date, pv.getPosition().negate(), rotation); @@ -265,17 +298,42 @@ public > FieldTransform getTransform(final qNode = FieldVector3D.getPlusI(date.getField()); } final FieldTransform rotation = - new FieldTransform<>(date, - new FieldRotation<>(pole, - qNode, - FieldVector3D.getPlusK(date.getField()), - FieldVector3D.getPlusI(date.getField()))); + new FieldTransform<>(date, + new FieldRotation<>(pole, + qNode, + FieldVector3D.getPlusK(date.getField()), + FieldVector3D.getPlusI(date.getField()))); // update transform from parent to self return new FieldTransform<>(date, translation, rotation); } + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + // field + final Field field = date.getField(); + // compute translation from parent frame to self + final FieldPVCoordinates pv = getPVCoordinates(date, definingFrame); + + // compute rotation from ICRF frame to self, + // as per the "Report of the IAU/IAG Working Group on Cartographic + // Coordinates and Rotational Elements of the Planets and Satellites" + // These definitions are common for all recent versions of this report + // published every three years, the precise values of pole direction + // and W angle coefficients may vary from publication year as models are + // adjusted. These coefficients are not in this class, they are in the + // specialized classes that do implement the getPole and getPrimeMeridianAngle + // methods + final FieldVector3D pole = iauPole.getPole(date); + final FieldVector3D qNode = iauPole.getNode(date); + final FieldRotation rotation = + new FieldRotation<>(pole, qNode, FieldVector3D.getPlusK(field), FieldVector3D.getPlusI(field)); + + // update transform from parent to self + return FieldStaticTransform.of(date, pv.getPosition().negate(), rotation); + } + }, frameName == null ? name + INERTIAL_FRAME_SUFFIX : frameName, true); } @@ -327,9 +385,9 @@ public > FieldTransform getTransform(final final T w0 = iauPole.getPrimeMeridianAngle(date); final T w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt)); return new FieldTransform<>(date, - new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), w0, - RotationConvention.FRAME_TRANSFORM), - new FieldVector3D<>(w1.subtract(w0).divide(dt), Vector3D.PLUS_K)); + new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), w0, + RotationConvention.FRAME_TRANSFORM), + new FieldVector3D<>(w1.subtract(w0).divide(dt), Vector3D.PLUS_K)); } }, frameName == null ? name + BODY_FRAME_SUFFIX : frameName, false); @@ -385,7 +443,7 @@ protected JPLCelestialBody getBody() { // first try to use the factory, in order to avoid building a new instance // each time we deserialize and have the object properly cached final CelestialBody factoryProvided = - DataContext.getDefault().getCelestialBodies().getBody(name); + DataContext.getDefault().getCelestialBodies().getBody(name); if (factoryProvided instanceof JPLCelestialBody) { final JPLCelestialBody jplBody = (JPLCelestialBody) factoryProvided; if (supportedNames.equals(jplBody.supportedNames) && generateType == jplBody.generateType) { diff --git a/src/main/java/org/orekit/bodies/JPLEphemeridesLoader.java b/src/main/java/org/orekit/bodies/JPLEphemeridesLoader.java index 2b963ed140..9c00dcf382 100644 --- a/src/main/java/org/orekit/bodies/JPLEphemeridesLoader.java +++ b/src/main/java/org/orekit/bodies/JPLEphemeridesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,8 @@ import java.util.concurrent.atomic.AtomicReference; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.AbstractSelfFeedingLoader; @@ -190,6 +192,14 @@ public interface RawPVProvider { */ PVCoordinates getRawPV(AbsoluteDate date); + /** Get the position at date. + * @param date date at which the position is desired + * @return position at the specified date + */ + default Vector3D getRawPosition(final AbsoluteDate date) { + return getRawPV(date).getPosition(); + } + /** Get the position-velocity at date. * @param date date at which the position-velocity is desired * @param type of the field elements @@ -197,6 +207,15 @@ public interface RawPVProvider { */ > FieldPVCoordinates getRawPV(FieldAbsoluteDate date); + /** Get the position at date. + * @param date date at which the position is desired + * @param type of the field elements + * @return position at the specified date + */ + default > FieldVector3D getRawPosition(final FieldAbsoluteDate date) { + return getRawPV(date).getPosition(); + } + } /** Ephemeris for selected body. */ @@ -1046,10 +1065,12 @@ private AbsoluteDate parseDataRecord(final byte[] record) { /** Raw position-velocity provider using ephemeris. */ private class EphemerisRawPVProvider implements RawPVProvider { - /** {@inheritDoc} */ - public PVCoordinates getRawPV(final AbsoluteDate date) { - // get raw PV from Chebyshev polynomials + /** Retrieve correct Chebyshev polynomial for given date. + * @param date date + * @return PosVelChebyshev Chebyshev polynomial class for position-velocity-acceleration + */ + private PosVelChebyshev getChebyshev(final AbsoluteDate date) { PosVelChebyshev chebyshev; try { chebyshev = ephemerides.getNeighbors(date).findFirst().get(); @@ -1061,6 +1082,14 @@ public PVCoordinates getRawPV(final AbsoluteDate date) { throw tce; } } + return chebyshev; + } + + /** {@inheritDoc} */ + public PVCoordinates getRawPV(final AbsoluteDate date) { + + // get raw PV from Chebyshev polynomials + final PosVelChebyshev chebyshev = getChebyshev(date); // evaluate the Chebyshev polynomials return chebyshev.getPositionVelocityAcceleration(date); @@ -1071,23 +1100,33 @@ public PVCoordinates getRawPV(final AbsoluteDate date) { public > FieldPVCoordinates getRawPV(final FieldAbsoluteDate date) { // get raw PV from Chebyshev polynomials - PosVelChebyshev chebyshev; - try { - chebyshev = ephemerides.getNeighbors(date.toAbsoluteDate()).findFirst().get(); - } catch (TimeStampedCacheException tce) { - // we cannot bracket the date, check if the last available chunk covers the specified date - chebyshev = ephemerides.getLatest(); - if (!chebyshev.inRange(date.toAbsoluteDate())) { - // we were not able to recover from the error, the date is too far - throw tce; - } - } + final PosVelChebyshev chebyshev = getChebyshev(date.toAbsoluteDate()); // evaluate the Chebyshev polynomials return chebyshev.getPositionVelocityAcceleration(date); } + /** {@inheritDoc} */ + @Override + public Vector3D getRawPosition(final AbsoluteDate date) { + // get raw PV from Chebyshev polynomials + final PosVelChebyshev chebyshev = getChebyshev(date); + + // evaluate the Chebyshev polynomials + return chebyshev.getPosition(date); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getRawPosition(final FieldAbsoluteDate date) { + // get raw PV from Chebyshev polynomials + final PosVelChebyshev chebyshev = getChebyshev(date.toAbsoluteDate()); + + // evaluate the Chebyshev polynomials + return chebyshev.getPosition(date); + } + } /** Raw position-velocity provider providing always zero. */ diff --git a/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java b/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java index d6c5dc0c28..1b3f87808f 100644 --- a/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java +++ b/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,10 +17,12 @@ package org.orekit.bodies; import java.io.Serializable; +import java.util.function.DoubleFunction; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.geometry.euclidean.threed.FieldLine; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Line; @@ -31,7 +33,7 @@ import org.hipparchus.util.MathArrays; import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; @@ -270,8 +272,8 @@ public > FieldVector3D getCartesianIntersec final FieldAbsoluteDate date) { // transform line and close to body frame - final FieldTransform frameToBodyFrame = frame.getTransformTo(bodyFrame, date); - final FieldLine lineInBodyFrame = frameToBodyFrame.transformLine(line); + final FieldStaticTransform frameToBodyFrame = frame.getStaticTransformTo(bodyFrame, date); + final FieldLine lineInBodyFrame = frameToBodyFrame.transformLine(line); // compute some miscellaneous variables final FieldVector3D point = lineInBodyFrame.getOrigin(); @@ -436,11 +438,21 @@ public TimeStampedPVCoordinates projectToGround(final TimeStampedPVCoordinates p *

*

* Some changes have been added to the original method: + *

*
    *
  • in order to handle more accurately corner cases near the pole
  • *
  • in order to handle properly corner cases near the equatorial plane, even far inside the ellipsoid
  • *
  • in order to handle very flat ellipsoids
  • *
+ *

+ * In some rare cases (for example very flat ellipsoid, or points close to ellipsoid center), the loop + * may fail to converge. As this seems to happen only in degenerate cases, a design choice was to return + * an approximate point corresponding to last iteration. This point may be incorrect and fail to give the + * initial point back if doing roundtrip by calling {@link #transform(GeodeticPoint)}. This design choice + * was made to avoid NaNs appearing for example in inter-satellites visibility checks when two satellites + * are almost on opposite sides of Earth. The intermediate points far within the Earth should not prevent + * the detection algorithm to find visibility start/end. + *

*/ public GeodeticPoint transform(final Vector3D point, final Frame frame, final AbsoluteDate date) { @@ -501,7 +513,11 @@ public GeodeticPoint transform(final Vector3D point, final Frame frame, final Ab double bn = 0; phi = Double.POSITIVE_INFINITY; h = Double.POSITIVE_INFINITY; - for (int i = 0; i < 10; ++i) { // this usually converges in 2 iterations + for (int i = 0; i < 1000; ++i) { + // this usually converges in 2 iterations, but in rare cases it can take much more + // see https://gitlab.orekit.org/orekit/orekit/-/issues/1224 for examples + // with points near Earth center which need 137 iterations for the first example + // and 1150 iterations for the second example final double oldSn = sn; final double oldCn = cn; final double oldPhi = phi; @@ -541,6 +557,13 @@ public GeodeticPoint transform(final Vector3D point, final Frame frame, final Ab } } + + if (Double.isInfinite(phi)) { + // we did not converge, the point is probably within the ellipsoid + // we just compute the "best" phi we can to avoid NaN + phi = FastMath.copySign(FastMath.atan(sn / (g * cn)), z); + } + } return new GeodeticPoint(phi, lambda, h); @@ -561,15 +584,26 @@ public GeodeticPoint transform(final Vector3D point, final Frame frame, final Ab *
  • in order to handle properly corner cases near the equatorial plane, even far inside the ellipsoid
  • *
  • in order to handle very flat ellipsoids
  • * + *

    + * In some rare cases (for example very flat ellipsoid, or points close to ellipsoid center), the loop + * may fail to converge. As this seems to happen only in degenerate cases, a design choice was to return + * an approximate point corresponding to last iteration. This point may be incorrect and fail to give the + * initial point back if doing roundtrip by calling {@link #transform(GeodeticPoint)}. This design choice + * was made to avoid NaNs appearing for example in inter-satellites visibility checks when two satellites + * are almost on opposite sides of Earth. The intermediate points far within the Earth should not prevent + * the detection algorithm to find visibility start/end. + *

    */ public > FieldGeodeticPoint transform(final FieldVector3D point, final Frame frame, final FieldAbsoluteDate date) { // transform point to body frame - final FieldVector3D pointInBodyFrame = frame.getTransformTo(bodyFrame, date).transformPosition(point); + final FieldVector3D pointInBodyFrame = (frame == bodyFrame) ? + point : + frame.getStaticTransformTo(bodyFrame, date).transformPosition(point); final T r2 = pointInBodyFrame.getX().multiply(pointInBodyFrame.getX()). - add(pointInBodyFrame.getY().multiply(pointInBodyFrame.getY())); + add(pointInBodyFrame.getY().multiply(pointInBodyFrame.getY())); final T r = r2.sqrt(); final T z = pointInBodyFrame.getZ(); @@ -622,7 +656,11 @@ public > FieldGeodeticPoint transform(final T bn = an.getField().getZero(); phi = an.getField().getZero().add(Double.POSITIVE_INFINITY); h = an.getField().getZero().add(Double.POSITIVE_INFINITY); - for (int i = 0; i < 10; ++i) { // this usually converges in 2 iterations + for (int i = 0; i < 1000; ++i) { + // this usually converges in 2 iterations, but in rare cases it can take much more + // see https://gitlab.orekit.org/orekit/orekit/-/issues/1224 for examples + // with points near Earth center which need 137 iterations for the first example + // and 1150 iterations for the second example final T oldSn = sn; final T oldCn = cn; final T oldPhi = phi; @@ -662,6 +700,13 @@ public > FieldGeodeticPoint transform(final } } + + if (Double.isInfinite(phi.getReal())) { + // we did not converge, the point is probably within the ellipsoid + // we just compute the "best" phi we can to avoid NaN + phi = sn.divide(cn.multiply(g)).atan().copySign(z); + } + } return new FieldGeodeticPoint<>(phi, lambda, h); @@ -795,6 +840,96 @@ public > T geodeticToIsometricLatitude(final T return a.add(b); } + /** Find intermediate point of lowest altitude along a line between two endpoints. + * @param endpoint1 first endpoint, in body frame + * @param endpoint2 second endpoint, in body frame + * @return point with lowest altitude between {@code endpoint1} and {@code endpoint2}. + * @since 12.0 + */ + public GeodeticPoint lowestAltitudeIntermediate(final Vector3D endpoint1, final Vector3D endpoint2) { + + final Vector3D delta = endpoint2.subtract(endpoint1); + + // function computing intermediate point above ellipsoid (lambda varying between 0 and 1) + final DoubleFunction intermediate = + lambda -> transform(new Vector3D(1 - lambda, endpoint1, lambda, endpoint2), + bodyFrame, null); + + // first endpoint + final GeodeticPoint gp1 = intermediate.apply(0.0); + + if (Vector3D.dotProduct(delta, gp1.getZenith()) >= 0) { + // the line from first endpoint to second endpoint is going away from central body + // the minimum altitude is reached at first endpoint + return gp1; + } else { + // the line from first endpoint to second endpoint is closing the central body + + // second endpoint + final GeodeticPoint gp2 = intermediate.apply(1.0); + + if (Vector3D.dotProduct(delta, gp2.getZenith()) <= 0) { + // the line from first endpoint to second endpoint is still decreasing when reaching second endpoint, + // the minimum altitude is reached at second endpoint + return gp2; + } else { + // the line from first endpoint to second endpoint reaches a minimum between first and second endpoints + final double lambdaMin = new BracketingNthOrderBrentSolver(1.0e-14, 5). + solve(1000, + lambda -> Vector3D.dotProduct(delta, intermediate.apply(lambda).getZenith()), + 0.0, 1.0); + return intermediate.apply(lambdaMin); + } + } + + } + + /** Find intermediate point of lowest altitude along a line between two endpoints. + * @param endpoint1 first endpoint, in body frame + * @param endpoint2 second endpoint, in body frame + * @param type of the field elements + * @return point with lowest altitude between {@code endpoint1} and {@code endpoint2}. + * @since 12.0 + */ + public > FieldGeodeticPoint lowestAltitudeIntermediate(final FieldVector3D endpoint1, + final FieldVector3D endpoint2) { + + final FieldVector3D delta = endpoint2.subtract(endpoint1); + + // function computing intermediate point above ellipsoid (lambda varying between 0 and 1) + final DoubleFunction> intermediate = + lambda -> transform(new FieldVector3D<>(1 - lambda, endpoint1, lambda, endpoint2), + bodyFrame, null); + + // first endpoint + final FieldGeodeticPoint gp1 = intermediate.apply(0.0); + + if (FieldVector3D.dotProduct(delta, gp1.getZenith()).getReal() >= 0) { + // the line from first endpoint to second endpoint is going away from central body + // the minimum altitude is reached at first endpoint + return gp1; + } else { + // the line from first endpoint to second endpoint is closing the central body + + // second endpoint + final FieldGeodeticPoint gp2 = intermediate.apply(1.0); + + if (FieldVector3D.dotProduct(delta, gp2.getZenith()).getReal() <= 0) { + // the line from first endpoint to second endpoint is still decreasing when reaching second endpoint, + // the minimum altitude is reached at second endpoint + return gp2; + } else { + // the line from first endpoint to second endpoint reaches a minimum between first and second endpoints + final double lambdaMin = new BracketingNthOrderBrentSolver(1.0e-14, 5). + solve(1000, + lambda -> FieldVector3D.dotProduct(delta, intermediate.apply(lambda).getZenith()).getReal(), + 0.0, 1.0); + return intermediate.apply(lambdaMin); + } + } + + } + /** Replace the instance with a data transfer object for serialization. *

    * This intermediate class serializes the files supported names, the diff --git a/src/main/java/org/orekit/bodies/PosVelChebyshev.java b/src/main/java/org/orekit/bodies/PosVelChebyshev.java index 2cc79ef683..1451eeba7f 100644 --- a/src/main/java/org/orekit/bodies/PosVelChebyshev.java +++ b/src/main/java/org/orekit/bodies/PosVelChebyshev.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -58,6 +58,11 @@ class PosVelChebyshev implements TimeStamped, Serializable { /** Chebyshev polynomials coefficients for the Z component. */ private final double[] zCoeffs; + /** Velocity scale for internal use. */ + private final double vScale; + /** Acceleration scale for internal use. */ + private final double aScale; + /** Simple constructor. * @param start start of the validity range of the instance * @param timeScale time scale in which the ephemeris is defined @@ -77,6 +82,8 @@ class PosVelChebyshev implements TimeStamped, Serializable { this.xCoeffs = xCoeffs; this.yCoeffs = yCoeffs; this.zCoeffs = zCoeffs; + this.vScale = 2 / duration; + this.aScale = this.vScale * this.vScale; } /** {@inheritDoc} */ @@ -84,6 +91,23 @@ public AbsoluteDate getDate() { return start; } + /** Compute value of Chebyshev's polynomial independent variable. + * @param date date + * @return double independent variable value + */ + private double computeValueIndependentVariable(final AbsoluteDate date) { + return (2 * date.offsetFrom(start, timeScale) - duration) / duration; + } + + /** Compute value of Chebyshev's polynomial independent variable. + * @param date date + * @param type of the field elements + * @return independent variable value + */ + private > T computeValueIndependentVariable(final FieldAbsoluteDate date) { + return date.offsetFrom(new FieldAbsoluteDate<>(date.getField(), start), timeScale).multiply(2).subtract(duration).divide(duration); + } + /** Check if a date is in validity range. * @param date date to check * @return true if date is in validity range @@ -93,14 +117,89 @@ public boolean inRange(final AbsoluteDate date) { return dt >= -0.001 && dt <= duration + 0.001; } + /** Get the position at a specified date. + * @param date date at which position is requested + * @return position at specified date + */ + Vector3D getPosition(final AbsoluteDate date) { + + // normalize date + final double t = computeValueIndependentVariable(date); + final double twoT = 2 * t; + + // initialize Chebyshev polynomials recursion + double pKm1 = 1; + double pK = t; + double xP = xCoeffs[0]; + double yP = yCoeffs[0]; + double zP = zCoeffs[0]; + + // combine polynomials by applying coefficients + for (int k = 1; k < xCoeffs.length; ++k) { + + // consider last computed polynomials on position + xP += xCoeffs[k] * pK; + yP += yCoeffs[k] * pK; + zP += zCoeffs[k] * pK; + + // compute next Chebyshev polynomial value + final double pKm2 = pKm1; + pKm1 = pK; + pK = twoT * pKm1 - pKm2; + + } + + return new Vector3D(xP, yP, zP); + } + + /** Get the position at a specified date. + * @param date date at which position is requested + * @param type of the field elements + * @return position at specified date + */ + > FieldVector3D getPosition(final FieldAbsoluteDate date) { + + final T zero = date.getField().getZero(); + final T one = date.getField().getOne(); + + // normalize date + final T t = computeValueIndependentVariable(date); + final T twoT = t.add(t); + + // initialize Chebyshev polynomials recursion + T pKm1 = one; + T pK = t; + T xP = zero.add(xCoeffs[0]); + T yP = zero.add(yCoeffs[0]); + T zP = zero.add(zCoeffs[0]); + + // combine polynomials by applying coefficients + for (int k = 1; k < xCoeffs.length; ++k) { + + // consider last computed polynomials on position + xP = xP.add(pK.multiply(xCoeffs[k])); + yP = yP.add(pK.multiply(yCoeffs[k])); + zP = zP.add(pK.multiply(zCoeffs[k])); + + // compute next Chebyshev polynomial value + final T pKm2 = pKm1; + pKm1 = pK; + pK = twoT.multiply(pKm1).subtract(pKm2); + + } + + return new FieldVector3D<>(xP, yP, zP); + + } + /** Get the position-velocity-acceleration at a specified date. * @param date date at which position-velocity-acceleration is requested * @return position-velocity-acceleration at specified date */ - public PVCoordinates getPositionVelocityAcceleration(final AbsoluteDate date) { + PVCoordinates getPositionVelocityAcceleration(final AbsoluteDate date) { // normalize date - final double t = (2 * date.offsetFrom(start, timeScale) - duration) / duration; + final double t = computeValueIndependentVariable(date); final double twoT = 2 * t; // initialize Chebyshev polynomials recursion @@ -159,8 +258,6 @@ public PVCoordinates getPositionVelocityAcceleration(final AbsoluteDate date) { } - final double vScale = 2 / duration; - final double aScale = vScale * vScale; return new PVCoordinates(new Vector3D(xP, yP, zP), new Vector3D(xV * vScale, yV * vScale, zV * vScale), new Vector3D(xA * aScale, yA * aScale, zA * aScale)); @@ -169,16 +266,16 @@ public PVCoordinates getPositionVelocityAcceleration(final AbsoluteDate date) { /** Get the position-velocity-acceleration at a specified date. * @param date date at which position-velocity-acceleration is requested - * @param type fo the field elements + * @param type of the field elements * @return position-velocity-acceleration at specified date */ - public > FieldPVCoordinates getPositionVelocityAcceleration(final FieldAbsoluteDate date) { + > FieldPVCoordinates getPositionVelocityAcceleration(final FieldAbsoluteDate date) { final T zero = date.getField().getZero(); final T one = date.getField().getOne(); // normalize date - final T t = date.offsetFrom(new FieldAbsoluteDate<>(date.getField(), start), timeScale).multiply(2).subtract(duration).divide(duration); + final T t = computeValueIndependentVariable(date); final T twoT = t.add(t); // initialize Chebyshev polynomials recursion @@ -237,8 +334,6 @@ public > FieldPVCoordinates getPositionVelo } - final double vScale = 2 / duration; - final double aScale = vScale * vScale; return new FieldPVCoordinates<>(new FieldVector3D<>(xP, yP, zP), new FieldVector3D<>(xV.multiply(vScale), yV.multiply(vScale), zV.multiply(vScale)), new FieldVector3D<>(xA.multiply(aScale), yA.multiply(aScale), zA.multiply(aScale))); diff --git a/src/main/java/org/orekit/bodies/PredefinedIAUPoles.java b/src/main/java/org/orekit/bodies/PredefinedIAUPoles.java index b3b914a86e..4158450419 100644 --- a/src/main/java/org/orekit/bodies/PredefinedIAUPoles.java +++ b/src/main/java/org/orekit/bodies/PredefinedIAUPoles.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/bodies/package-info.java b/src/main/java/org/orekit/bodies/package-info.java index 58d9d733e0..7ec4f48119 100644 --- a/src/main/java/org/orekit/bodies/package-info.java +++ b/src/main/java/org/orekit/bodies/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -47,8 +47,8 @@ *

      *  CelestialBody sun      = CelestialBodyFactory.getSun();
      *  CelestialBody moon     = CelestialBodyFactory.getMoon();
    - *  Vector3D sunInEME2000  = sun.getPVCoordinates(date, Frame.getEME2000()).getPosition();
    - *  Vector3D moonInEME2000 = moon.getPVCoordinates(date, Frame.getEME2000()).getPosition();
    + *  Vector3D sunInEME2000  = sun.getPosition(date, Frame.getEME2000());
    + *  Vector3D moonInEME2000 = moon.getPosition(date, Frame.getEME2000());
      *
    * *

    diff --git a/src/main/java/org/orekit/compiler/plugin/DefaultDataContextPlugin.java b/src/main/java/org/orekit/compiler/plugin/DefaultDataContextPlugin.java index 3d51ea3061..9dde32d0d3 100644 --- a/src/main/java/org/orekit/compiler/plugin/DefaultDataContextPlugin.java +++ b/src/main/java/org/orekit/compiler/plugin/DefaultDataContextPlugin.java @@ -66,6 +66,17 @@ public class DefaultDataContextPlugin implements Plugin, TaskListener { /** Compiler Trees. */ private Trees trees; + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public DefaultDataContextPlugin() { + // nothing to do + } + @Override public String getName() { return "dataContextPlugin"; diff --git a/src/main/java/org/orekit/compiler/plugin/package-info.java b/src/main/java/org/orekit/compiler/plugin/package-info.java index 867e3ea803..d2a8403f96 100644 --- a/src/main/java/org/orekit/compiler/plugin/package-info.java +++ b/src/main/java/org/orekit/compiler/plugin/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/AbstractListCrawler.java b/src/main/java/org/orekit/data/AbstractListCrawler.java index 7522e1b801..5ae28af491 100644 --- a/src/main/java/org/orekit/data/AbstractListCrawler.java +++ b/src/main/java/org/orekit/data/AbstractListCrawler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/BodiesElements.java b/src/main/java/org/orekit/data/BodiesElements.java index ecd982d7cc..79d58c1b56 100644 --- a/src/main/java/org/orekit/data/BodiesElements.java +++ b/src/main/java/org/orekit/data/BodiesElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/ClasspathCrawler.java b/src/main/java/org/orekit/data/ClasspathCrawler.java index e17469d794..728d8689a3 100644 --- a/src/main/java/org/orekit/data/ClasspathCrawler.java +++ b/src/main/java/org/orekit/data/ClasspathCrawler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/DataFilter.java b/src/main/java/org/orekit/data/DataFilter.java index a1831b581b..fe426c3ba6 100644 --- a/src/main/java/org/orekit/data/DataFilter.java +++ b/src/main/java/org/orekit/data/DataFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/DataLoader.java b/src/main/java/org/orekit/data/DataLoader.java index 65ce82374e..d67a09075d 100644 --- a/src/main/java/org/orekit/data/DataLoader.java +++ b/src/main/java/org/orekit/data/DataLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/DataProvider.java b/src/main/java/org/orekit/data/DataProvider.java index fa745beea3..57a3ea8ce5 100644 --- a/src/main/java/org/orekit/data/DataProvider.java +++ b/src/main/java/org/orekit/data/DataProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/DataProvidersManager.java b/src/main/java/org/orekit/data/DataProvidersManager.java index e0a16496d2..841cb4a90c 100644 --- a/src/main/java/org/orekit/data/DataProvidersManager.java +++ b/src/main/java/org/orekit/data/DataProvidersManager.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,7 +30,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.gnss.HatanakaCompressFilter; +import org.orekit.files.rinex.HatanakaCompressFilter; /** This class manages supported {@link DataProvider data providers}. *

    diff --git a/src/main/java/org/orekit/data/DataSource.java b/src/main/java/org/orekit/data/DataSource.java index fc989ec419..5c66a7037f 100644 --- a/src/main/java/org/orekit/data/DataSource.java +++ b/src/main/java/org/orekit/data/DataSource.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/DelaunayArguments.java b/src/main/java/org/orekit/data/DelaunayArguments.java index d43f88d487..7f877f6d04 100644 --- a/src/main/java/org/orekit/data/DelaunayArguments.java +++ b/src/main/java/org/orekit/data/DelaunayArguments.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/DirectoryCrawler.java b/src/main/java/org/orekit/data/DirectoryCrawler.java index bf52f3d325..cfe7a7e8fd 100644 --- a/src/main/java/org/orekit/data/DirectoryCrawler.java +++ b/src/main/java/org/orekit/data/DirectoryCrawler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/ExceptionalDataContext.java b/src/main/java/org/orekit/data/ExceptionalDataContext.java index 27f152bb86..dad55a4851 100644 --- a/src/main/java/org/orekit/data/ExceptionalDataContext.java +++ b/src/main/java/org/orekit/data/ExceptionalDataContext.java @@ -16,7 +16,6 @@ */ package org.orekit.data; -import org.orekit.attitudes.InertialProvider; import org.orekit.bodies.LazyLoadedCelestialBodies; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -34,7 +33,6 @@ * *

      *
    • {@link AbsoluteDate} - *
    • {@link InertialProvider} *
    * * @author Evan Ward @@ -43,6 +41,17 @@ */ public class ExceptionalDataContext extends LazyLoadedDataContext implements DataContext { + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public ExceptionalDataContext() { + // nothing to do + } + @Override public LazyLoadedTimeScales getTimeScales() { throw new OrekitException(OrekitMessages.EXCEPTIONAL_DATA_CONTEXT); diff --git a/src/main/java/org/orekit/data/FieldBodiesElements.java b/src/main/java/org/orekit/data/FieldBodiesElements.java index 0158244c61..5534518371 100644 --- a/src/main/java/org/orekit/data/FieldBodiesElements.java +++ b/src/main/java/org/orekit/data/FieldBodiesElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/FieldDelaunayArguments.java b/src/main/java/org/orekit/data/FieldDelaunayArguments.java index 7ba9ff6788..78b8bddfce 100644 --- a/src/main/java/org/orekit/data/FieldDelaunayArguments.java +++ b/src/main/java/org/orekit/data/FieldDelaunayArguments.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/FilesListCrawler.java b/src/main/java/org/orekit/data/FilesListCrawler.java index d31a5b5ead..51e5e3b376 100644 --- a/src/main/java/org/orekit/data/FilesListCrawler.java +++ b/src/main/java/org/orekit/data/FilesListCrawler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/FiltersManager.java b/src/main/java/org/orekit/data/FiltersManager.java index 111b942a43..bc9de634dd 100644 --- a/src/main/java/org/orekit/data/FiltersManager.java +++ b/src/main/java/org/orekit/data/FiltersManager.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/FundamentalNutationArguments.java b/src/main/java/org/orekit/data/FundamentalNutationArguments.java index c6b63f9e8f..2372303008 100644 --- a/src/main/java/org/orekit/data/FundamentalNutationArguments.java +++ b/src/main/java/org/orekit/data/FundamentalNutationArguments.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/GeneralTerm.java b/src/main/java/org/orekit/data/GeneralTerm.java index badda717e4..3e4bba2d23 100644 --- a/src/main/java/org/orekit/data/GeneralTerm.java +++ b/src/main/java/org/orekit/data/GeneralTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/GzipFilter.java b/src/main/java/org/orekit/data/GzipFilter.java index e3efbee17b..5699ee42ae 100644 --- a/src/main/java/org/orekit/data/GzipFilter.java +++ b/src/main/java/org/orekit/data/GzipFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,6 +27,17 @@ public class GzipFilter implements DataFilter { /** Suffix for gzip compressed files. */ private static final String SUFFIX = ".gz"; + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public GzipFilter() { + // nothing to do + } + /** {@inheritDoc} */ @Override public DataSource filter(final DataSource original) { diff --git a/src/main/java/org/orekit/data/LuniSolarTerm.java b/src/main/java/org/orekit/data/LuniSolarTerm.java index 3439aa43d6..ddd6ad92d5 100644 --- a/src/main/java/org/orekit/data/LuniSolarTerm.java +++ b/src/main/java/org/orekit/data/LuniSolarTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/NetworkCrawler.java b/src/main/java/org/orekit/data/NetworkCrawler.java index b8b1ccac67..746754200d 100644 --- a/src/main/java/org/orekit/data/NetworkCrawler.java +++ b/src/main/java/org/orekit/data/NetworkCrawler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/NoFarPlanetsTerm.java b/src/main/java/org/orekit/data/NoFarPlanetsTerm.java index 9e11244b0a..a286b5e06c 100644 --- a/src/main/java/org/orekit/data/NoFarPlanetsTerm.java +++ b/src/main/java/org/orekit/data/NoFarPlanetsTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/NutationCodec.java b/src/main/java/org/orekit/data/NutationCodec.java index 8f0a47667a..a6bf98d7af 100644 --- a/src/main/java/org/orekit/data/NutationCodec.java +++ b/src/main/java/org/orekit/data/NutationCodec.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/PlanetaryTerm.java b/src/main/java/org/orekit/data/PlanetaryTerm.java index 4e07c20f97..c1acc8ea45 100644 --- a/src/main/java/org/orekit/data/PlanetaryTerm.java +++ b/src/main/java/org/orekit/data/PlanetaryTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/PoissonSeries.java b/src/main/java/org/orekit/data/PoissonSeries.java index 2f5c64397c..29a633e611 100644 --- a/src/main/java/org/orekit/data/PoissonSeries.java +++ b/src/main/java/org/orekit/data/PoissonSeries.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -96,7 +96,7 @@ public double value(final BodiesElements elements) { /** Evaluate the value of the series. * @param elements bodies elements for nutation - * @param type fo the field elements + * @param type of the field elements * @return value of the series */ public > T value(final FieldBodiesElements elements) { diff --git a/src/main/java/org/orekit/data/PoissonSeriesParser.java b/src/main/java/org/orekit/data/PoissonSeriesParser.java index 77ee16c4a4..1d7bb04d6a 100644 --- a/src/main/java/org/orekit/data/PoissonSeriesParser.java +++ b/src/main/java/org/orekit/data/PoissonSeriesParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/PolynomialNutation.java b/src/main/java/org/orekit/data/PolynomialNutation.java index 0f6fc3c88a..fdbc464291 100644 --- a/src/main/java/org/orekit/data/PolynomialNutation.java +++ b/src/main/java/org/orekit/data/PolynomialNutation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/PolynomialParser.java b/src/main/java/org/orekit/data/PolynomialParser.java index d06e5a4078..72ede7c872 100644 --- a/src/main/java/org/orekit/data/PolynomialParser.java +++ b/src/main/java/org/orekit/data/PolynomialParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/SeriesTerm.java b/src/main/java/org/orekit/data/SeriesTerm.java index 312c5b6ead..23a747c1b9 100644 --- a/src/main/java/org/orekit/data/SeriesTerm.java +++ b/src/main/java/org/orekit/data/SeriesTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/SimpleTimeStampedTableParser.java b/src/main/java/org/orekit/data/SimpleTimeStampedTableParser.java index 8160d295ea..4de7fe7b23 100644 --- a/src/main/java/org/orekit/data/SimpleTimeStampedTableParser.java +++ b/src/main/java/org/orekit/data/SimpleTimeStampedTableParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/TideTerm.java b/src/main/java/org/orekit/data/TideTerm.java index 30e8dda732..d983cbdae0 100644 --- a/src/main/java/org/orekit/data/TideTerm.java +++ b/src/main/java/org/orekit/data/TideTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/TruncatingFilter.java b/src/main/java/org/orekit/data/TruncatingFilter.java new file mode 100644 index 0000000000..bcb9f1b985 --- /dev/null +++ b/src/main/java/org/orekit/data/TruncatingFilter.java @@ -0,0 +1,118 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.data; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +import org.hipparchus.util.FastMath; + +/** Filter for truncating line-oriented files. + *

    + * This filter is mainly intended for test purposes, but may also + * be used to filter out unwanted trailing data in time series for example + *

    + * @author Luc Maisonobe + * @since 12.0 + */ +public class TruncatingFilter implements DataFilter { + + /** Number of lines to keep. */ + private final int nbLines; + + /** Simple constructor. + * @param nbLines number of lines to keep + */ + public TruncatingFilter(final int nbLines) { + this.nbLines = nbLines; + } + + /** {@inheritDoc} */ + @Override + public DataSource filter(final DataSource original) throws IOException { + return new DataSource(original.getName() + "-truncated-after-line-" + nbLines, + () -> new TruncatingReader(original.getOpener().openReaderOnce())); + } + + private class TruncatingReader extends Reader { + + /** Line-oriented reader for raw data. */ + private final BufferedReader reader; + + /** Number of lines already read. */ + private int linesRead; + + /** Pending line, read but not output. */ + private String pending; + + /** Number of characters already output in pending line. */ + private int countOut; + + TruncatingReader(final Reader reader) { + this.reader = new BufferedReader(reader); + } + + /** {@inheritDoc} */ + @Override + public int read(final char[] b, final int offset, final int len) throws IOException { + + if (linesRead < nbLines) { + + if (pending == null) { + // we need to read another part from the underlying characters stream + countOut = 0; + pending = reader.readLine(); + if (pending == null) { + // there are no lines left + return -1; + } + } + + // copy as many characters as possible from current line + int n = FastMath.min(len, pending.length() - countOut); + for (int i = 0; i < n; ++i) { + b[offset + i] = pending.charAt(countOut + i); + } + + if (n < len) { + // line has been completed and we can still output end of line + b[offset + n] = '\n'; + ++linesRead; + pending = null; + return ++n; + } else { + // there are still some pending characters + countOut += n; + return n; + } + + } else { + return -1; + } + + } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException { + reader.close(); + } + + } + +} diff --git a/src/main/java/org/orekit/data/UnixCompressFilter.java b/src/main/java/org/orekit/data/UnixCompressFilter.java index a2c2abaa11..71e4cbb89c 100644 --- a/src/main/java/org/orekit/data/UnixCompressFilter.java +++ b/src/main/java/org/orekit/data/UnixCompressFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,6 +34,17 @@ public class UnixCompressFilter implements DataFilter { /** Suffix for Unix compressed files. */ private static final String SUFFIX = ".Z"; + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public UnixCompressFilter() { + // nothing to do + } + /** {@inheritDoc} */ @Override public DataSource filter(final DataSource original) { diff --git a/src/main/java/org/orekit/data/ZipJarCrawler.java b/src/main/java/org/orekit/data/ZipJarCrawler.java index a39e43485c..c25ec3e7ac 100644 --- a/src/main/java/org/orekit/data/ZipJarCrawler.java +++ b/src/main/java/org/orekit/data/ZipJarCrawler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/data/package-info.java b/src/main/java/org/orekit/data/package-info.java index fa0a3d9449..bbd6cc6d5d 100644 --- a/src/main/java/org/orekit/data/package-info.java +++ b/src/main/java/org/orekit/data/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/FrameAncestorException.java b/src/main/java/org/orekit/errors/FrameAncestorException.java index 5db6c722ba..03e8ce2aa5 100644 --- a/src/main/java/org/orekit/errors/FrameAncestorException.java +++ b/src/main/java/org/orekit/errors/FrameAncestorException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/LocalizedException.java b/src/main/java/org/orekit/errors/LocalizedException.java index 6504ea2934..b73155f84c 100644 --- a/src/main/java/org/orekit/errors/LocalizedException.java +++ b/src/main/java/org/orekit/errors/LocalizedException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/OrekitException.java b/src/main/java/org/orekit/errors/OrekitException.java index 2ec6e0d42f..58dcb86c50 100644 --- a/src/main/java/org/orekit/errors/OrekitException.java +++ b/src/main/java/org/orekit/errors/OrekitException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/OrekitIOException.java b/src/main/java/org/orekit/errors/OrekitIOException.java index 3c60766f2a..102df1b3ae 100644 --- a/src/main/java/org/orekit/errors/OrekitIOException.java +++ b/src/main/java/org/orekit/errors/OrekitIOException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/OrekitIllegalArgumentException.java b/src/main/java/org/orekit/errors/OrekitIllegalArgumentException.java index 8bd76aa031..3d2bf7b5b7 100644 --- a/src/main/java/org/orekit/errors/OrekitIllegalArgumentException.java +++ b/src/main/java/org/orekit/errors/OrekitIllegalArgumentException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/OrekitIllegalStateException.java b/src/main/java/org/orekit/errors/OrekitIllegalStateException.java index a9d1bce7ae..2dfd260bd8 100644 --- a/src/main/java/org/orekit/errors/OrekitIllegalStateException.java +++ b/src/main/java/org/orekit/errors/OrekitIllegalStateException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/OrekitInternalError.java b/src/main/java/org/orekit/errors/OrekitInternalError.java index 3257fd466a..65922ee543 100644 --- a/src/main/java/org/orekit/errors/OrekitInternalError.java +++ b/src/main/java/org/orekit/errors/OrekitInternalError.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/OrekitMessages.java b/src/main/java/org/orekit/errors/OrekitMessages.java index 3ab7fe2853..289a40acc4 100644 --- a/src/main/java/org/orekit/errors/OrekitMessages.java +++ b/src/main/java/org/orekit/errors/OrekitMessages.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -45,304 +45,872 @@ */ public enum OrekitMessages implements Localizable { - // CHECKSTYLE: stop JavadocVariable check - + /** INTERNAL_ERROR. */ INTERNAL_ERROR("internal error, please notify development team by creating a new topic at {0}"), + + /** ALTITUDE_BELOW_ALLOWED_THRESHOLD. */ ALTITUDE_BELOW_ALLOWED_THRESHOLD("altitude ({0} m) is below the {1} m allowed threshold"), + + /** POINT_INSIDE_ELLIPSOID. */ POINT_INSIDE_ELLIPSOID("point is inside ellipsoid"), + + /** TRAJECTORY_INSIDE_BRILLOUIN_SPHERE. */ TRAJECTORY_INSIDE_BRILLOUIN_SPHERE("trajectory inside the Brillouin sphere (r = {0})"), + + /** ALMOST_EQUATORIAL_ORBIT. */ ALMOST_EQUATORIAL_ORBIT("almost equatorial orbit (i = {0} degrees)"), + + /** ALMOST_CRITICALLY_INCLINED_ORBIT. */ ALMOST_CRITICALLY_INCLINED_ORBIT("almost critically inclined orbit (i = {0} degrees)"), - UNABLE_TO_COMPUTE_ECKSTEIN_HECHLER_MEAN_PARAMETERS( - "unable to compute Eckstein-Hechler mean parameters after {0} iterations"), - UNABLE_TO_COMPUTE_BROUWER_LYDDANE_MEAN_PARAMETERS( - "unable to compute Brouwer-Lyddane mean parameters after {0} iterations"), - UNABLE_TO_COMPUTE_TLE( - "unable to compute TLE after {0} iterations"), + + /** UNABLE_TO_COMPUTE_ECKSTEIN_HECHLER_MEAN_PARAMETERS. */ + UNABLE_TO_COMPUTE_ECKSTEIN_HECHLER_MEAN_PARAMETERS("unable to compute Eckstein-Hechler mean parameters after {0} iterations"), + + /** UNABLE_TO_COMPUTE_BROUWER_LYDDANE_MEAN_PARAMETERS. */ + UNABLE_TO_COMPUTE_BROUWER_LYDDANE_MEAN_PARAMETERS("unable to compute Brouwer-Lyddane mean parameters after {0} iterations"), + + /** UNABLE_TO_COMPUTE_TLE. */ + UNABLE_TO_COMPUTE_TLE("unable to compute TLE after {0} iterations"), + + /** NULL_PARENT_FOR_FRAME. */ NULL_PARENT_FOR_FRAME("null parent for frame {0}"), + + /** FRAME_ALREADY_ATTACHED. */ FRAME_ALREADY_ATTACHED("frame {0} is already attached to frame {1}"), + + /** FRAME_NOT_ATTACHED. */ FRAME_NOT_ATTACHED("frame {0} is not attached to the main frames tree"), + + /** FRAME_ANCESTOR_OF_BOTH_FRAMES. */ FRAME_ANCESTOR_OF_BOTH_FRAMES("frame {0} is an ancestor of both frames {1} and {2}"), + + /** FRAME_ANCESTOR_OF_NEITHER_FRAME. */ FRAME_ANCESTOR_OF_NEITHER_FRAME("frame {0} is an ancestor of neither frame {1} nor {2}"), + + /** FRAME_NO_NTH_ANCESTOR. */ FRAME_NO_NTH_ANCESTOR("frame {0} has depth {1}, it cannot have an ancestor {2} levels above"), + + /** NO_SUCH_ITRF_FRAME. */ NO_SUCH_ITRF_FRAME("ITRF frame {0} not found"), + + /** UNSUPPORTED_LOCAL_ORBITAL_FRAME. */ UNSUPPORTED_LOCAL_ORBITAL_FRAME("unsupported local orbital frame {0}"), + + /** NON_PSEUDO_INERTIAL_FRAME. */ NON_PSEUDO_INERTIAL_FRAME("non pseudo-inertial frame \"{0}\""), + + /** DATA_ROOT_DIRECTORY_DOES_NOT_EXIST. */ DATA_ROOT_DIRECTORY_DOES_NOT_EXIST("data root directory {0} does not exist"), + + /** NOT_A_DIRECTORY. */ NOT_A_DIRECTORY("{0} is not a directory"), + + /** NEITHER_DIRECTORY_NOR_ZIP_OR_JAR. */ NEITHER_DIRECTORY_NOR_ZIP_OR_JAR("{0} is neither a directory nor a zip/jar archive file"), + + /** UNABLE_TO_FIND_RESOURCE. */ UNABLE_TO_FIND_RESOURCE("unable to find resource {0} in classpath"), + + /** NO_EARTH_ORIENTATION_PARAMETERS_LOADED. */ NO_EARTH_ORIENTATION_PARAMETERS_LOADED("no Earth Orientation Parameters loaded"), + + /** MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES. */ MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES("missing Earth Orientation Parameters between {0} and {1}"), + + /** MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES_GAP. */ MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES_GAP("missing Earth Orientation Parameters between {0} and {1}, gap is {2,number,0.0##############E0} s"), + + /** NO_EARTH_ORIENTATION_PARAMETERS. */ NO_EARTH_ORIENTATION_PARAMETERS("missing Earth Orientation Parameters"), + + /** NOT_A_SUPPORTED_IERS_DATA_FILE. */ NOT_A_SUPPORTED_IERS_DATA_FILE("file {0} is not a supported IERS data file"), + + /** INCONSISTENT_DATES_IN_IERS_FILE. */ INCONSISTENT_DATES_IN_IERS_FILE("inconsistent dates in IERS file {0}: {1}-{2}-{3} and MJD {4}"), + + /** UNEXPECTED_DATA_AFTER_LINE_IN_FILE. */ UNEXPECTED_DATA_AFTER_LINE_IN_FILE("unexpected data after line {0} in file {1}: {2}"), + + /** UNEXPECTED_DATA_AT_LINE_IN_FILE. */ UNEXPECTED_DATA_AT_LINE_IN_FILE("unexpected data at line {0} in file {1}"), + + /** NON_CHRONOLOGICAL_DATES_IN_FILE. */ NON_CHRONOLOGICAL_DATES_IN_FILE("non-chronological dates in file {0}, line {1}"), + + /** NO_IERS_UTC_TAI_HISTORY_DATA_LOADED. */ NO_IERS_UTC_TAI_HISTORY_DATA_LOADED("no IERS UTC-TAI history data loaded"), + + /** NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE. */ NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE("no entries found in IERS UTC-TAI history file {0}"), + + /** MISSING_SERIE_J_IN_FILE. */ MISSING_SERIE_J_IN_FILE("missing serie j = {0} in file {1} (line {2})"), + + /** CANNOT_PARSE_BOTH_TAU_AND_GAMMA. */ CANNOT_PARSE_BOTH_TAU_AND_GAMMA("cannot parse both τ and γ from the same Poissons series file"), + + /** UNEXPECTED_END_OF_FILE_AFTER_LINE. */ UNEXPECTED_END_OF_FILE_AFTER_LINE("unexpected end of file {0} (after line {1})"), + + /** UNABLE_TO_PARSE_LINE_IN_FILE. */ UNABLE_TO_PARSE_LINE_IN_FILE("unable to parse line {0} of file {1}:\n{2}"), + + /** UNABLE_TO_PARSE_ELEMENT_IN_FILE. */ UNABLE_TO_PARSE_ELEMENT_IN_FILE("unable to parse element {0} at line {1}, file {2}"), + + /** UNABLE_TO_FIND_FILE. */ UNABLE_TO_FIND_FILE("unable to find file {0}"), - SPACECRAFT_MASS_BECOMES_NEGATIVE("spacecraft mass becomes negative: {0} kg"), + + /** POSITIVE_FLOW_RATE. */ POSITIVE_FLOW_RATE("positive flow rate (q: {0})"), + + /** NO_GRAVITY_FIELD_DATA_LOADED. */ NO_GRAVITY_FIELD_DATA_LOADED("no gravity field data loaded"), + + /** GRAVITY_FIELD_NORMALIZATION_UNDERFLOW. */ GRAVITY_FIELD_NORMALIZATION_UNDERFLOW("gravity field normalization underflow for degree {0} and order {1}"), + + /** NO_OCEAN_TIDE_DATA_LOADED. */ NO_OCEAN_TIDE_DATA_LOADED("no ocean tide data loaded"), + + /** OCEAN_TIDE_DATA_DEGREE_ORDER_LIMITS. */ OCEAN_TIDE_DATA_DEGREE_ORDER_LIMITS("ocean tide data file {0} limited to degree {1} and order {2}"), - OCEAN_TIDE_LOAD_DEFORMATION_LIMITS( - "load deformation coefficients limited to degree {0}, cannot parse degree {1} term from file {2}"), + + /** OCEAN_TIDE_LOAD_DEFORMATION_LIMITS. */ + OCEAN_TIDE_LOAD_DEFORMATION_LIMITS("load deformation coefficients limited to degree {0}, cannot parse degree {1} term from file {2}"), + + /** POLAR_TRAJECTORY. */ POLAR_TRAJECTORY("polar trajectory (distance to polar axis: {0})"), + + /** UNEXPECTED_FILE_FORMAT_ERROR_FOR_LOADER. */ UNEXPECTED_FILE_FORMAT_ERROR_FOR_LOADER("unexpected format error for file {0} with loader {1}"), + + /** DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE. */ DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE("duplicated gravity field coefficient {0}({1}, {2}) in file {3}"), + + /** MISSING_GRAVITY_FIELD_COEFFICIENT_IN_FILE. */ MISSING_GRAVITY_FIELD_COEFFICIENT_IN_FILE("missing gravity field coefficient {0}({1}, {2}) in file {3}"), + + /** TOO_LARGE_DEGREE_FOR_GRAVITY_FIELD. */ TOO_LARGE_DEGREE_FOR_GRAVITY_FIELD("too large degree (n = {0}, potential maximal degree is {1})"), + + /** TOO_LARGE_ORDER_FOR_GRAVITY_FIELD. */ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD("too large order (m = {0}, potential maximal order is {1})"), + + /** WRONG_DEGREE_OR_ORDER. */ WRONG_DEGREE_OR_ORDER("no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition"), + + /** SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD. */ SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD("several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2}"), + + /** NO_TLE_FOR_OBJECT. */ NO_TLE_FOR_OBJECT("no TLE data available for object {0}"), - NO_TLE_FOR_LAUNCH_YEAR_NUMBER_PIECE( - "no TLE data available for launch year {0}, launch number {1}, launch piece {2}"), + + /** NO_TLE_FOR_LAUNCH_YEAR_NUMBER_PIECE. */ + NO_TLE_FOR_LAUNCH_YEAR_NUMBER_PIECE("no TLE data available for launch year {0}, launch number {1}, launch piece {2}"), + + /** NOT_TLE_LINES. */ NOT_TLE_LINES("lines {0} and {1} are not TLE lines:\n{0}: \"{2}\"\n{1}: \"{3}\""), + + /** MISSING_SECOND_TLE_LINE. */ MISSING_SECOND_TLE_LINE("expected a second TLE line after line {0}:\n{0}: \"{1}\""), + + /** TLE_LINES_DO_NOT_REFER_TO_SAME_OBJECT. */ TLE_LINES_DO_NOT_REFER_TO_SAME_OBJECT("TLE lines do not refer to the same object:\n{0}\n{1}"), + + /** TLE_INVALID_PARAMETER. */ TLE_INVALID_PARAMETER("invalid TLE parameter for object {0}: {1} = {2}"), + + /** TLE_CHECKSUM_ERROR. */ TLE_CHECKSUM_ERROR("wrong checksum of TLE line {0}, expected {1} but got {2} ({3})"), + + /** NO_TLE_DATA_AVAILABLE. */ NO_TLE_DATA_AVAILABLE("no TLE data available"), + + /** NOT_POSITIVE_SPACECRAFT_MASS. */ NOT_POSITIVE_SPACECRAFT_MASS("spacecraft mass is not positive: {0} kg"), + + /** TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL. */ TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL("too large eccentricity for propagation model: e = {0}"), + + /** NO_SOLAR_ACTIVITY_AT_DATE. */ NO_SOLAR_ACTIVITY_AT_DATE("no solar activity available at {0}, data available only in range [{1}, {2}]"), + + /** NON_EXISTENT_MONTH. */ NON_EXISTENT_MONTH("non-existent month {0}"), + + /** NON_EXISTENT_YEAR_MONTH_DAY. */ NON_EXISTENT_YEAR_MONTH_DAY("non-existent date {0}-{1}-{2}"), + + /** NON_EXISTENT_WEEK_DATE. */ NON_EXISTENT_WEEK_DATE("non-existent week date {0}-W{1}-{2}"), + + /** NON_EXISTENT_DATE. */ NON_EXISTENT_DATE("non-existent date {0}"), + + /** NON_EXISTENT_DAY_NUMBER_IN_YEAR. */ NON_EXISTENT_DAY_NUMBER_IN_YEAR("no day number {0} in year {1}"), + + /** NON_EXISTENT_HMS_TIME. */ NON_EXISTENT_HMS_TIME("non-existent time {0}:{1}:{2}"), + + /** NON_EXISTENT_TIME. */ NON_EXISTENT_TIME("non-existent time {0}"), + + /** OUT_OF_RANGE_SECONDS_NUMBER. */ OUT_OF_RANGE_SECONDS_NUMBER("out of range seconds number: {0}"), + + /** OUT_OF_RANGE_SECONDS_NUMBER_DETAIL. */ OUT_OF_RANGE_SECONDS_NUMBER_DETAIL("out of range seconds number: {0} is not in [{1}, {2}]"), + + /** ANGLE_TYPE_NOT_SUPPORTED. */ ANGLE_TYPE_NOT_SUPPORTED("angle type not supported, supported angles: {0}, {1} and {2}"), + + /** SATELLITE_COLLIDED_WITH_TARGET. */ SATELLITE_COLLIDED_WITH_TARGET("satellite collided with target"), + + /** ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND. */ ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND("attitude pointing law misses ground"), - TOO_SHORT_TRANSITION_TIME_FOR_ATTITUDES_SWITCH( - "{0} seconds transition time for attitudes switch is too short, should be longer than {1} seconds"), + + /** TOO_SHORT_TRANSITION_TIME_FOR_ATTITUDES_SWITCH. */ + TOO_SHORT_TRANSITION_TIME_FOR_ATTITUDES_SWITCH("{0} seconds transition time for attitudes switch is too short, should be longer than {1} seconds"), + + /** ORBIT_AND_ATTITUDE_DATES_MISMATCH. */ ORBIT_AND_ATTITUDE_DATES_MISMATCH("orbit date ({0}) does not match attitude date ({1})"), + + /** FRAMES_MISMATCH. */ FRAMES_MISMATCH("frame {0} does not match frame {1}"), + + /** INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION. */ INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION("initial state not specified for orbit propagation"), - EVENT_DATE_TOO_CLOSE( - "target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added"), + + /** EVENT_DATE_TOO_CLOSE. */ + EVENT_DATE_TOO_CLOSE("target event date must be before {1} by {3,number,0.0##############E0} seconds or after {2} by {3,number,0.0##############E0} seconds, but target event date {0} is {4,number,0.0##############E0} seconds before {1} and {5,number,0.0##############E0} seconds after {2} so it cannot be added"), + + /** UNABLE_TO_READ_JPL_HEADER. */ UNABLE_TO_READ_JPL_HEADER("unable to read header record from JPL ephemerides binary file {0}"), - INCONSISTENT_ASTRONOMICAL_UNIT_IN_FILES( - "inconsistent values of astronomical unit in JPL ephemerides files: ({0} and {1})"), - INCONSISTENT_EARTH_MOON_RATIO_IN_FILES( - "inconsistent values of Earth/Moon mass ratio in JPL ephemerides files: ({0} and {1})"), + + /** INCONSISTENT_ASTRONOMICAL_UNIT_IN_FILES. */ + INCONSISTENT_ASTRONOMICAL_UNIT_IN_FILES("inconsistent values of astronomical unit in JPL ephemerides files: ({0} and {1})"), + + /** INCONSISTENT_EARTH_MOON_RATIO_IN_FILES. */ + INCONSISTENT_EARTH_MOON_RATIO_IN_FILES("inconsistent values of Earth/Moon mass ratio in JPL ephemerides files: ({0} and {1})"), + + /** NO_DATA_LOADED_FOR_CELESTIAL_BODY. */ NO_DATA_LOADED_FOR_CELESTIAL_BODY("no data loaded for celestial body {0}"), + + /** NOT_A_JPL_EPHEMERIDES_BINARY_FILE. */ NOT_A_JPL_EPHEMERIDES_BINARY_FILE("file {0} is not a JPL ephemerides binary file"), - NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE( - "file {0} is not a Marshall Solar Activity Future Estimation (MSAFE) file"), + + /** NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE. */ + NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE("file {0} is not a Marshall Solar Activity Future Estimation (MSAFE) file"), + + /** NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND. */ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND("no JPL ephemerides binary files found"), + + /** OUT_OF_RANGE_BODY_EPHEMERIDES_DATE. */ OUT_OF_RANGE_BODY_EPHEMERIDES_DATE("out of range date for {0} ephemerides: {1}"), - OUT_OF_RANGE_EPHEMERIDES_DATE("out of range date for ephemerides: {0}, [{1}, {2}]"), + + /** OUT_OF_RANGE_DATE. */ + OUT_OF_RANGE_DATE("out of range date: {0}, [{1}, {2}]"), + + /** OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE. */ OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE("out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}]"), + + /** OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER. */ OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER("out of range date for ephemerides: {0} is {3,number,0.0##############E0} s after [{1}, {2}]"), - UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH( - "unexpected two elevation values: {0} and {1}, for one azimuth: {2}"), + + /** UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH. */ + UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH("unexpected two elevation values: {0} and {1}, for one azimuth: {2}"), + + /** UNSUPPORTED_PARAMETER_NAME. */ UNSUPPORTED_PARAMETER_NAME("unsupported parameter name {0}, supported names: {1}"), + + /** PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES. */ + PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES("{0} parameter contains several span in its value TimeSpanMap, the {1} method must be called"), + + /** PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET. */ + PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET("setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed"), + + /** TOO_SMALL_SCALE_FOR_PARAMETER. */ TOO_SMALL_SCALE_FOR_PARAMETER("scale factor for parameter {0} is too small: {1}"), + + /** UNKNOWN_ADDITIONAL_STATE. */ UNKNOWN_ADDITIONAL_STATE("unknown additional state \"{0}\""), + + /** UNKNOWN_MONTH. */ UNKNOWN_MONTH("unknown month \"{0}\""), + + /** SINGULAR_JACOBIAN_FOR_ORBIT_TYPE. */ SINGULAR_JACOBIAN_FOR_ORBIT_TYPE("Jacobian matrix for type {0} is singular with current orbit"), + + /** STATE_JACOBIAN_NOT_INITIALIZED. */ STATE_JACOBIAN_NOT_INITIALIZED("state Jacobian has not been initialized yet"), + + /** STATE_JACOBIAN_NOT_6X6. */ STATE_JACOBIAN_NOT_6X6("state Jacobian is a {0}x{1} matrix, it should be a 6x6 matrix"), + + /** STATE_AND_PARAMETERS_JACOBIANS_ROWS_MISMATCH. */ STATE_AND_PARAMETERS_JACOBIANS_ROWS_MISMATCH("state Jacobian has {0} rows but parameters Jacobian has {1} rows"), - INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH( - "initial Jacobian matrix has {0} columns, but {1} parameters have been selected"), - ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE( - "orbit should be either elliptic with a > 0 and e < 1 or hyperbolic with a < 0 and e > 1, a = {0}, e = {1}"), + + /** INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH. */ + INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH("initial Jacobian matrix has {0} columns, but {1} parameters have been selected"), + + /** ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE. */ + ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE("orbit should be either elliptic with a > 0 and e < 1 or hyperbolic with a < 0 and e > 1, a = {0}, e = {1}"), + + /** ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE. */ ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE("true anomaly {0} out of hyperbolic range (e = {1}, {2} < v < {3})"), + + /** HYPERBOLIC_ORBIT_NOT_HANDLED_AS. */ HYPERBOLIC_ORBIT_NOT_HANDLED_AS("hyperbolic orbits cannot be handled as {0} instances"), + + /** CCSDS_DATE_INVALID_PREAMBLE_FIELD. */ CCSDS_DATE_INVALID_PREAMBLE_FIELD("invalid preamble field in CCSDS date: {0}"), + + /** CCSDS_DATE_INVALID_LENGTH_TIME_FIELD. */ CCSDS_DATE_INVALID_LENGTH_TIME_FIELD("invalid time field length in CCSDS date: {0}, expected {1}"), + + /** CCSDS_DATE_MISSING_AGENCY_EPOCH. */ CCSDS_DATE_MISSING_AGENCY_EPOCH("missing agency epoch in CCSDS date"), + + /** CCSDS_MISSING_KEYWORD. */ CCSDS_MISSING_KEYWORD("missing mandatory key {0} in CCSDS file {1}"), + + /** CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION. */ CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION("key {0} is not allowed in format version {1}"), + + /** CCSDS_UNEXPECTED_KEYWORD. */ CCSDS_UNEXPECTED_KEYWORD("unexpected keyword in CCSDS line number {0} of file {1}:\n{2}"), + + /** CCSDS_UNKNOWN_GM. */ CCSDS_UNKNOWN_GM("the central body gravitational coefficient cannot be retrieved from the ODM"), + + /** CCSDS_UNKNOWN_SPACECRAFT_MASS. */ CCSDS_UNKNOWN_SPACECRAFT_MASS("there is no spacecraft mass associated with this ODM file"), + + /** CCSDS_UNKNOWN_CONVENTIONS. */ CCSDS_UNKNOWN_CONVENTIONS("no IERS conventions have been set before parsing"), + + /** CCSDS_INVALID_FRAME. */ CCSDS_INVALID_FRAME("frame {0} is not valid in this CCSDS file context"), + + /** CCSDS_DIFFERENT_LVLH_DEFINITION. */ + CCSDS_DIFFERENT_LVLH_DEFINITION("this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead"), + + /** CCSDS_INCONSISTENT_TIME_SYSTEMS. */ CCSDS_INCONSISTENT_TIME_SYSTEMS("inconsistent time systems: {0} ≠ {1}"), - CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED( - "use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit"), + + /** CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED. */ + CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED("use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit"), + + /** CCSDS_TDM_KEYWORD_NOT_FOUND. */ CCSDS_TDM_KEYWORD_NOT_FOUND("No CCSDS TDM keyword was found at line {0} of file {1}:\n{2}"), + + /** CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER. */ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER("no Range Units converter configured for parsing Tracking Data Message"), + + /** CCSDS_TIME_SYSTEM_NOT_READ_YET. */ CCSDS_TIME_SYSTEM_NOT_READ_YET("Time system should have already been set before line {0} of file {1}"), - CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS("inconsistent time systems in the attitude blocks: {0} ≠ {1}"), - CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED("attitude type {0} in CCSDS AEM files is not implemented in Orekit"), + + /** CCSDS_UNKNOWN_ATTITUDE_TYPE. */ + CCSDS_UNKNOWN_ATTITUDE_TYPE("unknown attitude type {0}"), + + /** CCSDS_INCOMPLETE_DATA. */ + CCSDS_INCOMPLETE_DATA("incomplete data"), + + /** CCSDS_INVALID_ROTATION_SEQUENCE. */ CCSDS_INVALID_ROTATION_SEQUENCE("invalid rotation sequence {0} at line {1} of file {2}"), + + /** CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE. */ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE("element set type {0} ({1}) is not supported yet"), + + /** CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL. */ CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL("retrograde factor not supported in element set {0}"), - CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS("element set type {0} ({1}) expects {2} elements"), + + /** CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS. */ CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS("wrong number of units for maneuver {0}"), + + /** CCSDS_MANEUVER_MISSING_TIME. */ CCSDS_MANEUVER_MISSING_TIME("missing time field for maneuver {0}"), + + /** CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES. */ + CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES("attitude type {0} and rate type {1} calls for {2} states, got {3}"), + + /** CCSDS_INCOMPATIBLE_KEYS_BOTH_USED. */ + CCSDS_INCOMPATIBLE_KEYS_BOTH_USED("incompatible keys {0} and {1} should not both be used"), + + /** CCSDS_SENSOR_INDEX_ALREADY_USED. */ + CCSDS_SENSOR_INDEX_ALREADY_USED("sensor index {0} is already used"), + + /** CCSDS_MISSING_SENSOR_INDEX. */ + CCSDS_MISSING_SENSOR_INDEX("missing sensor index {0}"), + + /** INCONSISTENT_NUMBER_OF_ELEMENTS. */ + INCONSISTENT_NUMBER_OF_ELEMENTS("inconsistent number of elements: expected {0}, got {1}"), + + /** CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES. */ + CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES("cannot estimate precession without proper derivatives"), + + /** ADDITIONAL_STATE_NAME_ALREADY_IN_USE. */ ADDITIONAL_STATE_NAME_ALREADY_IN_USE("name \"{0}\" is already used for an additional state"), + + /** NON_RESETABLE_STATE. */ NON_RESETABLE_STATE("reset state not allowed"), + + /** DSST_NEWCOMB_OPERATORS_COMPUTATION. */ DSST_NEWCOMB_OPERATORS_COMPUTATION("Cannot compute Newcomb operators for sigma > rho ({0} > {1})"), + + /** DSST_VMNS_COEFFICIENT_ERROR_MS. */ DSST_VMNS_COEFFICIENT_ERROR_MS("Cannot compute the Vmns coefficient with m > n ({0} > {1})"), + + /** DSST_SPR_SHADOW_INCONSISTENT. */ DSST_SPR_SHADOW_INCONSISTENT("inconsistent shadow computation: entry = {0} whereas exit = {1}"), - DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD( - "The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates"), - SP3_UNSUPPORTED_VERSION("unsupported sp3 file version {0}"), + + /** DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD. */ + DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD("The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates"), + + /** SP3_UNSUPPORTED_VERSION. */ + SP3_UNSUPPORTED_VERSION("unsupported sp3 file version \"{0}\""), + + /** SP3_INVALID_HEADER_ENTRY. */ + SP3_INVALID_HEADER_ENTRY("invalid header entry {0} \"{1}\" in file {2} (format version {3})"), + + /** SP3_TOO_MANY_SATELLITES_FOR_VERSION. */ + SP3_TOO_MANY_SATELLITES_FOR_VERSION("version \"{0}\" supports only up to {1} satellites, found {2} in file {3}"), + + /** SP3_NUMBER_OF_EPOCH_MISMATCH. */ SP3_NUMBER_OF_EPOCH_MISMATCH("found {0} epochs in file {1}, expected {2}"), - SP3_UNEXPECTED_END_OF_FILE("unexpected end of sp3 file (after line {0})"), + + /** SP3_INCOMPATIBLE_FILE_METADATA. */ + SP3_INCOMPATIBLE_FILE_METADATA("cannot splice sp3 files with incompatible metadata"), + + /** SP3_INCOMPATIBLE_SATELLITE_MEDATADA. */ + SP3_INCOMPATIBLE_SATELLITE_MEDATADA("cannot splice sp3 files with incompatible satellite metadata for satellite {0}"), + + /** STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM. */ + STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM("STK coordinate system \"{0}\" is invalid or not yet supported"), + + /** STK_UNMAPPED_COORDINATE_SYSTEM. */ + STK_UNMAPPED_COORDINATE_SYSTEM("STK coordinate system \"{0}\" has not been mapped to an Orekit frame"), + + /** STK_UNEXPECTED_END_OF_FILE. */ + STK_UNEXPECTED_END_OF_FILE("unexpected end of STK file (after line {0})"), + + /** CLOCK_FILE_UNSUPPORTED_VERSION. */ CLOCK_FILE_UNSUPPORTED_VERSION("unsupported clock file version {0}"), - NAVIGATION_FILE_UNSUPPORTED_VERSION("unsupported navigation messages file version {0}"), + + /** UNSUPPORTED_FILE_FORMAT_VERSION. */ + UNSUPPORTED_FILE_FORMAT_VERSION("version {0} from file {1} is not supported, supported version: {2}"), + + /** NON_EXISTENT_GEOMAGNETIC_MODEL. */ NON_EXISTENT_GEOMAGNETIC_MODEL("non-existent geomagnetic model {0} for year {1}"), - UNSUPPORTED_TIME_TRANSFORM( - "geomagnetic model {0} with epoch {1} does not support time transformation, no secular variation coefficients defined"), - OUT_OF_RANGE_TIME_TRANSFORM( - "time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}]"), - NOT_ENOUGH_DATA_FOR_INTERPOLATION("not enough data for interpolation (sample size = {0})"), + + /** UNSUPPORTED_TIME_TRANSFORM. */ + UNSUPPORTED_TIME_TRANSFORM("geomagnetic model {0} with epoch {1} does not support time transformation, no secular variation coefficients defined"), + + /** OUT_OF_RANGE_TIME_TRANSFORM. */ + OUT_OF_RANGE_TIME_TRANSFORM("time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}]"), + + /** NOT_ENOUGH_DATA. */ + NOT_ENOUGH_DATA("not enough data (sample size = {0})"), + + /** NOT_ENOUGH_CACHED_NEIGHBORS. */ NOT_ENOUGH_CACHED_NEIGHBORS("too small number of cached neighbors: {0} (must be at least {1})"), + + /** NO_CACHED_ENTRIES. */ NO_CACHED_ENTRIES("no cached entries"), + + /** NON_CHRONOLOGICALLY_SORTED_ENTRIES. */ NON_CHRONOLOGICALLY_SORTED_ENTRIES("generated entries not sorted: {0} > {1} by {2,number,0.0##############E0} s"), + + /** NO_DATA_GENERATED. */ NO_DATA_GENERATED("no data generated around date: {0}"), + + /** UNABLE_TO_GENERATE_NEW_DATA_BEFORE. */ UNABLE_TO_GENERATE_NEW_DATA_BEFORE("unable to generate new data before {0}, but data is requested for {1} which is {2,number,0.0##############E0} s before"), + + /** UNABLE_TO_GENERATE_NEW_DATA_AFTER. */ UNABLE_TO_GENERATE_NEW_DATA_AFTER("unable to generate new data after {0}, but data is requested for {1} which is {2,number,0.0##############E0} s after"), - UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY( - "unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations"), + + /** UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY. */ + UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY("unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations"), + + /** UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS. */ UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS("unable to compute mean orbit from osculating orbit after {0} iterations"), + + /** OUT_OF_RANGE_DERIVATION_ORDER. */ OUT_OF_RANGE_DERIVATION_ORDER("derivation order {0} is out of range"), + + /** ORBIT_TYPE_NOT_ALLOWED. */ ORBIT_TYPE_NOT_ALLOWED("orbit type {0} not allowed here, allowed types: {1}"), - NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_AS_REFERENCE_FOR_INERTIAL_FORCES( - "non pseudo-inertial frame {0} is not suitable as reference for inertial forces"), + + /** NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_AS_REFERENCE_FOR_INERTIAL_FORCES. */ + NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_AS_REFERENCE_FOR_INERTIAL_FORCES("non pseudo-inertial frame {0} is not suitable as reference for inertial forces"), + + /** METHOD_NOT_AVAILABLE_WITHOUT_CENTRAL_BODY. */ METHOD_NOT_AVAILABLE_WITHOUT_CENTRAL_BODY("method not available in the absence of a central body"), + + /** INCOMPATIBLE_FRAMES. */ INCOMPATIBLE_FRAMES("operation not available between frames {0} and {1}"), + + /** UNDEFINED_ORBIT. */ UNDEFINED_ORBIT("orbit not defined, state rather contains an absolute position-velocity-acceleration"), - UNDEFINED_ABSOLUTE_PVCOORDINATES( - "absolute position-velocity-acceleration not defined, state rather contains an orbit"), + + /** UNDEFINED_ABSOLUTE_PVCOORDINATES. */ + UNDEFINED_ABSOLUTE_PVCOORDINATES("absolute position-velocity-acceleration not defined, state rather contains an orbit"), + + /** INERTIAL_FORCE_MODEL_MISSING. */ INERTIAL_FORCE_MODEL_MISSING("an inertial force model has to be used when propagating in non-inertial frame {0}"), + + /** NO_SEM_ALMANAC_AVAILABLE. */ NO_SEM_ALMANAC_AVAILABLE("no SEM almanac file found"), + + /** NOT_A_SUPPORTED_SEM_ALMANAC_FILE. */ NOT_A_SUPPORTED_SEM_ALMANAC_FILE("file {0} is not a supported SEM almanac file"), + + /** NO_YUMA_ALMANAC_AVAILABLE. */ NO_YUMA_ALMANAC_AVAILABLE("no Yuma almanac file found"), + + /** NOT_A_SUPPORTED_YUMA_ALMANAC_FILE. */ NOT_A_SUPPORTED_YUMA_ALMANAC_FILE("file {0} is not a supported Yuma almanac file"), + + /** NOT_ENOUGH_GNSS_FOR_DOP. */ NOT_ENOUGH_GNSS_FOR_DOP("only {0} GNSS orbits are provided while {1} are needed to compute the DOP"), - NOT_ENOUGH_PROPAGATORS( - "Creating an aggregate propagator requires at least one constituent propagator, but none were provided."), - NOT_ENOUGH_ATTITUDE_PROVIDERS( - "Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided."), + + /** NOT_ENOUGH_PROPAGATORS. */ + NOT_ENOUGH_PROPAGATORS("Creating an aggregate propagator requires at least one constituent propagator, but none were provided."), + + /** NOT_ENOUGH_ATTITUDE_PROVIDERS. */ + NOT_ENOUGH_ATTITUDE_PROVIDERS("Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided."), + + /** NULL_ARGUMENT. */ NULL_ARGUMENT("argument {0} cannot be null"), + + /** VALUE_NOT_FOUND. */ VALUE_NOT_FOUND("value {0} not found in {1}"), + + /** KLOBUCHAR_ALPHA_BETA_NOT_LOADED. */ KLOBUCHAR_ALPHA_BETA_NOT_LOADED("Klobuchar coefficients α or β could not be loaded from {0}"), + + /** KLOBUCHAR_ALPHA_BETA_NOT_AVAILABLE_FOR_DATE. */ KLOBUCHAR_ALPHA_BETA_NOT_AVAILABLE_FOR_DATE("Klobuchar coefficients α or β not available for date {0}"), + + /** NO_KLOBUCHAR_ALPHA_BETA_IN_FILE. */ NO_KLOBUCHAR_ALPHA_BETA_IN_FILE("file {0} does not contain Klobuchar coefficients α or β"), + + /** NO_REFERENCE_DATE_FOR_PARAMETER. */ NO_REFERENCE_DATE_FOR_PARAMETER("no reference date set for parameter {0}"), + + /** STATION_NOT_FOUND. */ STATION_NOT_FOUND("station {0} not found, known stations: {1}"), + + /** UNKNOWN_SATELLITE_SYSTEM. */ UNKNOWN_SATELLITE_SYSTEM("unknown satellite system {0}"), + + /** UNKNOWN_TIME_SYSTEM. */ UNKNOWN_TIME_SYSTEM("unknown time system {0}"), + + /** UNKNOWN_UTC_ID. */ + UNKNOWN_UTC_ID("unknown UTC Id {0}"), + + /** UNKNOWN_CLOCK_DATA_TYPE. */ UNKNOWN_CLOCK_DATA_TYPE("unknown clock data type {0}"), + + /** UNKNOWN_SATELLITE_ANTENNA_CODE. */ UNKNOWN_SATELLITE_ANTENNA_CODE("unknown satellite antenna code {0}"), + + /** UNSUPPORTED_FREQUENCY_FOR_ANTENNA. */ UNSUPPORTED_FREQUENCY_FOR_ANTENNA("frequency {0} is not supported by antenna {1}"), + + /** CANNOT_FIND_SATELLITE_IN_SYSTEM. */ CANNOT_FIND_SATELLITE_IN_SYSTEM("cannot find satellite {0} in satellite system {1}"), + + /** UNKNOWN_RINEX_FREQUENCY. */ UNKNOWN_RINEX_FREQUENCY("unknown RINEX frequency {0} in file {1}, line {2}"), + + /** MISMATCHED_FREQUENCIES. */ MISMATCHED_FREQUENCIES("mismatched frequencies in file {0}, line {1} (expected {2}, got {3})"), + + /** WRONG_PARSING_TYPE. */ + WRONG_PARSING_TYPE("wrong parsing type for file {0}"), + + /** WRONG_COLUMNS_NUMBER. */ WRONG_COLUMNS_NUMBER("wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns)"), + + /** UNSUPPORTED_FILE_FORMAT. */ UNSUPPORTED_FILE_FORMAT("unsupported format for file {0}"), + + /** INCOMPLETE_HEADER. */ INCOMPLETE_HEADER("incomplete header in file {0}"), - INCONSISTENT_NUMBER_OF_SATS( - "inconsistent number of satellites in line {0}, file {1}: observation with {2} satellites and number of max satellites is {3}"), - INCONSISTENT_SATELLITE_SYSTEM( - "the satellite system {3} from line {0}, file {1} is not consistent with the Rinex Satellite System {2} in header"), + + /** INCONSISTENT_NUMBER_OF_SATS. */ + INCONSISTENT_NUMBER_OF_SATS("inconsistent number of satellites in line {0}, file {1}: observation with {2} satellites and number of max satellites is {3}"), + + /** INCONSISTENT_SATELLITE_SYSTEM. */ + INCONSISTENT_SATELLITE_SYSTEM("the satellite system {3} from line {0}, file {1} is not consistent with the Rinex Satellite System {2} in header"), + + /** NO_PROPAGATOR_CONFIGURED. */ NO_PROPAGATOR_CONFIGURED("no propagator configured"), + + /** DIMENSION_INCONSISTENT_WITH_PARAMETERS. */ DIMENSION_INCONSISTENT_WITH_PARAMETERS("dimension {0} is inconsistent with parameters list: {1}"), + + /** NOT_A_SUPPORTED_UNIX_COMPRESSED_FILE. */ NOT_A_SUPPORTED_UNIX_COMPRESSED_FILE("file {0} is not a supported Unix-compressed file"), + + /** UNEXPECTED_END_OF_FILE. */ UNEXPECTED_END_OF_FILE("unexpected end of file {0}"), + + /** CORRUPTED_FILE. */ CORRUPTED_FILE("file {0} is corrupted"), + + /** VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_LOADED. */ VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_LOADED("Vienna coefficients ah or aw or zh or zw could not be loaded from {0}"), - VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_AVAILABLE_FOR_DATE( - "Vienna coefficients ah or aw or zh or zw not available for date {0}"), + + /** VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_AVAILABLE_FOR_DATE. */ + VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_AVAILABLE_FOR_DATE("Vienna coefficients ah or aw or zh or zw not available for date {0}"), + + /** NO_VIENNA_ACOEF_OR_ZENITH_DELAY_IN_FILE. */ NO_VIENNA_ACOEF_OR_ZENITH_DELAY_IN_FILE("file {0} does not contain Vienna coefficients ah, aw, zh or zw"), + + /** IRREGULAR_OR_INCOMPLETE_GRID. */ IRREGULAR_OR_INCOMPLETE_GRID("irregular or incomplete grid in file {0}"), + + /** INVALID_SATELLITE_SYSTEM. */ INVALID_SATELLITE_SYSTEM("invalid satellite system {0}"), - NO_TEC_DATA_IN_FILE_FOR_DATE("IONEX file {0} does not contain TEC data for date {1}"), + + /** NO_TEC_DATA_IN_FILES_FOR_DATE. */ + NO_TEC_DATA_IN_FILES_FOR_DATE("IONEX files {0} does not contain TEC data for date {1}"), + + /** INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE. */ INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE("number of maps {0} is inconsistent with header specification: {1}"), - NO_LATITUDE_LONGITUDE_BONDARIES_IN_IONEX_HEADER( - "file {0} does not contain latitude or longitude bondaries in its header section"), + + /** NO_LATITUDE_LONGITUDE_BONDARIES_IN_IONEX_HEADER. */ + NO_LATITUDE_LONGITUDE_BONDARIES_IN_IONEX_HEADER("file {0} does not contain latitude or longitude bondaries in its header section"), + + /** NO_EPOCH_IN_IONEX_HEADER. */ NO_EPOCH_IN_IONEX_HEADER("file {0} does not contain epoch of first or last map in its header section"), - ITRF_VERSIONS_PREFIX_ONLY("The first column of itrf-versions.conf is a plain " + - "prefix that is matched against the name of each loaded file. It should " + - "not contain any regular expression syntax or directory components, i.e. " + - "\"/\" or \"\\\". Actual value: \"{0}\"."), - CANNOT_COMPUTE_AIMING_AT_SINGULAR_POINT( - "cannot compute aiming direction at singular point: latitude = {0}, longitude = {1}"), + + /** ITRF_VERSIONS_PREFIX_ONLY. */ + ITRF_VERSIONS_PREFIX_ONLY("The first column of itrf-versions.conf is a plain prefix that is matched against the name of each loaded file. It should not contain any regular expression syntax or directory components, i.e. \"/\" or \"\\\". Actual value: \"{0}\"."), + + /** CANNOT_COMPUTE_AIMING_AT_SINGULAR_POINT. */ + CANNOT_COMPUTE_AIMING_AT_SINGULAR_POINT("cannot compute aiming direction at singular point: latitude = {0}, longitude = {1}"), + + /** STEC_INTEGRATION_DID_NOT_CONVERGE. */ STEC_INTEGRATION_DID_NOT_CONVERGE("STEC integration did not converge"), + + /** MODIP_GRID_NOT_LOADED. */ MODIP_GRID_NOT_LOADED("MODIP grid not be loaded from {0}"), + + /** NEQUICK_F2_FM3_NOT_LOADED. */ NEQUICK_F2_FM3_NOT_LOADED("NeQuick coefficient f2 or fm3 not be loaded from {0}"), + + /** NOT_A_SUPPORTED_HATANAKA_COMPRESSED_FILE. */ NOT_A_SUPPORTED_HATANAKA_COMPRESSED_FILE("file {0} is not a supported Hatanaka-compressed file"), + + /** CANNOT_COMPUTE_LAGRANGIAN. */ CANNOT_COMPUTE_LAGRANGIAN("Cannot compute around {0}"), + + /** TRAJECTORY_NOT_CROSSING_XZPLANE. */ TRAJECTORY_NOT_CROSSING_XZPLANE("The trajectory does not cross XZ Plane, it will not result in a Halo Orbit"), - MULTIPLE_SHOOTING_UNDERCONSTRAINED( - "The multiple shooting problem is underconstrained : {0} free variables, {1} constraints"), - INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS( - "invalid measurement types {0} and {1} for the combination of measurements {2}"), - INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS( - "frequencies {0} and {1} are incompatibles for the {2} combination"), + + /** MULTIPLE_SHOOTING_UNDERCONSTRAINED. */ + MULTIPLE_SHOOTING_UNDERCONSTRAINED("The multiple shooting problem is underconstrained : {0} free variables, {1} constraints"), + + /** INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS. */ + INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS("invalid measurement types {0} and {1} for the combination of measurements {2}"), + + /** INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS. */ + INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS("frequencies {0} and {1} are incompatibles for the {2} combination"), + + /** NON_CHRONOLOGICAL_DATES_FOR_OBSERVATIONS. */ NON_CHRONOLOGICAL_DATES_FOR_OBSERVATIONS("observations are not in chronological order: {0} is {2,number,0.0##############E0} s after {1}"), - EXCEPTIONAL_DATA_CONTEXT( - "Use of the ExceptionalDataContext detected. This is typically used to detect developer errors."), - NON_DIFFERENT_DATES_FOR_OBSERVATIONS( - "Observations must have different dates: {0}, {1} ({3,number,0.0##############E0} s from first observation), and {2} ({4,number,0.0##############E0} s from first observation, {5,number,0.0##############E0} s from second observation)"), + + /** INCONSISTENT_SAMPLING_DATE. */ + INCONSISTENT_SAMPLING_DATE("inconsistent sampling date: expected {0} but got {1}"), + + /** EXCEPTIONAL_DATA_CONTEXT. */ + EXCEPTIONAL_DATA_CONTEXT("Use of the ExceptionalDataContext detected. This is typically used to detect developer errors."), + + /** NON_DIFFERENT_DATES_FOR_OBSERVATIONS. */ + NON_DIFFERENT_DATES_FOR_OBSERVATIONS("Observations must have different dates: {0}, {1} ({3,number,0.0##############E0} s from first observation), and {2} ({4,number,0.0##############E0} s from first observation, {5,number,0.0##############E0} s from second observation)"), + + /** NON_COPLANAR_POINTS. */ NON_COPLANAR_POINTS("observations are not in the same plane"), + + /** INVALID_PARAMETER_RANGE. */ INVALID_PARAMETER_RANGE("invalid parameter {0}: {1} not in range [{2}, {3}]"), + + /** PARAMETER_NOT_SET. */ PARAMETER_NOT_SET("The parameter {0} should not be null in {1}"), + + /** FUNCTION_NOT_IMPLEMENTED. */ FUNCTION_NOT_IMPLEMENTED("{0} is not implemented"), + + /** INVALID_TYPE_FOR_FUNCTION. */ INVALID_TYPE_FOR_FUNCTION("Impossible to execute {0} with {1} set to {2}"), + + /** NO_DATA_IN_FILE. */ NO_DATA_IN_FILE("No data could be parsed from file {0}"), + + /** CPF_UNEXPECTED_END_OF_FILE. */ CPF_UNEXPECTED_END_OF_FILE("Unexpected end of CPF file (after line {0})"), + + /** UNEXPECTED_FORMAT_FOR_ILRS_FILE. */ UNEXPECTED_FORMAT_FOR_ILRS_FILE("Unexpected file format. Must be {0} but is {1}"), + + /** CRD_UNEXPECTED_END_OF_FILE. */ CRD_UNEXPECTED_END_OF_FILE("Unexpected end of CRD file (after line {0})"), + + /** INVALID_RANGE_INDICATOR_IN_CRD_FILE. */ INVALID_RANGE_INDICATOR_IN_CRD_FILE("Invalid range indicator {0} in CRD file header"), + + /** END_OF_ENCODED_MESSAGE. */ END_OF_ENCODED_MESSAGE("end of encoded message reached"), + + /** TOO_LARGE_DATA_TYPE. */ TOO_LARGE_DATA_TYPE("too large data type ({0} bits)"), + + /** UNKNOWN_ENCODED_MESSAGE_NUMBER. */ UNKNOWN_ENCODED_MESSAGE_NUMBER("unknown encoded message number {0}"), + + /** UNKNOWN_AUTHENTICATION_METHOD. */ UNKNOWN_AUTHENTICATION_METHOD("unknown authentication method: {0}"), + + /** UNKNOWN_CARRIER_PHASE_CODE. */ UNKNOWN_CARRIER_PHASE_CODE("unknown carrier phase code: {0}"), + + /** UNKNOWN_DATA_FORMAT. */ UNKNOWN_DATA_FORMAT("unknown data format: {0}"), + + /** UNKNOWN_NAVIGATION_SYSTEM. */ UNKNOWN_NAVIGATION_SYSTEM("unknown navigation system: {0}"), + + /** STREAM_REQUIRES_NMEA_FIX. */ STREAM_REQUIRES_NMEA_FIX("data stream {0} requires a NMEA fix data"), + + /** FAILED_AUTHENTICATION. */ FAILED_AUTHENTICATION("failed authentication for mountpoint {0}"), + + /** CONNECTION_ERROR. */ CONNECTION_ERROR("error connecting to {0}: {1}"), + + /** UNEXPECTED_CONTENT_TYPE. */ UNEXPECTED_CONTENT_TYPE("unexpected content type {0}"), + + /** CANNOT_PARSE_GNSS_DATA. */ CANNOT_PARSE_GNSS_DATA("cannot parse GNSS data from {0}"), + + /** INVALID_GNSS_DATA. */ + INVALID_GNSS_DATA("invalid GNSS data: {0}"), + + /** GNSS_PARITY_ERROR. */ + GNSS_PARITY_ERROR("GNSS parity error on word {0}"), + + /** UNKNOWN_HOST. */ UNKNOWN_HOST("unknown host {0}"), + + /** SOURCETABLE_PARSE_ERROR. */ SOURCETABLE_PARSE_ERROR("error parsing sourcetable line {0} from {1}: {2}"), + + /** CANNOT_PARSE_SOURCETABLE. */ CANNOT_PARSE_SOURCETABLE("cannot parse sourcetable from {0}"), + + /** MOUNPOINT_ALREADY_CONNECTED. */ MOUNPOINT_ALREADY_CONNECTED("mount point {0} is already connected"), + + /** MISSING_HEADER. */ MISSING_HEADER("missing header from {0}: {1}"), + + /** NOT_VALID_INTERNATIONAL_DESIGNATOR. */ NOT_VALID_INTERNATIONAL_DESIGNATOR("{0} is not a valid international designator"), + + /** UNINITIALIZED_VALUE_FOR_KEY. */ UNINITIALIZED_VALUE_FOR_KEY("value for key {0} has not been initialized"), + + /** UNKNOWN_UNIT. */ UNKNOWN_UNIT("unknown unit {0}"), + + /** INCOMPATIBLE_UNITS. */ INCOMPATIBLE_UNITS("units {0} and {1} are not compatible"), + + /** MISSING_VELOCITY. */ MISSING_VELOCITY("missing velocity data"), + + /** ATTEMPT_TO_GENERATE_MALFORMED_FILE. */ ATTEMPT_TO_GENERATE_MALFORMED_FILE("attempt to generate file {0} with a formatting error"), - FIND_ROOT( - "{0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0})"), - BACKWARD_PROPAGATION_NOT_ALLOWED("backward propagation not allowed here"), - NO_STATION_ECCENTRICITY_FOR_EPOCH( - "no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2}"), + + /** FIND_ROOT. */ + FIND_ROOT("{0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0})"), + + /** MISSING_STATION_DATA_FOR_EPOCH. */ + MISSING_STATION_DATA_FOR_EPOCH("missing station data for epoch {0}"), + + /** INCONSISTENT_SELECTION. */ INCONSISTENT_SELECTION("inconsistent parameters selection between pairs {0}/{1} and {2}/{3}"), + + /** NO_UNSCENTED_TRANSFORM_CONFIGURED. */ NO_UNSCENTED_TRANSFORM_CONFIGURED("no unscented transform configured"), + + /** NOT_STRICTLY_POSITIVE. */ NOT_STRICTLY_POSITIVE("value is not strictly positive: {0}"), + + /** UNSUPPORTED_TRANSFORM. */ UNSUPPORTED_TRANSFORM("transform from {0} to {1} is not implemented"), + + /** WRONG_ORBIT_PARAMETERS_TYPE. */ WRONG_ORBIT_PARAMETERS_TYPE("orbital parameters type: {0} is different from expected orbital type : {1}"), + + /** WRONG_NB_COMPONENTS. */ + WRONG_NB_COMPONENTS("{0} expects {1} elements, got {2}"), + + /** CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF. */ CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF("cannot change covariance type if defined in a local orbital frame"), - CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME("cannot change covariance type if defined in a non pseudo-inertial reference frame"); - // CHECKSTYLE: resume JavadocVariable check + + /** CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME. */ + CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME("cannot change covariance type if defined in a non pseudo-inertial reference frame"), + + /** DIFFERENT_TIME_OF_CLOSEST_APPROACH. */ + DIFFERENT_TIME_OF_CLOSEST_APPROACH("primary collision object time of closest approach is different from the secondary collision object's one"), + + /** DATES_MISMATCH. */ + DATES_MISMATCH("first date {0} does not match second date {1}"), + + /** ORBITS_MUS_MISMATCH. */ + ORBITS_MUS_MISMATCH("first orbit mu {0} does not match second orbit mu {1}"), + + /** DIFFERENT_STATE_DEFINITION. */ + DIFFERENT_STATE_DEFINITION("one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration"), + + /** STATE_AND_COVARIANCE_DATES_MISMATCH. */ + STATE_AND_COVARIANCE_DATES_MISMATCH("state date {0} does not match its covariance date {1}"), + + /** NO_INTERPOLATOR_FOR_STATE_DEFINITION. */ + NO_INTERPOLATOR_FOR_STATE_DEFINITION("creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator"), + + /** WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION. */ + WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION("wrong interpolator defined for this spacecraft state type (orbit or absolute PV)"), + + /** MULTIPLE_INTERPOLATOR_USED. */ + MULTIPLE_INTERPOLATOR_USED("multiple interpolators are used so they may use different numbers of interpolation points"), + + /** HEADER_NOT_WRITTEN. */ + HEADER_NOT_WRITTEN("header for file {0} has not been written yet"), + + /** HEADER_ALREADY_WRITTEN. */ + HEADER_ALREADY_WRITTEN("header for file {0} has already been written"), + + /** CANNOT_START_PROPAGATION_FROM_INFINITY. */ + CANNOT_START_PROPAGATION_FROM_INFINITY("Cannot start the propagation from an infinitely far date"), + + /** INVALID_SATELLITE_ID. */ + INVALID_SATELLITE_ID("invalid satellite id {0}"), + + /** WRONG_EOP_INTERPOLATION_DEGREE. */ + WRONG_EOP_INTERPOLATION_DEGREE("EOP interpolation degree must be of the form 4k-1, got {0}"); /** Base name of the resource bundle in classpath. */ private static final String RESOURCE_BASE_NAME = "assets/org/orekit/localization/OrekitMessages"; @@ -399,6 +967,17 @@ public String getLocalizedString(final Locale locale) { */ public static class UTF8Control extends ResourceBundle.Control { + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public UTF8Control() { + // nothing to do + } + /** {@inheritDoc} */ @Override public ResourceBundle newBundle(final String baseName, final Locale locale, final String format, diff --git a/src/main/java/org/orekit/errors/OrekitParseException.java b/src/main/java/org/orekit/errors/OrekitParseException.java index 3d4105d00f..4c99efd93d 100644 --- a/src/main/java/org/orekit/errors/OrekitParseException.java +++ b/src/main/java/org/orekit/errors/OrekitParseException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/TimeStampedCacheException.java b/src/main/java/org/orekit/errors/TimeStampedCacheException.java index 87433d551e..ee000fbe6f 100644 --- a/src/main/java/org/orekit/errors/TimeStampedCacheException.java +++ b/src/main/java/org/orekit/errors/TimeStampedCacheException.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/errors/UnsupportedParameterException.java b/src/main/java/org/orekit/errors/UnsupportedParameterException.java new file mode 100644 index 0000000000..1eec49ac53 --- /dev/null +++ b/src/main/java/org/orekit/errors/UnsupportedParameterException.java @@ -0,0 +1,68 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.errors; + +import java.util.List; + +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; + +/** Exception for unsupported {@link ParameterDriver} in a model implementing {@link ParameterDriversProvider}. + * + * @author Maxime Journot + * @author Luc Maisonobe + * @since 12.0 + */ +public class UnsupportedParameterException extends OrekitException { + + /** String for empty parameter drivers' list. */ + public static final String NO_PARAMETER = ""; + + /** Comma separator for printing list of supported parameter drivers. */ + public static final String COMMA_SEP = ", "; + + /** Serializable UID. */ + private static final long serialVersionUID = -1363569710782876135L; + + /** Constructor. + * + * @param parameterName name of the parameter driver that is not supported by the model + * @param parameterDrivers list of the model's parameter drivers + */ + public UnsupportedParameterException(final String parameterName, final List parameterDrivers) { + super(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, parameterName, getSupportedNames(parameterDrivers)); + } + + /** Builder for the supported parameters' names. + * + * @param parameterDrivers list of model parameter drivers + * @return supported parameter names as a String + */ + private static String getSupportedNames(final List parameterDrivers) { + final StringBuilder builder = new StringBuilder(); + for (final ParameterDriver driver : parameterDrivers) { + if (builder.length() > 0) { + builder.append(COMMA_SEP); + } + builder.append(driver.getName()); + } + if (builder.length() == 0) { + builder.append(NO_PARAMETER); + } + return builder.toString(); + } +} diff --git a/src/main/java/org/orekit/errors/package-info.java b/src/main/java/org/orekit/errors/package-info.java index 0b8fa08bb0..b12b09dd92 100644 --- a/src/main/java/org/orekit/errors/package-info.java +++ b/src/main/java/org/orekit/errors/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/iod/IodGauss.java b/src/main/java/org/orekit/estimation/iod/IodGauss.java new file mode 100644 index 0000000000..1e65fd2f42 --- /dev/null +++ b/src/main/java/org/orekit/estimation/iod/IodGauss.java @@ -0,0 +1,232 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.iod; + +import org.hipparchus.analysis.solvers.LaguerreSolver; +import org.hipparchus.complex.Complex; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.Array2DRowRealMatrix; +import org.hipparchus.linear.LUDecomposition; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.linear.RealVector; +import org.hipparchus.util.FastMath; +import org.orekit.estimation.measurements.AngularAzEl; +import org.orekit.estimation.measurements.AngularRaDec; +import org.orekit.frames.Frame; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinates; + +/** + * Gauss angles-only Initial Orbit Determination (IOD) algorithm. + *

    + * The algorithm works best when the separation between observation is less than about 60°. + * The method performs remarkably well when the data is separated by 10° or less. + * + * An orbit is determined from three lines of sight w.r.t. their respective observers + * inertial positions vectors. + *

    + * References: + * Vallado, D., Fundamentals of Astrodynamics and Applications + * Curtis, Orbital Mechanics for Engineering Students + *

    + * @author Julien Asquier + * @since 12.0 + */ +public class IodGauss { + + /** Gravitational constant. */ + private final double mu; + + /** + * Constructor. + * + * @param mu gravitational constant + */ + public IodGauss(final double mu) { + this.mu = mu; + } + + /** + * Estimate and orbit based on Gauss Intial Orbit Determination method. + * + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param azEl1 first angular observation + * @param azEl2 second angular observation + * @param azEl3 third angular observation + * @return an estimate of the orbit at the central date or null if + * no estimate is possible with the given data + */ + public Orbit estimate(final Frame outputFrame, final AngularAzEl azEl1, + final AngularAzEl azEl2, final AngularAzEl azEl3) { + return estimate(outputFrame, + azEl1.getGroundStationPosition(outputFrame), azEl1.getDate(), azEl1.getObservedLineOfSight(outputFrame), + azEl2.getGroundStationPosition(outputFrame), azEl2.getDate(), azEl2.getObservedLineOfSight(outputFrame), + azEl3.getGroundStationPosition(outputFrame), azEl3.getDate(), azEl3.getObservedLineOfSight(outputFrame)); + } + + /** + * Estimate and orbit based on Gauss Intial Orbit Determination method. + * + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param raDec1 first angular observation + * @param raDec2 second angular observation + * @param raDec3 third angular observation + * @return an estimate of the orbit at the central date or null if + * no estimate is possible with the given data + */ + public Orbit estimate(final Frame outputFrame, final AngularRaDec raDec1, + final AngularRaDec raDec2, final AngularRaDec raDec3) { + return estimate(outputFrame, + raDec1.getGroundStationPosition(outputFrame), raDec1.getDate(), raDec1.getObservedLineOfSight(outputFrame), + raDec2.getGroundStationPosition(outputFrame), raDec2.getDate(), raDec2.getObservedLineOfSight(outputFrame), + raDec3.getGroundStationPosition(outputFrame), raDec3.getDate(), raDec3.getObservedLineOfSight(outputFrame)); + } + + /** + * Estimate and orbit based on Gauss Intial Orbit Determination method. + * + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param obsP1 observer position at obsDate1 + * @param obsDate1 date of the 1st observation + * @param los1 line of sight unit vector at obsDate1 + * @param obsP2 observer position at obsDate2 + * @param obsDate2 date of the 2nd observation + * @param los2 line of sight unit vector at obsDate2 + * @param obsP3 observer position at obsDate3 + * @param obsDate3 date of the 3rd observation + * @param los3 line of sight unit vector at obsDate3 + * @return an estimate of the orbit at the central date obsDate2 or null if + * no estimate is possible with the given data + */ + public Orbit estimate(final Frame outputFrame, + final Vector3D obsP1, final AbsoluteDate obsDate1, final Vector3D los1, + final Vector3D obsP2, final AbsoluteDate obsDate2, final Vector3D los2, + final Vector3D obsP3, final AbsoluteDate obsDate3, final Vector3D los3) { + + // Getting the difference of time between 1st and 3rd observation with 2nd observation + final double tau1 = obsDate1.getDate().durationFrom(obsDate2.getDate()); + final double tau3 = obsDate3.getDate().durationFrom(obsDate2.getDate()); + + final double diffTau3Tau1 = tau3 - tau1; + + // mathematical coefficients, see Vallado 7.3.2 + final double a1 = tau3 / diffTau3Tau1; + final double a3 = -tau1 / diffTau3Tau1; + final double a1u = tau3 * ((diffTau3Tau1 * diffTau3Tau1) - tau3 * tau3) / (6. * diffTau3Tau1); + final double a3u = -tau1 * ((diffTau3Tau1 * diffTau3Tau1) - tau1 * tau1) / (6. * diffTau3Tau1); + + // Creating the line of Sight Matrix and inverting it + final RealMatrix losMatrix = new Array2DRowRealMatrix(3, 3); + losMatrix.setColumn(0, los1.toArray()); + losMatrix.setColumn(1, los2.toArray()); + losMatrix.setColumn(2, los3.toArray()); + + final RealMatrix invertedLosMatrix = new LUDecomposition(losMatrix).getSolver().getInverse(); + + // Creating the position of observations matrix + final RealMatrix rSite = new Array2DRowRealMatrix(3, 3); + rSite.setColumn(0, obsP1.toArray()); + rSite.setColumn(1, obsP2.toArray()); + rSite.setColumn(2, obsP3.toArray()); + + // mathematical matrix and coefficients, see Vallado 7.3.2 + final RealMatrix m = invertedLosMatrix.multiply(rSite); + + final double d1 = m.getEntry(1, 0) * a1 - m.getEntry(1, 1) + m.getEntry(1, 2) * a3; + final double d2 = m.getEntry(1, 0) * a1u + m.getEntry(1, 2) * a3u; + final double C = los2.dotProduct(obsP2); + + // norm of the position of the second observation + final double r2Norm = obsP2.getNorm(); + + // Coefficients of the 8th order polynomial we need to solve to determine "r" + final double[] coeff = new double[] { -mu * mu * d2 * d2, 0., 0., -2. * mu * (C * d2 + d1 * d2), 0., + 0., -(d1 * d1 + 2. * C * d1 + r2Norm * r2Norm), 0., 1.0 }; + final LaguerreSolver solver = new LaguerreSolver(1E-10, + 1E-10, 1E-10); + final Complex[] roots = solver.solveAllComplex(coeff, 5. * r2Norm); + + // Looking for the first adequate root of the equation (7-16) of Vallado + double r2Mag = 0.0; + for (final Complex root : roots) { + if (root.getReal() > r2Mag && + FastMath.abs(root.getImaginary()) < solver.getAbsoluteAccuracy()) { + r2Mag = root.getReal(); + } + } + if (r2Mag == 0.0) { + return null; + } + + // mathematical matrix and coefficients, see Vallado 7.3.2 + final double u = mu / (r2Mag * r2Mag * r2Mag); + + final double c1 = -(-a1 - a1u * u); + final double c2 = -1; + final double c3 = -(-a3 - a3u * u); + + final RealMatrix cCoeffMatrix = new Array2DRowRealMatrix(3, 1); + cCoeffMatrix.setEntry(0, 0, -c1); + cCoeffMatrix.setEntry(1, 0, -c2); + cCoeffMatrix.setEntry(2, 0, -c3); + + final RealMatrix B = m.multiply(cCoeffMatrix.scalarMultiply(1)); + + final RealMatrix A = new Array2DRowRealMatrix(3, 3); + A.setEntry(0, 0, c1); + A.setEntry(1, 1, c2); + A.setEntry(2, 2, c3); + + // Slant ranges matrix + final RealMatrix slantRanges = new LUDecomposition(A).getSolver().solve(B); + + // Position Matrix of the satellite corresponding to the 1st, 2nd and 3rd observation + final RealMatrix posMatrix = new Array2DRowRealMatrix(3, 3); + for (int i = 0; i <= 2; i++) { + final RealVector position = losMatrix.getColumnVector(i). + mapMultiply(slantRanges.getEntry(i, 0)).add(rSite.getColumnVector(i)); + posMatrix.setRowVector(i, position); + } + // At this point, the proper Gauss Initial Orbit determination is ending because we have the 3 positions of the + // satellite from the 3 observations. However, we could also calculate the velocity using the next f and g + // coefficients, see Vallado 7.3.2 and Vallado 2.3.1 for more details + final double pos2Norm = posMatrix.getRowVector(1).getNorm(); + final double pos2NormCubed = pos2Norm * pos2Norm * pos2Norm; + + // mathematical matrix and coefficients, see Curtis algorithms 5.5 and 5.6. It is still the IOD GAUSS + final double f1 = 1. - (0.5 * mu * tau1 * tau1 / pos2NormCubed); + final double f3 = 1. - (0.5 * mu * tau3 * tau3 / pos2NormCubed); + final double g1 = tau1 - ((1. / 6.) * mu * tau1 * tau1 * tau1 / pos2NormCubed); + final double g3 = tau3 - ((1. / 6.) * mu * tau3 * tau3 * tau3 / pos2NormCubed); + + final double v2EquationCoeff = 1. / (f1 * g3 - f3 * g1); + // velocity at the central position of the satellite, corresponding to the second observation + final RealVector v2 = (posMatrix.getRowVector(0).mapMultiply(-f3).add( + posMatrix.getRowVector(2).mapMultiply(f1))).mapMultiply(v2EquationCoeff); + + // position at the central position of the satellite, corresponding to the second observation + final RealVector p2 = posMatrix.getRowVector(1); + + // We can finally build the Orekit Object, PVCoordinates and Orbit from p2 and v2 + final Vector3D p2Vector3D = new Vector3D(p2.toArray()); + final Vector3D v2Vector3D = new Vector3D(v2.toArray()); + return new CartesianOrbit(new PVCoordinates(p2Vector3D, v2Vector3D), outputFrame, obsDate2, mu); + } + +} diff --git a/src/main/java/org/orekit/estimation/iod/IodGibbs.java b/src/main/java/org/orekit/estimation/iod/IodGibbs.java index d7692597b0..4d4404da3a 100644 --- a/src/main/java/org/orekit/estimation/iod/IodGibbs.java +++ b/src/main/java/org/orekit/estimation/iod/IodGibbs.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,29 +23,32 @@ import org.orekit.estimation.measurements.PV; import org.orekit.estimation.measurements.Position; import org.orekit.frames.Frame; -import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.CartesianOrbit; import org.orekit.time.AbsoluteDate; import org.orekit.utils.PVCoordinates; /** - * Gibbs initial orbit determination. - * An orbit is determined from three position vectors. + * Gibbs position-based Initial Orbit Determination (IOD) algorithm. + *

    + * An orbit is determined from three position vectors. This method requires + * the vectors to be coplanar. Orekit uses a {@link IodGibbs#COPLANAR_THRESHOLD + * default coplanar threshold of 5°}. * * Reference: * Vallado, D., Fundamentals of Astrodynamics and Applications - * + *

    * @author Joris Olympio * @since 8.0 - * */ public class IodGibbs { - /** Threshold for checking coplanr vectors. */ - private static final double COPLANAR_THRESHOLD = FastMath.toRadians(5.); - - /** gravitationnal constant. */ + /** Gravitational constant. **/ private final double mu; + /** Threshold for checking coplanar vectors. */ + private final double COPLANAR_THRESHOLD = FastMath.toRadians(5.); + /** Creator. * * @param mu gravitational constant @@ -65,7 +68,7 @@ public IodGibbs(final double mu) { * (i.e., date of the second position measurement) * @since 11.0 */ - public KeplerianOrbit estimate(final Frame frame, final Position p1, final Position p2, final Position p3) { + public Orbit estimate(final Frame frame, final Position p1, final Position p2, final Position p3) { return estimate(frame, p1.getPosition(), p1.getDate(), p2.getPosition(), p2.getDate(), @@ -82,7 +85,7 @@ public KeplerianOrbit estimate(final Frame frame, final Position p1, final Posit * @return an initial orbit estimation at the central date * (i.e., date of the second PV measurement) */ - public KeplerianOrbit estimate(final Frame frame, final PV pv1, final PV pv2, final PV pv3) { + public Orbit estimate(final Frame frame, final PV pv1, final PV pv2, final PV pv3) { return estimate(frame, pv1.getPosition(), pv1.getDate(), pv2.getPosition(), pv2.getDate(), @@ -102,10 +105,10 @@ public KeplerianOrbit estimate(final Frame frame, final PV pv1, final PV pv2, fi * @return an initial orbit estimation at the central date * (i.e., date of the second position measurement) */ - public KeplerianOrbit estimate(final Frame frame, - final Vector3D r1, final AbsoluteDate date1, - final Vector3D r2, final AbsoluteDate date2, - final Vector3D r3, final AbsoluteDate date3) { + public Orbit estimate(final Frame frame, + final Vector3D r1, final AbsoluteDate date1, + final Vector3D r2, final AbsoluteDate date2, + final Vector3D r3, final AbsoluteDate date3) { // Checks measures are not at the same date if (date1.equals(date2) || date1.equals(date3) || date2.equals(date3)) { throw new OrekitException(OrekitMessages.NON_DIFFERENT_DATES_FOR_OBSERVATIONS, date1, date2, date3, @@ -132,26 +135,25 @@ public KeplerianOrbit estimate(final Frame frame, .add(r3.scalarMultiply(r1.getNorm() - r2.getNorm()))); // middle velocity - final double vm = FastMath.sqrt(mu / (N.getNorm() * D.getNorm())); + final double vm = FastMath.sqrt(mu / (N.getNorm() * D.getNorm())); final Vector3D vlEci = B.scalarMultiply(vm / r2.getNorm()).add(S.scalarMultiply(vm)); // compile a new middle point with position, velocity - final PVCoordinates pv = new PVCoordinates(r2, vlEci); - final AbsoluteDate date = date2; + final PVCoordinates pv = new PVCoordinates(r2, vlEci); - // compute the equivalent Keplerian orbit - final KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, date, mu); + // compute the equivalent Cartesian orbit + final CartesianOrbit orbit = new CartesianOrbit(pv, frame, date2, mu); //define the reverse orbit final PVCoordinates pv2 = new PVCoordinates(r2, vlEci.scalarMultiply(-1)); - final KeplerianOrbit orbit2 = new KeplerianOrbit(pv2, frame, date, mu); + final CartesianOrbit orbit2 = new CartesianOrbit(pv2, frame, date2, mu); //check which orbit is correct final Vector3D estP3 = orbit.shiftedBy(date3.durationFrom(date2)). - getPVCoordinates().getPosition(); + getPosition(); final double dist = estP3.subtract(r3).getNorm(); final Vector3D estP3_2 = orbit2.shiftedBy(date3.durationFrom(date2)). - getPVCoordinates().getPosition(); + getPosition(); final double dist2 = estP3_2.subtract(r3).getNorm(); if (dist <= dist2) { diff --git a/src/main/java/org/orekit/estimation/iod/IodGooding.java b/src/main/java/org/orekit/estimation/iod/IodGooding.java index c0c48e5e4f..f4c3b7aa74 100644 --- a/src/main/java/org/orekit/estimation/iod/IodGooding.java +++ b/src/main/java/org/orekit/estimation/iod/IodGooding.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,20 +18,24 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.orekit.estimation.measurements.AngularAzEl; import org.orekit.estimation.measurements.AngularRaDec; import org.orekit.frames.Frame; -import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; import org.orekit.time.AbsoluteDate; import org.orekit.utils.PVCoordinates; /** - * Gooding angles only initial orbit determination, assuming Keplerian motion. - * An orbit is determined from three angular observations. + * Gooding angles only Initial Orbit Determination (IOD) algorithm, assuming Keplerian motion. + *

    + * An orbit is determined from three lines of sight w.r.t. their respective observers + * inertial positions vectors. Gooding algorithm can handle multiple satellite's revolutions. * * Reference: * Gooding, R.H., A New Procedure for Orbit Determination Based on Three Lines of Sight (Angles only), * Technical Report 93004, April 1993 - * + *

    * @author Joris Olympio * @since 8.0 */ @@ -70,9 +74,11 @@ public class IodGooding { /** Range of point 1 (O1-R1). */ private double rho1; - /** Range of point 2 (O2-R2). */ + + /** Range of point 2 (O1-R1). */ private double rho2; - /** Range of point 3 (O3-R3). */ + + /** Range of point 1 (O1-R1). */ private double rho3; /** working variable. */ @@ -87,46 +93,69 @@ public class IodGooding { /** Simple Lambert's problem solver. */ private IodLambert lambert; - /** Creator. - * @param mu gravitational constant + /** + * Constructor. + * + * @param mu gravitational constant */ public IodGooding(final double mu) { - this.mu = mu; + this.mu = mu; this.rho1 = 0; this.rho2 = 0; this.rho3 = 0; } - /** Get the range for observation (1). + /** Get range for observation (1). * - * @return the range for observation (1). + * @return the range for observation (1) */ public double getRange1() { return rho1 * R; } - /** Get the range for observation (2). + /** Get range for observation (2). * - * @return the range for observation (2). + * @return the range for observation (2) */ public double getRange2() { return rho2 * R; } - /** Get the range for observation (3). + /** Get range for observation (3). * - * @return the range for observation (3). + * @return the range for observation (3) */ public double getRange3() { return rho3 * R; } - /** Orbit got from three angular observations. - * @param frame inertial frame for observer coordinates and orbit estimate - * @param raDec1 first angular observation - * @param raDec2 second angular observation - * @param raDec3 third angular observation + /** Estimate orbit from three angular observations. + *

    + * This signature assumes there was less than an half revolution between start and final date + *

    + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param azEl1 first angular observation + * @param azEl2 second angular observation + * @param azEl3 third angular observation + * @param rho1init initial guess of the range problem. range 1, in meters + * @param rho3init initial guess of the range problem. range 3, in meters + * @return an estimate of the Keplerian orbit at the central date + * (i.e., date of the second angular observation) + * @since 12.0 + */ + public Orbit estimate(final Frame outputFrame, final AngularAzEl azEl1, + final AngularAzEl azEl2, final AngularAzEl azEl3, + final double rho1init, final double rho3init) { + return estimate(outputFrame, azEl1, azEl2, azEl3, rho1init, rho3init, 0, true); + } + + /** Estimate orbit from three angular observations. + * + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param azEl1 first angular observation + * @param azEl2 second angular observation + * @param azEl3 third angular observation * @param rho1init initial guess of the range problem. range 1, in meters * @param rho3init initial guess of the range problem. range 3, in meters * @param nRev number of complete revolutions between observation 1 and 3 @@ -135,39 +164,99 @@ public double getRange3() { * (i.e., date of the second angular observation) * @since 11.0 */ - public KeplerianOrbit estimate(final Frame frame, final AngularRaDec raDec1, - final AngularRaDec raDec2, final AngularRaDec raDec3, - final double rho1init, final double rho3init, - final int nRev, final boolean direction) { - return estimate(frame, - stationPosition(frame, raDec1), - stationPosition(frame, raDec2), - stationPosition(frame, raDec3), - lineOfSight(raDec1), raDec1.getDate(), - lineOfSight(raDec2), raDec2.getDate(), - lineOfSight(raDec3), raDec3.getDate(), + public Orbit estimate(final Frame outputFrame, final AngularAzEl azEl1, + final AngularAzEl azEl2, final AngularAzEl azEl3, + final double rho1init, final double rho3init, + final int nRev, final boolean direction) { + return estimate(outputFrame, + azEl1.getGroundStationPosition(outputFrame), + azEl2.getGroundStationPosition(outputFrame), + azEl3.getGroundStationPosition(outputFrame), + azEl1.getObservedLineOfSight(outputFrame), azEl1.getDate(), + azEl2.getObservedLineOfSight(outputFrame), azEl2.getDate(), + azEl3.getObservedLineOfSight(outputFrame), azEl3.getDate(), rho1init, rho3init, nRev, direction); } - /** Orbit got from three angular observations. - * @param frame inertial frame for observer coordinates and orbit estimate + /** Estimate orbit from three angular observations. + *

    + * This signature assumes there was less than an half revolution between start and final date + *

    + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param raDec1 first angular observation + * @param raDec2 second angular observation + * @param raDec3 third angular observation + * @param rho1init initial guess of the range problem. range 1, in meters + * @param rho3init initial guess of the range problem. range 3, in meters + * @return an estimate of the Keplerian orbit at the central date + * (i.e., date of the second angular observation) + * @since 11.0 + */ + public Orbit estimate(final Frame outputFrame, final AngularRaDec raDec1, + final AngularRaDec raDec2, final AngularRaDec raDec3, + final double rho1init, final double rho3init) { + return estimate(outputFrame, raDec1, raDec2, raDec3, rho1init, rho3init, 0, true); + } + + /** Estimate orbit from three angular observations. + * + * @param outputFrame inertial frame for observer coordinates and orbit estimate * @param raDec1 first angular observation * @param raDec2 second angular observation * @param raDec3 third angular observation * @param rho1init initial guess of the range problem. range 1, in meters * @param rho3init initial guess of the range problem. range 3, in meters + * @param nRev number of complete revolutions between observation 1 and 3 + * @param direction true if posigrade (short way) * @return an estimate of the Keplerian orbit at the central date * (i.e., date of the second angular observation) * @since 11.0 */ - public KeplerianOrbit estimate(final Frame frame, final AngularRaDec raDec1, - final AngularRaDec raDec2, final AngularRaDec raDec3, - final double rho1init, final double rho3init) { - return estimate(frame, raDec1, raDec2, raDec3, rho1init, rho3init, 0, true); + public Orbit estimate(final Frame outputFrame, final AngularRaDec raDec1, + final AngularRaDec raDec2, final AngularRaDec raDec3, + final double rho1init, final double rho3init, + final int nRev, final boolean direction) { + return estimate(outputFrame, + raDec1.getGroundStationPosition(outputFrame), + raDec2.getGroundStationPosition(outputFrame), + raDec3.getGroundStationPosition(outputFrame), + raDec1.getObservedLineOfSight(outputFrame), raDec1.getDate(), + raDec2.getObservedLineOfSight(outputFrame), raDec2.getDate(), + raDec3.getObservedLineOfSight(outputFrame), raDec3.getDate(), + rho1init, rho3init, nRev, direction); + } + + /** Estimate orbit from three line of sight. + *

    + * This signature assumes there was less than an half revolution between start and final date + *

    + * @param outputFrame inertial frame for observer coordinates and orbit estimate + * @param O1 Observer position 1 + * @param O2 Observer position 2 + * @param O3 Observer position 3 + * @param lineOfSight1 line of sight 1 + * @param dateObs1 date of observation 1 + * @param lineOfSight2 line of sight 2 + * @param dateObs2 date of observation 1 + * @param lineOfSight3 line of sight 3 + * @param dateObs3 date of observation 1 + * @param rho1init initial guess of the range problem. range 1, in meters + * @param rho3init initial guess of the range problem. range 3, in meters + * @return an estimate of the Keplerian orbit at the central date + * (i.e., date of the second angular observation) + */ + public Orbit estimate(final Frame outputFrame, final Vector3D O1, final Vector3D O2, final Vector3D O3, + final Vector3D lineOfSight1, final AbsoluteDate dateObs1, + final Vector3D lineOfSight2, final AbsoluteDate dateObs2, + final Vector3D lineOfSight3, final AbsoluteDate dateObs3, + final double rho1init, final double rho3init) { + return this.estimate(outputFrame, O1, O2, O3, lineOfSight1, dateObs1, lineOfSight2, dateObs2, + lineOfSight3, dateObs3, rho1init, rho3init, 0, true); } - /** Orbit got from Observed Three Lines of Sight (angles only). - * @param frame inertial frame for observer coordinates and orbit estimate + /** Estimate orbit from three line of sight. + * + * @param outputFrame inertial frame for observer coordinates and orbit estimate * @param O1 Observer position 1 * @param O2 Observer position 2 * @param O3 Observer position 3 @@ -184,12 +273,13 @@ public KeplerianOrbit estimate(final Frame frame, final AngularRaDec raDec1, * @return an estimate of the Keplerian orbit at the central date * (i.e., date of the second angular observation) */ - public KeplerianOrbit estimate(final Frame frame, final Vector3D O1, final Vector3D O2, final Vector3D O3, - final Vector3D lineOfSight1, final AbsoluteDate dateObs1, - final Vector3D lineOfSight2, final AbsoluteDate dateObs2, - final Vector3D lineOfSight3, final AbsoluteDate dateObs3, - final double rho1init, final double rho3init, final int nRev, - final boolean direction) { + public Orbit estimate(final Frame outputFrame, + final Vector3D O1, final Vector3D O2, final Vector3D O3, + final Vector3D lineOfSight1, final AbsoluteDate dateObs1, + final Vector3D lineOfSight2, final AbsoluteDate dateObs2, + final Vector3D lineOfSight3, final AbsoluteDate dateObs3, + final double rho1init, final double rho3init, final int nRev, + final boolean direction) { this.date1 = dateObs1; @@ -205,50 +295,20 @@ public KeplerianOrbit estimate(final Frame frame, final Vector3D O1, final Vecto this.vObserverPosition2 = O2.scalarMultiply(1. / R); this.vObserverPosition3 = O3.scalarMultiply(1. / R); - final int maxiter = 100; // maximum iter - // solve the range problem - solveRangeProblem(frame, + solveRangeProblem(outputFrame, rho1init / R, rho3init / R, dateObs3.durationFrom(dateObs1) / T, dateObs2.durationFrom(dateObs1) / T, nRev, direction, - lineOfSight1, lineOfSight2, lineOfSight3, - maxiter); + lineOfSight1, lineOfSight2, lineOfSight3); // use the Gibbs problem to get the orbit now that we have three position vectors. final IodGibbs gibbs = new IodGibbs(mu); - final Vector3D p1 = vObserverPosition1.add(lineOfSight1.scalarMultiply(rho1)).scalarMultiply(R); - final Vector3D p2 = vObserverPosition2.add(lineOfSight2.scalarMultiply(rho2)).scalarMultiply(R); - final Vector3D p3 = vObserverPosition3.add(lineOfSight3.scalarMultiply(rho3)).scalarMultiply(R); - return gibbs.estimate(frame, p1, dateObs1, p2, dateObs2, p3, dateObs3); - } - - /** Orbit got from Observed Three Lines of Sight (angles only). - * assuming there was less than an half revolution between start and final date - * @param frame inertial frame for observer coordinates and orbit estimate - * @param O1 Observer position 1 - * @param O2 Observer position 2 - * @param O3 Observer position 3 - * @param lineOfSight1 line of sight 1 - * @param dateObs1 date of observation 1 - * @param lineOfSight2 line of sight 2 - * @param dateObs2 date of observation 1 - * @param lineOfSight3 line of sight 3 - * @param dateObs3 date of observation 1 - * @param rho1init initial guess of the range problem. range 1, in meters - * @param rho3init initial guess of the range problem. range 3, in meters - * @return an estimate of the Keplerian orbit at the central date - * (i.e., date of the second angular observation) - */ - public KeplerianOrbit estimate(final Frame frame, final Vector3D O1, final Vector3D O2, final Vector3D O3, - final Vector3D lineOfSight1, final AbsoluteDate dateObs1, - final Vector3D lineOfSight2, final AbsoluteDate dateObs2, - final Vector3D lineOfSight3, final AbsoluteDate dateObs3, - final double rho1init, final double rho3init) { - - return this.estimate(frame, O1, O2, O3, lineOfSight1, dateObs1, lineOfSight2, dateObs2, - lineOfSight3, dateObs3, rho1init, rho3init, 0, true); + final Vector3D p1 = vObserverPosition1.add(lineOfSight1.scalarMultiply(rho1)).scalarMultiply(R); + final Vector3D p2 = vObserverPosition2.add(lineOfSight2.scalarMultiply(rho2)).scalarMultiply(R); + final Vector3D p3 = vObserverPosition3.add(lineOfSight3.scalarMultiply(rho3)).scalarMultiply(R); + return gibbs.estimate(outputFrame, p1, dateObs1, p2, dateObs2, p3, dateObs3); } /** Solve the range problem when three line of sight are given. @@ -257,23 +317,21 @@ public KeplerianOrbit estimate(final Frame frame, final Vector3D O1, final Vecto * @param rho3init initial value for range R3, in meters * @param T13 time of flight 1->3, in seconds * @param T12 time of flight 1->2, in seconds - * @param nrev number of revolutions + * @param nRev number of revolutions * @param direction posigrade (true) or retrograde * @param lineOfSight1 line of sight 1 * @param lineOfSight2 line of sight 2 * @param lineOfSight3 line of sight 3 - * @param maxIterations max iter - * @return nothing */ - private boolean solveRangeProblem(final Frame frame, - final double rho1init, final double rho3init, - final double T13, final double T12, - final int nrev, - final boolean direction, - final Vector3D lineOfSight1, - final Vector3D lineOfSight2, - final Vector3D lineOfSight3, - final int maxIterations) { + private void solveRangeProblem(final Frame frame, + final double rho1init, final double rho3init, + final double T13, final double T12, + final int nRev, + final boolean direction, + final Vector3D lineOfSight1, + final Vector3D lineOfSight2, + final Vector3D lineOfSight3) { + final int maxIterations = 100; final double ARBF = 1e-6; // finite differences step boolean withHalley = true; // use Halley's method final double cvtol = 1e-14; // convergence tolerance @@ -281,7 +339,6 @@ private boolean solveRangeProblem(final Frame frame, rho1 = rho1init; rho3 = rho3init; - int iter = 0; double stoppingCriterion = 10 * cvtol; while (iter < maxIterations && FastMath.abs(stoppingCriterion) > cvtol) { @@ -297,10 +354,10 @@ private boolean solveRangeProblem(final Frame frame, final Vector3D P2 = getPositionOnLoS2(frame, lineOfSight1, rho1, lineOfSight3, rho3, - T13, T12, nrev, direction); + T13, T12, nRev, direction); if (P2 == null) { - modifyIterate(lineOfSight1, lineOfSight2, lineOfSight3); + modifyIterate(lineOfSight1, lineOfSight3); } else { // R2 = P2.getNorm(); @@ -308,10 +365,6 @@ private boolean solveRangeProblem(final Frame frame, // compute current line of sight for (2) and the associated range final Vector3D C = P2.subtract(vObserverPosition2); rho2 = C.getNorm(); - - final double R10 = R1; - final double R30 = R3; - // indicate how close we are to the direction of sight for measurement (2) // for convergence test final double CR = lineOfSight2.dotProduct(C); @@ -326,7 +379,7 @@ private boolean solveRangeProblem(final Frame frame, // if ENt is zero we have a solution! final double ENR = ENt.getNorm(); if (ENR == 0.) { - return true; + break; } //Normalize EN @@ -341,13 +394,12 @@ private boolean solveRangeProblem(final Frame frame, final double[] GD = new double[2]; computeDerivatives(frame, rho1, rho3, - R10, R30, lineOfSight1, lineOfSight3, P, EN, Fc, T13, T12, withHalley, - nrev, + nRev, direction, FD, GD); @@ -374,18 +426,14 @@ private boolean solveRangeProblem(final Frame frame, ++iter; } // end while - - return true; } /** Change the current iterate if Lambert's problem solver failed to find a solution. * * @param lineOfSight1 line of sight 1 - * @param lineOfSight2 line of sight 2 * @param lineOfSight3 line of sight 3 */ private void modifyIterate(final Vector3D lineOfSight1, - final Vector3D lineOfSight2, final Vector3D lineOfSight3) { // This is a modifier proposed in the initial paper of Gooding. // Try to avoid Lambert-fail, by common-perpendicular starters @@ -412,8 +460,6 @@ private void modifyIterate(final Vector3D lineOfSight1, * @param frame frame to be used (orbit frame) * @param x current range 1 * @param y current range 3 - * @param R10 current radius 1 - * @param R30 current radius 3 * @param lineOfSight1 line of sight * @param lineOfSight3 line of sight * @param Pin basis vector @@ -422,21 +468,20 @@ private void modifyIterate(final Vector3D lineOfSight1, * @param T13 time of flight 1->3, in seconds * @param T12 time of flight 1->2, in seconds * @param withHalley use Halley iterative method - * @param nrev number of revolutions + * @param nRev number of revolutions * @param direction direction of motion * @param FD derivatives of f wrt (rho1, rho3) by finite differences * @param GD derivatives of g wrt (rho1, rho3) by finite differences */ private void computeDerivatives(final Frame frame, final double x, final double y, - final double R10, final double R30, final Vector3D lineOfSight1, final Vector3D lineOfSight3, final Vector3D Pin, final Vector3D Ein, final double F, final double T13, final double T12, final boolean withHalley, - final int nrev, + final int nRev, final boolean direction, final double[] FD, final double[] GD) { @@ -451,7 +496,7 @@ private void computeDerivatives(final Frame frame, final Vector3D Cm1 = getPositionOnLoS2 (frame, lineOfSight1, x - dx, lineOfSight3, y, - T13, T12, nrev, direction).subtract(vObserverPosition2); + T13, T12, nRev, direction).subtract(vObserverPosition2); final double Fm1 = P.dotProduct(Cm1); final double Gm1 = EN.dotProduct(Cm1); @@ -459,7 +504,7 @@ private void computeDerivatives(final Frame frame, final Vector3D Cp1 = getPositionOnLoS2 (frame, lineOfSight1, x + dx, lineOfSight3, y, - T13, T12, nrev, direction).subtract(vObserverPosition2); + T13, T12, nRev, direction).subtract(vObserverPosition2); final double Fp1 = P.dotProduct(Cp1); final double Gp1 = EN.dotProduct(Cp1); @@ -471,7 +516,7 @@ private void computeDerivatives(final Frame frame, final Vector3D Cm3 = getPositionOnLoS2 (frame, lineOfSight1, x, lineOfSight3, y - dy, - T13, T12, nrev, direction).subtract(vObserverPosition2); + T13, T12, nRev, direction).subtract(vObserverPosition2); final double Fm3 = P.dotProduct(Cm3); final double Gm3 = EN.dotProduct(Cm3); @@ -479,7 +524,7 @@ private void computeDerivatives(final Frame frame, final Vector3D Cp3 = getPositionOnLoS2 (frame, lineOfSight1, x, lineOfSight3, y + dy, - T13, T12, nrev, direction).subtract(vObserverPosition2); + T13, T12, nRev, direction).subtract(vObserverPosition2); final double Fp3 = P.dotProduct(Cp3); final double Gp3 = EN.dotProduct(Cp3); @@ -511,7 +556,7 @@ private void computeDerivatives(final Frame frame, final Vector3D Cp13 = getPositionOnLoS2 (frame, lineOfSight1, x + dx, lineOfSight3, y + dy, - T13, T12, nrev, direction).subtract(vObserverPosition2); + T13, T12, nRev, direction).subtract(vObserverPosition2); // f function value at (x1+dx1, x3+dx3) final double Fp13 = P.dotProduct(Cp13); @@ -521,18 +566,14 @@ private void computeDerivatives(final Frame frame, final Vector3D Cm13 = getPositionOnLoS2 (frame, lineOfSight1, x - dx, lineOfSight3, y - dy, - T13, T12, nrev, direction).subtract(vObserverPosition2); + T13, T12, nRev, direction).subtract(vObserverPosition2); // f function value at (x1-dx1, x3-dx3) final double Fm13 = P.dotProduct(Cm13); // g function value at (x1-dx1, x3-dx3) final double Gm13 = EN.dotProduct(Cm13); - // Second order derivatives: d^2f / drho1drho3 and d^2g / drho1drho3 - //double Fxy = Fp13 / (dx * dy) - (Fx / dy + Fy / dx) - - // 0.5 * (Fxx * dx / dy + Fyy * dy / dx); - //double Gxy = Gp13 / (dx * dy) - (Gx / dy + Gy / dx) - - // 0.5 * (Gxx * dx / dy + Gyy * dy / dx); + // Second order derivatives: final double Fxy = (Fp13 + Fm13) / (2 * dx * dy) - 0.5 * (Fxx * dx / dy + Fyy * dy / dx) - F / (dx * dy); final double Gxy = (Gp13 + Gm13) / (2 * dx * dy) - 0.5 * (Gxx * dx / dy + Gyy * dy / dx) - F / (dx * dy); @@ -566,7 +607,7 @@ private void computeDerivatives(final Frame frame, * @param RO3 distance along E3 * @param T12 time of flight * @param T13 time of flight - * @param nRev number of revolutions + * @param nRev number of revolutions * @param posigrade direction of motion * @return (R2-O2) */ @@ -613,65 +654,13 @@ private Vector3D getPositionOnLoS2(final Frame frame, // estimate the position at the second observation time // propagate (P1, V1) during TAU + T12 to get (P2, V2) - final Vector3D P2 = propagatePV(frame, P1, Vel1, T12); + final PVCoordinates pv1 = new PVCoordinates(P1, Vel1); + final Orbit orbit = new CartesianOrbit(pv1, frame, date1, 1.); - return P2; + return orbit.shiftedBy(T12).getPosition(); } return null; } - /** Propagate a solution (Kepler). - * @param frame frame to be used (orbit frame) - * @param P1 initial position vector - * @param V1 initial velocity vector - * @param tau propagation time - * @return final position vector - */ - private Vector3D propagatePV(final Frame frame, final Vector3D P1, final Vector3D V1, final double tau) { - final PVCoordinates pv1 = new PVCoordinates(P1, V1); - // create a Keplerian orbit. Assume MU = 1. - final KeplerianOrbit orbit = new KeplerianOrbit(pv1, frame, date1, 1.); - return orbit.shiftedBy(tau).getPVCoordinates().getPosition(); - } - - /** - * Calculates the line of sight vector. - * @param alpha right ascension angle, in radians - * @param delta declination angle, in radians - * @return the line of sight vector - * @since 11.0 - */ - public static Vector3D lineOfSight(final double alpha, final double delta) { - return new Vector3D(FastMath.cos(delta) * FastMath.cos(alpha), - FastMath.cos(delta) * FastMath.sin(alpha), - FastMath.sin(delta)); - } - - /** - * Calculate the line of sight vector from an AngularRaDec measurement. - * @param raDec measurement - * @return the line of sight vector - * @since 11.0 - */ - public static Vector3D lineOfSight(final AngularRaDec raDec) { - - // Observed values - final double[] observed = raDec.getObservedValue(); - - // Return - return lineOfSight(observed[0], observed[1]); - - } - - /** - * Get the station position from the right ascension / declination measurement. - * @param frame inertial frame for station posiiton and orbit estimate - * @param raDec measurement - * @return the station position - */ - private static Vector3D stationPosition(final Frame frame, final AngularRaDec raDec) { - return raDec.getStation().getBaseFrame().getPVCoordinates(raDec.getDate(), frame).getPosition(); - } - } diff --git a/src/main/java/org/orekit/estimation/iod/IodLambert.java b/src/main/java/org/orekit/estimation/iod/IodLambert.java index fff90d5e35..f1755b0e18 100644 --- a/src/main/java/org/orekit/estimation/iod/IodLambert.java +++ b/src/main/java/org/orekit/estimation/iod/IodLambert.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,20 +20,23 @@ import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.estimation.measurements.PV; import org.orekit.estimation.measurements.Position; import org.orekit.frames.Frame; -import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; import org.orekit.time.AbsoluteDate; import org.orekit.utils.PVCoordinates; /** - * Lambert initial orbit determination, assuming Keplerian motion. + * Lambert position-based Initial Orbit Determination (IOD) algorithm, assuming Keplerian motion. + *

    * An orbit is determined from two position vectors. * * References: * Battin, R.H., An Introduction to the Mathematics and Methods of Astrodynamics, AIAA Education, 1999. * Lancaster, E.R. and Blanchard, R.C., A Unified Form of Lambert’s Theorem, Goddard Space Flight Center, 1968. - * + *

    * @author Joris Olympio * @since 8.0 */ @@ -81,12 +84,49 @@ public IodLambert(final double mu) { * @return an initial Keplerian orbit estimation at the first observation date t1 * @since 11.0 */ - public KeplerianOrbit estimate(final Frame frame, final boolean posigrade, - final int nRev, final Position p1, final Position p2) { + public Orbit estimate(final Frame frame, final boolean posigrade, + final int nRev, final Position p1, final Position p2) { return estimate(frame, posigrade, nRev, p1.getPosition(), p1.getDate(), p2.getPosition(), p2.getDate()); } + /** Estimate an initial orbit from two PV measurements. + *

    + * The logic for setting {@code posigrade} and {@code nRev} is that the + * sweep angle Δυ travelled by the object between {@code t1} and {@code t2} is + * 2π {@code nRev +1} - α if {@code posigrade} is false and 2π {@code nRev} + α + * if {@code posigrade} is true, where α is the separation angle between + * {@code p1} and {@code p2}, which is always computed between 0 and π + * (because in 3D without a normal reference, vector angles cannot go past π). + *

    + *

    + * This implies that {@code posigrade} should be set to true if {@code p2} is + * located in the half orbit starting at {@code p1} and it should be set to + * false if {@code p2} is located in the half orbit ending at {@code p1}, + * regardless of the number of periods between {@code t1} and {@code t2}, + * and {@code nRev} should be set accordingly. + *

    + *

    + * As an example, if {@code t2} is less than half a period after {@code t1}, + * then {@code posigrade} should be {@code true} and {@code nRev} should be 0. + * If {@code t2} is more than half a period after {@code t1} but less than + * one period after {@code t1}, {@code posigrade} should be {@code false} and + * {@code nRev} should be 0. + *

    + * @param frame measurements frame + * @param posigrade flag indicating the direction of motion + * @param nRev number of revolutions + * @param pv1 first PV measurement + * @param pv2 second PV measurement + * @return an initial Keplerian orbit estimation at the first observation date t1 + * @since 12.0 + */ + public Orbit estimate(final Frame frame, final boolean posigrade, + final int nRev, final PV pv1, final PV pv2) { + return estimate(frame, posigrade, nRev, + pv1.getPosition(), pv1.getDate(), pv2.getPosition(), pv2.getDate()); + } + /** Estimate a Keplerian orbit given two position vectors and a duration. *

    * The logic for setting {@code posigrade} and {@code nRev} is that the @@ -119,10 +159,10 @@ public KeplerianOrbit estimate(final Frame frame, final boolean posigrade, * @param t2 date of observation 2 * @return an initial Keplerian orbit estimate at the first observation date t1 */ - public KeplerianOrbit estimate(final Frame frame, final boolean posigrade, - final int nRev, - final Vector3D p1, final AbsoluteDate t1, - final Vector3D p2, final AbsoluteDate t2) { + public Orbit estimate(final Frame frame, final boolean posigrade, + final int nRev, + final Vector3D p1, final AbsoluteDate t1, + final Vector3D p2, final AbsoluteDate t2) { final double r1 = p1.getNorm(); final double r2 = p2.getNorm(); @@ -168,8 +208,8 @@ public KeplerianOrbit estimate(final Frame frame, final boolean posigrade, final Vector3D Vel1 = new Vector3D(V * Vdep[0] / r1, p1, V * Vdep[1] / RT, Pt); - // compute the equivalent Keplerian orbit - return new KeplerianOrbit(new PVCoordinates(p1, Vel1), frame, t1, mu); + // compute the equivalent Cartesian orbit + return new CartesianOrbit(new PVCoordinates(p1, Vel1), frame, t1, mu); } return null; @@ -198,10 +238,9 @@ boolean solveLambertPb(final double r1, final double r2, final double dth, final final int m = FastMath.abs(mRev); final double rtof = FastMath.abs(tau); - final double theta = dth; // non-dimensional chord ||r2-r1|| - final double chord = FastMath.sqrt(r1 * r1 + r2 * r2 - 2 * r1 * r2 * FastMath.cos(theta)); + final double chord = FastMath.sqrt(r1 * r1 + r2 * r2 - 2 * r1 * r2 * FastMath.cos(dth)); // non-dimensional semi-perimeter of the triangle final double speri = 0.5 * (r1 + r2 + chord); diff --git a/src/main/java/org/orekit/estimation/iod/IodLaplace.java b/src/main/java/org/orekit/estimation/iod/IodLaplace.java index 3763f48ffe..e161e1d5e9 100644 --- a/src/main/java/org/orekit/estimation/iod/IodLaplace.java +++ b/src/main/java/org/orekit/estimation/iod/IodLaplace.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,21 +22,26 @@ import org.hipparchus.linear.Array2DRowRealMatrix; import org.hipparchus.linear.LUDecomposition; import org.hipparchus.util.FastMath; +import org.orekit.estimation.measurements.AngularAzEl; import org.orekit.estimation.measurements.AngularRaDec; import org.orekit.frames.Frame; import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; import org.orekit.time.AbsoluteDate; import org.orekit.utils.PVCoordinates; /** - * Laplace angles-only initial orbit determination, assuming Keplerian motion. - * An orbit is determined from three angular observations from the same site. - * + * Laplace angles-only Initial Orbit Determination (IOD) algorithm, assuming Keplerian motion. + *

    + * Laplace algorithm is one of the first method to determine orbits. + * An orbit is determined from three lines of sight w.r.t. their respective observers + * inertial positions vectors. For Laplace method, the observer is identical for all + * observations. * * Reference: * Bate, R., Mueller, D. D., & White, J. E. (1971). Fundamentals of astrodynamics. * New York: Dover Publications. - * + *

    * @author Shiva Iyer * @since 10.1 */ @@ -55,27 +60,45 @@ public IodLaplace(final double mu) { /** Estimate the orbit from three angular observations at the same location. * - * @param frame inertial frame for observer coordinates and orbit estimate - * @param obsPva Observer coordinates at time of raDec2 + * @param outputFrame Observer coordinates at time of raDec2 + * @param azEl1 first angular observation + * @param azEl2 second angular observation + * @param azEl3 third angular observation + * @return estimate of the orbit at the central date or null if + * no estimate is possible with the given data + * @since 12.0 + */ + public Orbit estimate(final Frame outputFrame, + final AngularAzEl azEl1, final AngularAzEl azEl2, + final AngularAzEl azEl3) { + return estimate(outputFrame, azEl2.getGroundStationCoordinates(outputFrame), + azEl1.getDate(), azEl1.getObservedLineOfSight(outputFrame), + azEl2.getDate(), azEl2.getObservedLineOfSight(outputFrame), + azEl3.getDate(), azEl3.getObservedLineOfSight(outputFrame)); + } + + /** Estimate the orbit from three angular observations at the same location. + * + * @param outputFrame Observer coordinates at time of raDec2 * @param raDec1 first angular observation * @param raDec2 second angular observation * @param raDec3 third angular observation - * @return estimate of the orbit at the central date obsDate2 or null if + * @return estimate of the orbit at the central date or null if * no estimate is possible with the given data * @since 11.0 */ - public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva, - final AngularRaDec raDec1, final AngularRaDec raDec2, - final AngularRaDec raDec3) { - return estimate(frame, obsPva, - raDec1.getDate(), lineOfSight(raDec1), - raDec2.getDate(), lineOfSight(raDec2), - raDec3.getDate(), lineOfSight(raDec3)); + public Orbit estimate(final Frame outputFrame, + final AngularRaDec raDec1, final AngularRaDec raDec2, + final AngularRaDec raDec3) { + return estimate(outputFrame, raDec2.getGroundStationCoordinates(outputFrame), + raDec1.getDate(), raDec1.getObservedLineOfSight(outputFrame), + raDec2.getDate(), raDec2.getObservedLineOfSight(outputFrame), + raDec3.getDate(), raDec3.getObservedLineOfSight(outputFrame)); } - /** Estimate orbit from three line of sight angles from the same location. + /** Estimate orbit from three line of sight angles at the same location. * - * @param frame inertial frame for observer coordinates and orbit estimate + * @param outputFrame inertial frame for observer coordinates and orbit estimate * @param obsPva Observer coordinates at time obsDate2 * @param obsDate1 date of observation 1 * @param los1 line of sight unit vector 1 @@ -86,21 +109,22 @@ public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva, * @return estimate of the orbit at the central date obsDate2 or null if * no estimate is possible with the given data */ - public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva, - final AbsoluteDate obsDate1, final Vector3D los1, - final AbsoluteDate obsDate2, final Vector3D los2, - final AbsoluteDate obsDate3, final Vector3D los3) { + public Orbit estimate(final Frame outputFrame, final PVCoordinates obsPva, + final AbsoluteDate obsDate1, final Vector3D los1, + final AbsoluteDate obsDate2, final Vector3D los2, + final AbsoluteDate obsDate3, final Vector3D los3) { + // The first observation is taken as t1 = 0 final double t2 = obsDate2.durationFrom(obsDate1); final double t3 = obsDate3.durationFrom(obsDate1); // Calculate the first and second derivatives of the Line Of Sight vector at t2 final Vector3D Ldot = los1.scalarMultiply((t2 - t3) / (t2 * t3)). - add(los2.scalarMultiply((2.0 * t2 - t3) / (t2 * (t2 - t3)))). - add(los3.scalarMultiply(t2 / (t3 * (t3 - t2)))); + add(los2.scalarMultiply((2.0 * t2 - t3) / (t2 * (t2 - t3)))). + add(los3.scalarMultiply(t2 / (t3 * (t3 - t2)))); final Vector3D Ldotdot = los1.scalarMultiply(2.0 / (t2 * t3)). - add(los2.scalarMultiply(2.0 / (t2 * (t2 - t3)))). - add(los3.scalarMultiply(2.0 / (t3 * (t3 - t2)))); + add(los2.scalarMultiply(2.0 / (t2 * (t2 - t3)))). + add(los3.scalarMultiply(2.0 / (t3 * (t3 - t2)))); // The determinant will vanish if the observer lies in the plane of the orbit at t2 final double D = 2.0 * getDeterminant(los2, Ldot, Ldotdot); @@ -108,8 +132,8 @@ public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva, return null; } - final double Dsq = D * D; - final double R = obsPva.getPosition().getNorm(); + final double Dsq = D * D; + final double R = obsPva.getPosition().getNorm(); final double RdotL = obsPva.getPosition().dotProduct(los2); final double D1 = getDeterminant(los2, Ldot, obsPva.getAcceleration()); @@ -128,14 +152,14 @@ public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva, // Use the Laguerre polynomial solver and take the initial guess to be // 5 times the observer's position magnitude final LaguerreSolver solver = new LaguerreSolver(1E-10, 1E-10, 1E-10); - final Complex[] roots = solver.solveAllComplex(coeff, 5.0 * R); + final Complex[] roots = solver.solveAllComplex(coeff, 5.0 * R); // We consider "r" to be the positive real root with the largest magnitude double rMag = 0.0; - for (int i = 0; i < roots.length; i++) { - if (roots[i].getReal() > rMag && - FastMath.abs(roots[i].getImaginary()) < solver.getAbsoluteAccuracy()) { - rMag = roots[i].getReal(); + for (Complex root : roots) { + if (root.getReal() > rMag && + FastMath.abs(root.getImaginary()) < solver.getAbsoluteAccuracy()) { + rMag = root.getReal(); } } if (rMag == 0.0) { @@ -144,49 +168,20 @@ public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva, // Calculate rho, the slant range from the observer to the satellite at t2. // This yields the "r" vector, which is the satellite's position vector at t2. - final double rCubed = rMag * rMag * rMag; - final double rho = -2.0 * D1 / D - 2.0 * mu * D2 / (D * rCubed); + final double rCubed = rMag * rMag * rMag; + final double rho = -2.0 * D1 / D - 2.0 * mu * D2 / (D * rCubed); final Vector3D posVec = los2.scalarMultiply(rho).add(obsPva.getPosition()); // Calculate rho_dot at t2, which will yield the satellite's velocity vector at t2 - final double D3 = getDeterminant(los2, obsPva.getAcceleration(), Ldotdot); - final double D4 = getDeterminant(los2, obsPva.getPosition(), Ldotdot); + final double D3 = getDeterminant(los2, obsPva.getAcceleration(), Ldotdot); + final double D4 = getDeterminant(los2, obsPva.getPosition(), Ldotdot); final double rhoDot = -D3 / D - mu * D4 / (D * rCubed); final Vector3D velVec = los2.scalarMultiply(rhoDot). - add(Ldot.scalarMultiply(rho)). - add(obsPva.getVelocity()); + add(Ldot.scalarMultiply(rho)). + add(obsPva.getVelocity()); // Return the estimated orbit - return new CartesianOrbit(new PVCoordinates(posVec, velVec), frame, obsDate2, mu); - } - - /** - * Calculates the line of sight vector. - * @param alpha right ascension angle, in radians - * @param delta declination angle, in radians - * @return the line of sight vector - * @since 11.0 - */ - public static Vector3D lineOfSight(final double alpha, final double delta) { - return new Vector3D(FastMath.cos(delta) * FastMath.cos(alpha), - FastMath.cos(delta) * FastMath.sin(alpha), - FastMath.sin(delta)); - } - - /** - * Calculate the line of sight vector from an AngularRaDec measurement. - * @param raDec measurement - * @return the line of sight vector - * @since 11.0 - */ - public static Vector3D lineOfSight(final AngularRaDec raDec) { - - // Observed values - final double[] observed = raDec.getObservedValue(); - - // Return - return lineOfSight(observed[0], observed[1]); - + return new CartesianOrbit(new PVCoordinates(posVec, velVec), outputFrame, obsDate2, mu); } /** Calculate the determinant of the matrix with given column vectors. diff --git a/src/main/java/org/orekit/estimation/iod/package-info.java b/src/main/java/org/orekit/estimation/iod/package-info.java index f7048cb7ec..5a39079139 100644 --- a/src/main/java/org/orekit/estimation/iod/package-info.java +++ b/src/main/java/org/orekit/estimation/iod/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java b/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java index f5521c4134..bc467b4509 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java +++ b/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -40,14 +40,15 @@ import org.orekit.propagation.Propagator; import org.orekit.propagation.PropagatorsParallelizer; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.propagation.integration.AbstractJacobiansMapper; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.propagation.sampling.MultiSatStepHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.ChronologicalComparator; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeSpanMap; /** Bridge between {@link ObservedMeasurement measurements} and {@link * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem @@ -55,14 +56,16 @@ * @author Luc Maisonobe * @author Bryan Cazabonne * @author Thomas Paulet + * @author Melina Vanel * @since 11.0 */ public abstract class AbstractBatchLSModel implements MultivariateJacobianFunction { /** Builders for propagators. */ - private final OrbitDeterminationPropagatorBuilder[] builders; + private final PropagatorBuilder[] builders; - /** Array of each builder's selected orbit drivers. + /** Array of each builder's selected orbit drivers. Orbit drivers + * should have only 1 span on their value TimeSpanMap. * @since 11.1 */ private final ParameterDriversList[] estimatedOrbitalParameters; @@ -130,28 +133,9 @@ public abstract class AbstractBatchLSModel implements MultivariateJacobianFuncti * @param propagatorBuilders builders to use for propagation * @param measurements measurements * @param estimatedMeasurementsParameters estimated measurements parameters - * @param harvesters harvesters for matrices (ignored since 11.1) * @param observer observer to be notified at model calls - * @deprecated as of 11.1, replaced by [@link #AbstractBatchLSModel(OrbitDeterminationPropagatorBuilder[], - * List, ParameterDriversList, ModelObserver)} */ - @Deprecated - public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final MatricesHarvester[] harvesters, - final ModelObserver observer) { - this(propagatorBuilders, measurements, estimatedMeasurementsParameters, observer); - } - - /** - * Constructor. - * @param propagatorBuilders builders to use for propagation - * @param measurements measurements - * @param estimatedMeasurementsParameters estimated measurements parameters - * @param observer observer to be notified at model calls - */ - public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders, + public AbstractBatchLSModel(final PropagatorBuilder[] propagatorBuilders, final List> measurements, final ParameterDriversList estimatedMeasurementsParameters, final ModelObserver observer) { @@ -159,7 +143,7 @@ public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagat this.builders = propagatorBuilders.clone(); this.measurements = measurements; this.estimatedMeasurementsParameters = estimatedMeasurementsParameters; - this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size()); + this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getNbValuesToEstimate()); this.estimatedOrbitalParameters = new ParameterDriversList[builders.length]; this.estimatedPropagationParameters = new ParameterDriversList[builders.length]; this.evaluations = new IdentityHashMap<>(measurements.size()); @@ -196,10 +180,20 @@ public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagat // The index i in array estimatedPropagationParameters (attribute of the class) is populated // when the first call to getSelectedPropagationDriversForBuilder(i) is made for (final DelegatingDriver delegating : getSelectedPropagationDriversForBuilder(i).getDrivers()) { - final String driverName = delegating.getName(); - // Add the driver name if it has not been added yet - if (!estimatedPropagationParametersNames.contains(driverName)) { - estimatedPropagationParametersNames.add(driverName); + + final TimeSpanMap delegatingNameSpanMap = delegating.getNamesSpanMap(); + // for each span (for each estimated value) corresponding name is added + Span currentNameSpan = delegatingNameSpanMap.getFirstSpan(); + // Add the driver name if it has not been added yet and the number of estimated values for this param + if (!estimatedPropagationParametersNames.contains(currentNameSpan.getData())) { + estimatedPropagationParametersNames.add(currentNameSpan.getData()); + } + for (int spanNumber = 1; spanNumber < delegatingNameSpanMap.getSpansNumber(); ++spanNumber) { + currentNameSpan = delegatingNameSpanMap.getSpan(currentNameSpan.getEnd()); + // Add the driver name if it has not been added yet and the number of estimated values for this param + if (!estimatedPropagationParametersNames.contains(currentNameSpan.getData())) { + estimatedPropagationParametersNames.add(currentNameSpan.getData()); + } } } } @@ -210,11 +204,12 @@ public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagat propagationParameterColumns.put(driverName, columns); ++columns; } - // Populate the map of measurement drivers' columns and update the total number of columns for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { - measurementParameterColumns.put(parameter.getName(), columns); - ++columns; + for (Span span = parameter.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + measurementParameterColumns.put(span.getData(), columns); + columns++; + } } // Initialize point and value @@ -266,19 +261,7 @@ public boolean isForwardPropagation() { * @param propagator {@link Propagator} to configure * @return harvester harvester to retrive the State Transition Matrix and Jacobian Matrix */ - protected MatricesHarvester configureHarvester(final Propagator propagator) { - // FIXME: this default implementation is only intended for version 11.1 to delegate to a deprecated method - // it should be removed in 12.0 when configureDerivatives is removed - return configureDerivatives(propagator); - } - - /** Configure the propagator to compute derivatives. - * @param propagators {@link Propagator} to configure - * @return mapper for this propagator - * @deprecated as of 11.1, replaced by {@link #configureHarvester(Propagator)} - */ - @Deprecated - protected abstract AbstractJacobiansMapper configureDerivatives(Propagator propagators); + protected abstract MatricesHarvester configureHarvester(Propagator propagator); /** Configure the current estimated orbits. *

    @@ -391,28 +374,42 @@ public Propagator[] createPropagators(final RealVector point) { final Propagator[] propagators = new Propagator[builders.length]; + // Set up the propagators for (int i = 0; i < builders.length; ++i) { - // Get the number of selected orbital drivers in the builder + int element = 0; + // Get the number of values to estimate for selected orbital drivers in the builder final int nbOrb = orbitsEndColumns[i] - orbitsStartColumns[i]; // Get the list of selected propagation drivers in the builder and its size final ParameterDriversList selectedPropagationDrivers = getSelectedPropagationDriversForBuilder(i); final int nbParams = selectedPropagationDrivers.getNbParams(); + final int nbValuesToEstimate = selectedPropagationDrivers.getNbValuesToEstimate(); // Init the array of normalized parameters for the builder - final double[] propagatorArray = new double[nbOrb + nbParams]; + final double[] propagatorArray = new double[nbOrb + nbValuesToEstimate]; // Add the orbital drivers normalized values for (int j = 0; j < nbOrb; ++j) { - propagatorArray[j] = point.getEntry(orbitsStartColumns[i] + j); + propagatorArray[element++] = point.getEntry(orbitsStartColumns[i] + j); } // Add the propagation drivers normalized values for (int j = 0; j < nbParams; ++j) { - propagatorArray[nbOrb + j] = - point.getEntry(propagationParameterColumns.get(selectedPropagationDrivers.getDrivers().get(j).getName())); + final DelegatingDriver driver = selectedPropagationDrivers.getDrivers().get(j); + final TimeSpanMap delegatingNameSpanMap = driver.getNamesSpanMap(); + // get point entry for each span (for each estimated value), point is sorted + // with following parameters values and for each parameter driver + // span value are sorted in chronological order + Span currentNameSpan = delegatingNameSpanMap.getFirstSpan(); + propagatorArray[element++] = point.getEntry(propagationParameterColumns.get(currentNameSpan.getData())); + + for (int spanNumber = 1; spanNumber < delegatingNameSpanMap.getSpansNumber(); ++spanNumber) { + currentNameSpan = delegatingNameSpanMap.getSpan(currentNameSpan.getEnd()); + propagatorArray[element++] = point.getEntry(propagationParameterColumns.get(currentNameSpan.getData())); + + } } // Build the propagator @@ -454,7 +451,7 @@ public void fetchEvaluatedMeasurement(final int index, final EstimatedMeasuremen // partial derivatives of the current Cartesian coordinates with respect to current orbital state final double[][] aCY = new double[6][6]; final Orbit currentOrbit = evaluationStates[k].getOrbit(); - currentOrbit.getJacobianWrtParameters(builders[p].getPositionAngle(), aCY); + currentOrbit.getJacobianWrtParameters(builders[p].getPositionAngleType(), aCY); final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false); // Jacobian of the measurement with respect to current orbital state @@ -484,11 +481,19 @@ public void fetchEvaluatedMeasurement(final int index, final EstimatedMeasuremen if (nbParams > 0) { final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]); final RealMatrix dMdPp = dMdY.multiply(dYdPp); + for (int i = 0; i < dMdPp.getRowDimension(); ++i) { + int col = 0; + + // Add the propagation drivers normalized values for (int j = 0; j < nbParams; ++j) { final ParameterDriver delegating = selectedPropagationDrivers.getDrivers().get(j); - jacobian.addToEntry(index + i, propagationParameterColumns.get(delegating.getName()), - weight[i] * dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale()); + final TimeSpanMap delegatingNameSpanMap = delegating.getNamesSpanMap(); + // get point entry for each span (for each estimated value), point is sorted + for (Span currentNameSpan = delegatingNameSpanMap.getFirstSpan(); currentNameSpan != null; currentNameSpan = currentNameSpan.next()) { + jacobian.addToEntry(index + i, propagationParameterColumns.get(currentNameSpan.getData()), + weight[i] * dMdPp.getEntry(i, col++) / sigma[i] * delegating.getScale()); + } } } } @@ -496,10 +501,12 @@ public void fetchEvaluatedMeasurement(final int index, final EstimatedMeasuremen // Jacobian of the measurement with respect to measurements parameters for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { if (driver.isSelected()) { - final double[] aMPm = evaluation.getParameterDerivatives(driver); - for (int i = 0; i < aMPm.length; ++i) { - jacobian.setEntry(index + i, measurementParameterColumns.get(driver.getName()), - weight[i] * aMPm[i] / sigma[i] * driver.getScale()); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final double[] aMPm = evaluation.getParameterDerivatives(driver, span.getStart()); + for (int i = 0; i < aMPm.length; ++i) { + jacobian.setEntry(index + i, measurementParameterColumns.get(span.getData()), + weight[i] * aMPm[i] / sigma[i] * driver.getScale()); + } } } } @@ -515,7 +522,10 @@ private MultiSatStepHandler configureMeasurements(final RealVector point) { // Set up the measurement parameters int index = orbitsEndColumns[builders.length - 1] + propagationParameterColumns.size(); for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { - parameter.setNormalizedValue(point.getEntry(index++)); + + for (Span span = parameter.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + parameter.setNormalizedValue(point.getEntry(index++), span.getStart()); + } } // Set up measurements handler diff --git a/src/main/java/org/orekit/estimation/leastsquares/BatchLSEstimator.java b/src/main/java/org/orekit/estimation/leastsquares/BatchLSEstimator.java index ae7fc81d34..da45c5973c 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/BatchLSEstimator.java +++ b/src/main/java/org/orekit/estimation/leastsquares/BatchLSEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -42,21 +42,29 @@ import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.orbits.Orbit; import org.orekit.propagation.Propagator; +import org.orekit.propagation.analytical.BrouwerLyddanePropagator; +import org.orekit.propagation.analytical.EcksteinHechlerPropagator; +import org.orekit.propagation.analytical.Ephemeris; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.analytical.tle.TLEPropagator; import org.orekit.propagation.conversion.AbstractPropagatorBuilder; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import org.orekit.utils.TimeSpanMap.Span; /** Least squares estimator for orbit determination. *

    - * Since 10.0, the least squares estimator can be used with both - * {@link NumericalPropagator numerical} and {@link DSSTPropagator DSST} - * orbit propagators. + * The least squares estimator can be used with different orbit propagators + * in Orekit. Current propagators list of usable propagators are {@link NumericalPropagator numerical}, + * {@link DSSTPropagator DSST}, {@link BrouwerLyddanePropagator Brouwer-Lyddane}, + * {@link EcksteinHechlerPropagator Eckstein-Hechler}, {@link TLEPropagator SGP4}, + * {@link KeplerianPropagator Keplerian}, and {@link Ephemeris ephemeris-based}. *

    * @author Luc Maisonobe * @since 8.0 @@ -64,7 +72,7 @@ public class BatchLSEstimator { /** Builders for propagator. */ - private final OrbitDeterminationPropagatorBuilder[] builders; + private final PropagatorBuilder[] builders; /** Measurements. */ private final List> measurements; @@ -116,7 +124,7 @@ public class BatchLSEstimator { * @param propagatorBuilder builders to use for propagation */ public BatchLSEstimator(final LeastSquaresOptimizer optimizer, - final OrbitDeterminationPropagatorBuilder... propagatorBuilder) { + final PropagatorBuilder... propagatorBuilder) { this.builders = propagatorBuilder; this.measurements = new ArrayList>(); @@ -376,18 +384,34 @@ public Propagator[] estimate() { final ParameterDriversList estimatedMeasurementsParameters = getMeasurementsParametersDrivers(true); // create start point - final double[] start = new double[estimatedOrbitalParameters.getNbParams() + - estimatedPropagatorParameters.getNbParams() + - estimatedMeasurementsParameters.getNbParams()]; + final double[] start = new double[estimatedOrbitalParameters.getNbValuesToEstimate() + + estimatedPropagatorParameters.getNbValuesToEstimate() + + estimatedMeasurementsParameters.getNbValuesToEstimate()]; + int iStart = 0; for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) { - start[iStart++] = driver.getNormalizedValue(); + Span span = driver.getValueSpanMap().getFirstSpan(); + start[iStart++] = driver.getNormalizedValue(span.getStart()); + for (int spanNumber = 0; spanNumber < driver.getNbOfValues() - 1; ++spanNumber) { + span = driver.getValueSpanMap().getSpan(span.getEnd()); + start[iStart++] = driver.getNormalizedValue(span.getStart()); + } } for (final ParameterDriver driver : estimatedPropagatorParameters.getDrivers()) { - start[iStart++] = driver.getNormalizedValue(); + Span span = driver.getValueSpanMap().getFirstSpan(); + start[iStart++] = driver.getNormalizedValue(span.getStart()); + for (int spanNumber = 0; spanNumber < driver.getNbOfValues() - 1; ++spanNumber) { + span = driver.getValueSpanMap().getSpan(span.getEnd()); + start[iStart++] = driver.getNormalizedValue(span.getStart()); + } } for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - start[iStart++] = driver.getNormalizedValue(); + Span span = driver.getValueSpanMap().getFirstSpan(); + start[iStart++] = driver.getNormalizedValue(span.getStart()); + for (int spanNumber = 0; spanNumber < driver.getNbOfValues() - 1; ++spanNumber) { + span = driver.getValueSpanMap().getSpan(span.getEnd()); + start[iStart++] = driver.getNormalizedValue(span.getStart()); + } } lsBuilder.start(start); @@ -411,7 +435,7 @@ public void modelCalled(final Orbit[] newOrbits, BatchLSEstimator.this.estimations = newEstimations; } }; - final AbstractBatchLSModel model = builders[0].buildLSModel(builders, measurements, estimatedMeasurementsParameters, modelObserver); + final AbstractBatchLSModel model = builders[0].buildLeastSquaresModel(builders, measurements, estimatedMeasurementsParameters, modelObserver); lsBuilder.model(model); @@ -492,13 +516,19 @@ public RealMatrix getPhysicalCovariances(final double threshold) { final double[] scale = new double[covariances.getRowDimension()]; int index = 0; for (final ParameterDriver driver : getOrbitalParametersDrivers(true).getDrivers()) { - scale[index++] = driver.getScale(); + for (int i = 0; i < driver.getNbOfValues(); ++i) { + scale[index++] = driver.getScale(); + } } for (final ParameterDriver driver : getPropagatorParametersDrivers(true).getDrivers()) { - scale[index++] = driver.getScale(); + for (int i = 0; i < driver.getNbOfValues(); ++i) { + scale[index++] = driver.getScale(); + } } for (final ParameterDriver driver : getMeasurementsParametersDrivers(true).getDrivers()) { - scale[index++] = driver.getScale(); + for (int i = 0; i < driver.getNbOfValues(); ++i) { + scale[index++] = driver.getScale(); + } } // unnormalize the matrix, to retrieve physical covariances @@ -706,20 +736,71 @@ public RealVector validate(final RealVector params) { int i = 0; for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) { // let the parameter handle min/max clipping - driver.setNormalizedValue(params.getEntry(i)); - params.setEntry(i++, driver.getNormalizedValue()); + if (driver.getNbOfValues() == 1) { + driver.setNormalizedValue(params.getEntry(i), null); + params.setEntry(i++, driver.getNormalizedValue(null)); + + // If the parameter driver contains only 1 value to estimate over the all time range + } else { + // initialization getting the value of the first Span + Span span = driver.getValueSpanMap().getFirstSpan(); + driver.setNormalizedValue(params.getEntry(i), span.getStart()); + params.setEntry(i++, driver.getNormalizedValue(span.getStart())); + + for (int spanNumber = 0; spanNumber < driver.getNbOfValues() - 1; ++spanNumber) { + final AbsoluteDate modificationDate = span.getEnd(); + // get next span, previousSpan.getEnd = span.getStart + span = driver.getValueSpanMap().getSpan(modificationDate); + driver.setNormalizedValue(params.getEntry(i), modificationDate); + params.setEntry(i++, driver.getNormalizedValue(modificationDate)); + } + } + } for (final ParameterDriver driver : estimatedPropagatorParameters.getDrivers()) { // let the parameter handle min/max clipping - driver.setNormalizedValue(params.getEntry(i)); - params.setEntry(i++, driver.getNormalizedValue()); + if (driver.getNbOfValues() == 1) { + driver.setNormalizedValue(params.getEntry(i), null); + params.setEntry(i++, driver.getNormalizedValue(null)); + + // If the parameter driver contains only 1 value to estimate over the all time range + } else { + // initialization getting the value of the first Span + Span span = driver.getValueSpanMap().getFirstSpan(); + driver.setNormalizedValue(params.getEntry(i), span.getStart()); + params.setEntry(i++, driver.getNormalizedValue(span.getStart())); + + for (int spanNumber = 0; spanNumber < driver.getNbOfValues() - 1; ++spanNumber) { + final AbsoluteDate modificationDate = span.getEnd(); + // get next span, previousSpan.getEnd = span.getStart + span = driver.getValueSpanMap().getSpan(modificationDate); + driver.setNormalizedValue(params.getEntry(i), modificationDate); + params.setEntry(i++, driver.getNormalizedValue(modificationDate)); + } + } } for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { // let the parameter handle min/max clipping - driver.setNormalizedValue(params.getEntry(i)); - params.setEntry(i++, driver.getNormalizedValue()); + if (driver.getNbOfValues() == 1) { + driver.setNormalizedValue(params.getEntry(i), null); + params.setEntry(i++, driver.getNormalizedValue(null)); + + // If the parameter driver contains only 1 value to estimate over the all time range + } else { + // initialization getting the value of the first Span + Span span = driver.getValueSpanMap().getFirstSpan(); + driver.setNormalizedValue(params.getEntry(i), span.getStart()); + params.setEntry(i++, driver.getNormalizedValue(span.getStart())); + + for (int spanNumber = 0; spanNumber < driver.getNbOfValues() - 1; ++spanNumber) { + final AbsoluteDate modificationDate = span.getEnd(); + // get next span, previousSpan.getEnd = span.getStart + span = driver.getValueSpanMap().getSpan(modificationDate); + driver.setNormalizedValue(params.getEntry(i), modificationDate); + params.setEntry(i++, driver.getNormalizedValue(modificationDate)); + } + } } - return params; } } diff --git a/src/main/java/org/orekit/estimation/leastsquares/BatchLSModel.java b/src/main/java/org/orekit/estimation/leastsquares/BatchLSModel.java index 2ba459dcb4..0db4821f2b 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/BatchLSModel.java +++ b/src/main/java/org/orekit/estimation/leastsquares/BatchLSModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,10 +22,7 @@ import org.orekit.orbits.Orbit; import org.orekit.propagation.MatricesHarvester; import org.orekit.propagation.Propagator; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.propagation.numerical.JacobiansMapper; -import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.utils.ParameterDriversList; /** Bridge between {@link ObservedMeasurement measurements} and {@link @@ -45,7 +42,7 @@ public class BatchLSModel extends AbstractBatchLSModel { * @param estimatedMeasurementsParameters estimated measurements parameters * @param observer observer to be notified at model calls */ - public BatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders, + public BatchLSModel(final PropagatorBuilder[] propagatorBuilders, final List> measurements, final ParameterDriversList estimatedMeasurementsParameters, final ModelObserver observer) { @@ -59,23 +56,6 @@ protected MatricesHarvester configureHarvester(final Propagator propagator) { return propagator.setupMatricesComputation(STM_NAME, null, null); } - /** {@inheritDoc} */ - @Override - @Deprecated - protected JacobiansMapper configureDerivatives(final Propagator propagator) { - - final org.orekit.propagation.numerical.PartialDerivativesEquations partials = - new org.orekit.propagation.numerical.PartialDerivativesEquations(STM_NAME, (NumericalPropagator) propagator); - - // add the derivatives to the initial state - final SpacecraftState rawState = propagator.getInitialState(); - final SpacecraftState stateWithDerivatives = partials.setInitialJacobians(rawState); - propagator.resetInitialState(stateWithDerivatives); - - return partials.getMapper(); - - } - /** {@inheritDoc} */ @Override protected Orbit configureOrbits(final MatricesHarvester harvester, final Propagator propagator) { diff --git a/src/main/java/org/orekit/estimation/leastsquares/BatchLSObserver.java b/src/main/java/org/orekit/estimation/leastsquares/BatchLSObserver.java index 322581c1fa..35034a3500 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/BatchLSObserver.java +++ b/src/main/java/org/orekit/estimation/leastsquares/BatchLSObserver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java b/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java index 18dc01574e..973b183678 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java +++ b/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,9 +24,8 @@ import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.propagation.semianalytical.dsst.DSSTHarvester; -import org.orekit.propagation.semianalytical.dsst.DSSTJacobiansMapper; import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; import org.orekit.utils.ParameterDriversList; @@ -35,7 +34,7 @@ * least squares problems}. *

    * This class is an adaption of the {@link BatchLSModel} class - * but for the {@link DSSTPropagator DSST propagator}. + * for the {@link DSSTPropagator DSST propagator}. *

    * @author Luc Maisonobe * @author Bryan Cazabonne @@ -50,27 +49,21 @@ public class DSSTBatchLSModel extends AbstractBatchLSModel { /** Type of the orbit used for the propagation.*/ private PropagationType propagationType; - /** Type of the elements used to define the orbital state.*/ - private PropagationType stateType; - /** Simple constructor. * @param propagatorBuilders builders to use for propagation * @param measurements measurements * @param estimatedMeasurementsParameters estimated measurements parameters * @param observer observer to be notified at model calls * @param propagationType type of the orbit used for the propagation (mean or osculating) - * @param stateType type of the elements used to define the orbital state (mean or osculating) */ - public DSSTBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders, + public DSSTBatchLSModel(final PropagatorBuilder[] propagatorBuilders, final List> measurements, final ParameterDriversList estimatedMeasurementsParameters, final ModelObserver observer, - final PropagationType propagationType, - final PropagationType stateType) { + final PropagationType propagationType) { // call super constructor super(propagatorBuilders, measurements, estimatedMeasurementsParameters, observer); this.propagationType = propagationType; - this.stateType = stateType; } /** {@inheritDoc} */ @@ -79,23 +72,6 @@ protected MatricesHarvester configureHarvester(final Propagator propagator) { return propagator.setupMatricesComputation(STM_NAME, null, null); } - /** {@inheritDoc} */ - @Override - @Deprecated - protected DSSTJacobiansMapper configureDerivatives(final Propagator propagator) { - - final org.orekit.propagation.semianalytical.dsst.DSSTPartialDerivativesEquations partials = - new org.orekit.propagation.semianalytical.dsst.DSSTPartialDerivativesEquations(STM_NAME, (DSSTPropagator) propagator, propagationType); - - // add the derivatives to the initial state - final SpacecraftState rawState = propagator.getInitialState(); - final SpacecraftState stateWithDerivatives = partials.setInitialJacobians(rawState); - ((DSSTPropagator) propagator).setInitialState(stateWithDerivatives, stateType); - - return partials.getMapper(); - - } - /** {@inheritDoc} */ @Override protected Orbit configureOrbits(final MatricesHarvester harvester, final Propagator propagator) { diff --git a/src/main/java/org/orekit/estimation/leastsquares/MeasurementHandler.java b/src/main/java/org/orekit/estimation/leastsquares/MeasurementHandler.java index 94e2d85879..9c0fcbafc5 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/MeasurementHandler.java +++ b/src/main/java/org/orekit/estimation/leastsquares/MeasurementHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/leastsquares/ModelObserver.java b/src/main/java/org/orekit/estimation/leastsquares/ModelObserver.java index 5701774681..13ff61aeae 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/ModelObserver.java +++ b/src/main/java/org/orekit/estimation/leastsquares/ModelObserver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/leastsquares/PreCompensation.java b/src/main/java/org/orekit/estimation/leastsquares/PreCompensation.java index a0a9b299f4..5e490542a5 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/PreCompensation.java +++ b/src/main/java/org/orekit/estimation/leastsquares/PreCompensation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/leastsquares/SequentialBatchLSEstimator.java b/src/main/java/org/orekit/estimation/leastsquares/SequentialBatchLSEstimator.java index 147976f062..ab8a273dc2 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/SequentialBatchLSEstimator.java +++ b/src/main/java/org/orekit/estimation/leastsquares/SequentialBatchLSEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,6 @@ import org.hipparchus.linear.QRDecomposer; import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem.Evaluation; import org.hipparchus.optim.nonlinear.vector.leastsquares.SequentialGaussNewtonOptimizer; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; import org.orekit.propagation.conversion.PropagatorBuilder; /** @@ -120,7 +119,7 @@ public class SequentialBatchLSEstimator extends BatchLSEstimator { * @param propagatorBuilder builders to use for propagation. */ public SequentialBatchLSEstimator(final SequentialGaussNewtonOptimizer sequentialOptimizer, - final OrbitDeterminationPropagatorBuilder... propagatorBuilder) { + final PropagatorBuilder... propagatorBuilder) { super(sequentialOptimizer, propagatorBuilder); } diff --git a/src/main/java/org/orekit/estimation/leastsquares/TLEBatchLSModel.java b/src/main/java/org/orekit/estimation/leastsquares/TLEBatchLSModel.java deleted file mode 100644 index 3923a2a837..0000000000 --- a/src/main/java/org/orekit/estimation/leastsquares/TLEBatchLSModel.java +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.leastsquares; - -import java.util.List; - -import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.orbits.Orbit; -import org.orekit.propagation.MatricesHarvester; -import org.orekit.propagation.Propagator; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.analytical.tle.TLEJacobiansMapper; -import org.orekit.propagation.analytical.tle.TLEPropagator; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.utils.ParameterDriversList; - -/** Bridge between {@link ObservedMeasurement measurements} and {@link - * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem - * least squares problems}. - * @author Luc Maisonobe - * @author Bryan Cazabonne - * @author Thomas Paulet - * @since 11.0 - * @deprecated as of 11.1, replaced by {@link BatchLSModel} - */ -@Deprecated -public class TLEBatchLSModel extends AbstractBatchLSModel { - - /** Name of the State Transition Matrix state. */ - private static final String STM_NAME = TLEBatchLSModel.class.getName() + "-derivatives"; - - /** Simple constructor. - * @param propagatorBuilders builders to use for propagation - * @param measurements measurements - * @param estimatedMeasurementsParameters estimated measurements parameters - * @param observer observer to be notified at model calls - */ - public TLEBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - // call super constructor - super(propagatorBuilders, measurements, estimatedMeasurementsParameters, observer); - } - - /** {@inheritDoc} */ - @Override - protected MatricesHarvester configureHarvester(final Propagator propagator) { - return ((TLEPropagator) propagator).setupMatricesComputation(STM_NAME, null, null); - } - - /** {@inheritDoc} */ - @Override - @Deprecated - protected TLEJacobiansMapper configureDerivatives(final Propagator propagator) { - - final org.orekit.propagation.analytical.tle.TLEPartialDerivativesEquations partials = - new org.orekit.propagation.analytical.tle.TLEPartialDerivativesEquations(STM_NAME, (TLEPropagator) propagator); - - // add the derivatives to the initial state - final SpacecraftState rawState = propagator.getInitialState(); - final SpacecraftState stateWithDerivatives = partials.setInitialJacobians(rawState); - propagator.resetInitialState(stateWithDerivatives); - - return partials.getMapper(); - - } - - /** {@inheritDoc} */ - @Override - protected Orbit configureOrbits(final MatricesHarvester harvester, final Propagator propagator) { - // Directly return the propagator's initial state - return propagator.getInitialState().getOrbit(); - } - -} diff --git a/src/main/java/org/orekit/estimation/leastsquares/package-info.java b/src/main/java/org/orekit/estimation/leastsquares/package-info.java index 5819a6c04c..8c547c72dd 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/package-info.java +++ b/src/main/java/org/orekit/estimation/leastsquares/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java b/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java index b7ed484de1..7a7f5ffe6f 100644 --- a/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -38,8 +38,7 @@ * @author Luc Maisonobe * @since 8.0 */ -public abstract class AbstractMeasurement> - implements ObservedMeasurement { +public abstract class AbstractMeasurement> implements ObservedMeasurement { /** List of the supported parameters. */ private final List supportedParameters; @@ -180,6 +179,20 @@ public List getSatellites() { return satellites; } + /** Estimate the theoretical value without derivatives. + *

    + * The theoretical value does not have any modifiers applied. + *

    + * @param iteration iteration number + * @param evaluation evaluation number + * @param states orbital states at measurement date + * @return theoretical value + * @see #estimate(int, int, SpacecraftState[]) + * @since 12.0 + */ + protected abstract EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(int iteration, int evaluation, + SpacecraftState[] states); + /** Estimate the theoretical value. *

    * The theoretical value does not have any modifiers applied. @@ -192,6 +205,22 @@ public List getSatellites() { */ protected abstract EstimatedMeasurement theoreticalEvaluation(int iteration, int evaluation, SpacecraftState[] states); + /** {@inheritDoc} */ + @Override + public EstimatedMeasurementBase estimateWithoutDerivatives(final int iteration, final int evaluation, final SpacecraftState[] states) { + + // compute the theoretical value + final EstimatedMeasurementBase estimation = theoreticalEvaluationWithoutDerivatives(iteration, evaluation, states); + + // apply the modifiers + for (final EstimationModifier modifier : modifiers) { + modifier.modifyWithoutDerivatives(estimation); + } + + return estimation; + + } + /** {@inheritDoc} */ @Override public EstimatedMeasurement estimate(final int iteration, final int evaluation, final SpacecraftState[] states) { @@ -278,8 +307,8 @@ public static double signalTimeOfFlight(final TimeStampedPVCoordinates adjustabl * @param the type of the components */ public static > T signalTimeOfFlight(final TimeStampedFieldPVCoordinates adjustableEmitterPV, - final FieldVector3D receiverPosition, - final FieldAbsoluteDate signalArrivalDate) { + final FieldVector3D receiverPosition, + final FieldAbsoluteDate signalArrivalDate) { // Initialize emission date search loop assuming the emitter PV is almost correct // this will be true for all but the first orbit determination iteration, @@ -322,7 +351,7 @@ public static TimeStampedFieldPVCoordinates getCoordinates(final Space // Position of the satellite expressed as a gradient // The components of the position are the 3 first derivative parameters - final Vector3D p = state.getPVCoordinates().getPosition(); + final Vector3D p = state.getPosition(); final FieldVector3D pDS = new FieldVector3D<>(Gradient.variable(freeParameters, firstDerivative + 0, p.getX()), Gradient.variable(freeParameters, firstDerivative + 1, p.getY()), diff --git a/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java b/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java index 89a1c95ac4..ce2d920157 100644 --- a/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java +++ b/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,20 +17,18 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -42,14 +40,11 @@ * @author Thierry Ceolin * @since 8.0 */ -public class AngularAzEl extends AbstractMeasurement { +public class AngularAzEl extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "AngularAzEl"; - /** Ground station from which measurement is performed. */ - private final GroundStation station; - /** Simple constructor. * @param station ground station from which measurement is performed * @param date date of the measurement @@ -62,25 +57,47 @@ public class AngularAzEl extends AbstractMeasurement { public AngularAzEl(final GroundStation station, final AbsoluteDate date, final double[] angular, final double[] sigma, final double[] baseWeight, final ObservableSatellite satellite) { - super(date, angular, sigma, baseWeight, Collections.singletonList(satellite)); - addParameterDriver(station.getClockOffsetDriver()); - addParameterDriver(station.getEastOffsetDriver()); - addParameterDriver(station.getNorthOffsetDriver()); - addParameterDriver(station.getZenithOffsetDriver()); - addParameterDriver(station.getPrimeMeridianOffsetDriver()); - addParameterDriver(station.getPrimeMeridianDriftDriver()); - addParameterDriver(station.getPolarOffsetXDriver()); - addParameterDriver(station.getPolarDriftXDriver()); - addParameterDriver(station.getPolarOffsetYDriver()); - addParameterDriver(station.getPolarDriftYDriver()); - this.station = station; + super(station, false, date, angular, sigma, baseWeight, satellite); } - /** Get the ground station from which measurement is performed. - * @return ground station from which measurement is performed - */ - public GroundStation getStation() { - return station; + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final GroundReceiverCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states[0]); + final TimeStampedPVCoordinates transitPV = common.getTransitPV(); + + // Station topocentric frame (east-north-zenith) in inertial frame expressed as Gradient + final Vector3D east = common.getOffsetToInertialDownlink().transformVector(Vector3D.PLUS_I); + final Vector3D north = common.getOffsetToInertialDownlink().transformVector(Vector3D.PLUS_J); + final Vector3D zenith = common.getOffsetToInertialDownlink().transformVector(Vector3D.PLUS_K); + + // Station-satellite vector expressed in inertial frame + final Vector3D staSat = transitPV.getPosition().subtract(common.getStationDownlink().getPosition()); + + // Compute azimuth/elevation + final double baseAzimuth = FastMath.atan2(staSat.dotProduct(east), staSat.dotProduct(north)); + final double twoPiWrap = MathUtils.normalizeAngle(baseAzimuth, getObservedValue()[0]) - baseAzimuth; + final double azimuth = baseAzimuth + twoPiWrap; + final double elevation = FastMath.asin(staSat.dotProduct(zenith) / staSat.getNorm()); + + // Prepare the estimation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getTransitState() + }, new TimeStampedPVCoordinates[] { + transitPV, + common.getStationDownlink() + }); + + // azimuth - elevation values + estimated.setEstimatedValue(azimuth, elevation); + + return estimated; + } /** {@inheritDoc} */ @@ -98,55 +115,17 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iter // - 0..2 - Position of the spacecraft in inertial frame // - 3..5 - Velocity of the spacecraft in inertial frame // - 6..n - station parameters (clock offset, station offsets, pole, prime meridian...) + final GroundReceiverCommonParametersWithDerivatives common = computeCommonParametersWithDerivatives(state); + final TimeStampedFieldPVCoordinates transitPV = common.getTransitPV(); - // Get the number of parameters used for derivation - // Place the selected drivers into a map - int nbParams = 6; - final Map indices = new HashMap<>(); - for (ParameterDriver driver : getParametersDrivers()) { - if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); - } - } - final Field field = GradientField.getField(nbParams); - final FieldVector3D zero = FieldVector3D.getZero(field); - - // Coordinates of the spacecraft expressed as a gradient - final TimeStampedFieldPVCoordinates pvaDS = getCoordinates(state, 0, nbParams); - - // Transform between station and inertial frame, expressed as a gradient - // The components of station's position in offset frame are the 3 last derivative parameters - final FieldTransform offsetToInertialDownlink = - station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); - final FieldAbsoluteDate downlinkDateDS = - offsetToInertialDownlink.getFieldDate(); - - // Station position/velocity in inertial frame at end of the downlink leg - final TimeStampedFieldPVCoordinates stationDownlink = - offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS, - zero, zero, zero)); // Station topocentric frame (east-north-zenith) in inertial frame expressed as Gradient - final FieldVector3D east = offsetToInertialDownlink.transformVector(FieldVector3D.getPlusI(field)); - final FieldVector3D north = offsetToInertialDownlink.transformVector(FieldVector3D.getPlusJ(field)); - final FieldVector3D zenith = offsetToInertialDownlink.transformVector(FieldVector3D.getPlusK(field)); - - // Compute propagation times - // (if state has already been set up to pre-compensate propagation delay, - // we will have delta == tauD and transitState will be the same as state) - - // Downlink delay - final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS); - - // Transit state - final Gradient delta = downlinkDateDS.durationFrom(state.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); - final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue()); - - // Transit state (re)computed with derivative structures - final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD); + final GradientField field = common.getTauD().getField(); + final FieldVector3D east = common.getOffsetToInertialDownlink().transformVector(FieldVector3D.getPlusI(field)); + final FieldVector3D north = common.getOffsetToInertialDownlink().transformVector(FieldVector3D.getPlusJ(field)); + final FieldVector3D zenith = common.getOffsetToInertialDownlink().transformVector(FieldVector3D.getPlusK(field)); // Station-satellite vector expressed in inertial frame - final FieldVector3D staSat = transitStateDS.getPosition().subtract(stationDownlink.getPosition()); + final FieldVector3D staSat = transitPV.getPosition().subtract(common.getStationDownlink().getPosition()); // Compute azimuth/elevation final Gradient baseAzimuth = staSat.dotProduct(east).atan2(staSat.dotProduct(north)); @@ -159,10 +138,10 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iter final EstimatedMeasurement estimated = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { - transitState + common.getTransitState() }, new TimeStampedPVCoordinates[] { - transitStateDS.toTimeStampedPVCoordinates(), - stationDownlink.toTimeStampedPVCoordinates() + transitPV.toTimeStampedPVCoordinates(), + common.getStationDownlink().toTimeStampedPVCoordinates() }); // azimuth - elevation values @@ -178,12 +157,26 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iter // Set partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, azDerivatives[index], elDerivatives[index]); + + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = common.getIndices().get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), azDerivatives[index], elDerivatives[index]); + } } } return estimated; + } + + /** Calculate the Line Of Sight of the given measurement. + * @param outputFrame output frame of the line of sight vector + * @return Vector3D the line of Sight of the measurement + */ + public Vector3D getObservedLineOfSight(final Frame outputFrame) { + return getStation().getBaseFrame().getStaticTransformTo(outputFrame, getDate()) + .transformVector(new Vector3D(MathUtils.SEMI_PI - getObservedValue()[0], getObservedValue()[1])); + } + } diff --git a/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java b/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java index 3dabcb8ed0..236bd79e1e 100644 --- a/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java +++ b/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,24 +17,22 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.MathUtils; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; -/** Class modeling an Right Ascension - Declination measurement from a ground point (station, telescope). +/** Class modeling a Right Ascension - Declination measurement from a ground point (station, telescope). * The angles are given in an inertial reference frame. * The motion of the spacecraft during the signal flight time is taken into * account. The date of the measurement corresponds to the reception on @@ -44,14 +42,11 @@ * @author Maxime Journot * @since 9.0 */ -public class AngularRaDec extends AbstractMeasurement { +public class AngularRaDec extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "AngularRaDec"; - /** Ground station from which measurement is performed. */ - private final GroundStation station; - /** Reference frame in which the right ascension - declination angles are given. */ private final Frame referenceFrame; @@ -68,28 +63,10 @@ public class AngularRaDec extends AbstractMeasurement { public AngularRaDec(final GroundStation station, final Frame referenceFrame, final AbsoluteDate date, final double[] angular, final double[] sigma, final double[] baseWeight, final ObservableSatellite satellite) { - super(date, angular, sigma, baseWeight, Collections.singletonList(satellite)); - addParameterDriver(station.getClockOffsetDriver()); - addParameterDriver(station.getEastOffsetDriver()); - addParameterDriver(station.getNorthOffsetDriver()); - addParameterDriver(station.getZenithOffsetDriver()); - addParameterDriver(station.getPrimeMeridianOffsetDriver()); - addParameterDriver(station.getPrimeMeridianDriftDriver()); - addParameterDriver(station.getPolarOffsetXDriver()); - addParameterDriver(station.getPolarDriftXDriver()); - addParameterDriver(station.getPolarOffsetYDriver()); - addParameterDriver(station.getPolarDriftYDriver()); - this.station = station; + super(station, false, date, angular, sigma, baseWeight, satellite); this.referenceFrame = referenceFrame; } - /** Get the ground station from which measurement is performed. - * @return ground station from which measurement is performed - */ - public GroundStation getStation() { - return station; - } - /** Get the reference frame in which the right ascension - declination angles are given. * @return reference frame in which the right ascension - declination angles are given */ @@ -97,6 +74,48 @@ public Frame getReferenceFrame() { return referenceFrame; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final GroundReceiverCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states[0]); + final TimeStampedPVCoordinates transitPV = common.getTransitPV(); + + // Station-satellite vector expressed in inertial frame + final Vector3D staSatInertial = transitPV.getPosition().subtract(common.getStationDownlink().getPosition()); + + // Field transform from inertial to reference frame at station's reception date + final StaticTransform inertialToReferenceDownlink = common.getState().getFrame(). + getStaticTransformTo(referenceFrame, common.getStationDownlink().getDate()); + + // Station-satellite vector in reference frame + final Vector3D staSatReference = inertialToReferenceDownlink.transformVector(staSatInertial); + + // Compute right ascension and declination + final double baseRightAscension = staSatReference.getAlpha(); + final double twoPiWrap = MathUtils.normalizeAngle(baseRightAscension, getObservedValue()[0]) - baseRightAscension; + final double rightAscension = baseRightAscension + twoPiWrap; + final double declination = staSatReference.getDelta(); + + // Prepare the estimation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getTransitState() + }, new TimeStampedPVCoordinates[] { + transitPV, + common.getStationDownlink() + }); + + // azimuth - elevation values + estimated.setEstimatedValue(rightAscension, declination); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, @@ -112,54 +131,15 @@ protected EstimatedMeasurement theoreticalEvaluation(final int ite // - 0..2 - Position of the spacecraft in inertial frame // - 3..5 - Velocity of the spacecraft in inertial frame // - 6..n - station parameters (clock offset, station offsets, pole, prime meridian...) - - // Get the number of parameters used for derivation - // Place the selected drivers into a map - int nbParams = 6; - final Map indices = new HashMap<>(); - for (ParameterDriver driver : getParametersDrivers()) { - if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); - } - } - final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); - - // Coordinates of the spacecraft expressed as a gradient - final TimeStampedFieldPVCoordinates pvaDS = getCoordinates(state, 0, nbParams); - - // Transform between station and inertial frame, expressed as a gradient - // The components of station's position in offset frame are the 3 last derivative parameters - final FieldTransform offsetToInertialDownlink = - station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); - final FieldAbsoluteDate downlinkDateDS = - offsetToInertialDownlink.getFieldDate(); - - // Station position/velocity in inertial frame at end of the downlink leg - final TimeStampedFieldPVCoordinates stationDownlink = - offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS, - zero, zero, zero)); - - // Compute propagation times - // (if state has already been set up to pre-compensate propagation delay, - // we will have delta == tauD and transitState will be the same as state) - - // Downlink delay - final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS); - - // Transit state - final Gradient delta = downlinkDateDS.durationFrom(state.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); - final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue()); - - // Transit state (re)computed with gradients - final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD); + final GroundReceiverCommonParametersWithDerivatives common = computeCommonParametersWithDerivatives(state); + final TimeStampedFieldPVCoordinates transitPV = common.getTransitPV(); // Station-satellite vector expressed in inertial frame - final FieldVector3D staSatInertial = transitStateDS.getPosition().subtract(stationDownlink.getPosition()); + final FieldVector3D staSatInertial = transitPV.getPosition().subtract(common.getStationDownlink().getPosition()); // Field transform from inertial to reference frame at station's reception date - final FieldTransform inertialToReferenceDownlink = - state.getFrame().getTransformTo(referenceFrame, downlinkDateDS); + final FieldStaticTransform inertialToReferenceDownlink = + state.getFrame().getStaticTransformTo(referenceFrame, common.getStationDownlink().getDate()); // Station-satellite vector in reference frame final FieldVector3D staSatReference = inertialToReferenceDownlink.transformVector(staSatInertial); @@ -175,10 +155,10 @@ protected EstimatedMeasurement theoreticalEvaluation(final int ite final EstimatedMeasurement estimated = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { - transitState + common.getTransitState() }, new TimeStampedPVCoordinates[] { - transitStateDS.toTimeStampedPVCoordinates(), - stationDownlink.toTimeStampedPVCoordinates() + transitPV.toTimeStampedPVCoordinates(), + common.getStationDownlink().toTimeStampedPVCoordinates() }); // azimuth - elevation values @@ -194,12 +174,25 @@ protected EstimatedMeasurement theoreticalEvaluation(final int ite // Partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, raDerivatives[index], decDerivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = common.getIndices().get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), raDerivatives[index], decDerivatives[index]); + } } } return estimated; + + } + + /** Calculate the Line Of Sight of the given measurement. + * @param outputFrame output frame of the line of sight vector + * @return Vector3D the line of Sight of the measurement + * @since 12.0 + */ + public Vector3D getObservedLineOfSight(final Frame outputFrame) { + return referenceFrame.getStaticTransformTo(outputFrame, getDate()) + .transformVector(new Vector3D(getObservedValue()[0], getObservedValue()[1])); } } diff --git a/src/main/java/org/orekit/estimation/measurements/BistaticRange.java b/src/main/java/org/orekit/estimation/measurements/BistaticRange.java index 34e9b794cb..c942204789 100644 --- a/src/main/java/org/orekit/estimation/measurements/BistaticRange.java +++ b/src/main/java/org/orekit/estimation/measurements/BistaticRange.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Mark Rutten +/* Copyright 2002-2023 Mark Rutten * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,19 +17,21 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.frames.FieldTransform; +import org.orekit.frames.Transform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -51,7 +53,7 @@ * @author Mark Rutten * @since 11.2 */ -public class BistaticRange extends AbstractMeasurement { +public class BistaticRange extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "BistaticRange"; @@ -61,11 +63,6 @@ public class BistaticRange extends AbstractMeasurement { */ private final GroundStation emitter; - /** - * Ground station from which measurement is performed. - */ - private final GroundStation receiver; - /** * Simple constructor. * @@ -81,7 +78,8 @@ public class BistaticRange extends AbstractMeasurement { public BistaticRange(final GroundStation emitter, final GroundStation receiver, final AbsoluteDate date, final double range, final double sigma, final double baseWeight, final ObservableSatellite satellite) { - super(date, range, sigma, baseWeight, Collections.singletonList(satellite)); + super(receiver, true, date, range, sigma, baseWeight, satellite); + addParameterDriver(emitter.getClockOffsetDriver()); addParameterDriver(emitter.getEastOffsetDriver()); addParameterDriver(emitter.getNorthOffsetDriver()); @@ -93,27 +91,105 @@ public BistaticRange(final GroundStation emitter, final GroundStation receiver, addParameterDriver(emitter.getPolarOffsetYDriver()); addParameterDriver(emitter.getPolarDriftYDriver()); - addParameterDriver(receiver.getClockOffsetDriver()); - addParameterDriver(receiver.getEastOffsetDriver()); - addParameterDriver(receiver.getNorthOffsetDriver()); - addParameterDriver(receiver.getZenithOffsetDriver()); - addParameterDriver(receiver.getPrimeMeridianOffsetDriver()); - addParameterDriver(receiver.getPrimeMeridianDriftDriver()); - addParameterDriver(receiver.getPolarOffsetXDriver()); - addParameterDriver(receiver.getPolarDriftXDriver()); - addParameterDriver(receiver.getPolarOffsetYDriver()); - addParameterDriver(receiver.getPolarDriftYDriver()); - this.emitter = emitter; - this.receiver = receiver; + } + /** Get the emitter ground station. + * @return emitter ground station + */ public GroundStation getEmitterStation() { return emitter; } + /** Get the receiver ground station. + * @return receiver ground station + */ public GroundStation getReceiverStation() { - return receiver; + return getStation(); + } + + /** + * {@inheritDoc} + */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState state = states[0]; + + // Coordinates of the spacecraft + final TimeStampedPVCoordinates pva = state.getPVCoordinates(); + + // transform between station and inertial frame, expressed as a gradient + // The components of station's position in offset frame are the 3 last derivative parameters + final Transform offsetToInertialRx = getReceiverStation().getOffsetToInertial(state.getFrame(), getDate(), false); + final AbsoluteDate downlinkDate = offsetToInertialRx.getDate(); + + // Station position in inertial frame at end of the downlink leg + final TimeStampedPVCoordinates stationReceiver = + offsetToInertialRx.transformPVCoordinates(new TimeStampedPVCoordinates(downlinkDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Compute propagation times + // (if state has already been set up to pre-compensate propagation delay, + // we will have delta == tauD and transitState will be the same as state) + + // Downlink delay + final double tauD = signalTimeOfFlight(pva, stationReceiver.getPosition(), downlinkDate); + + // Transit state & Transit state (re)computed with gradients + final double delta = downlinkDate.durationFrom(state.getDate()); + final double deltaMTauD = delta - tauD; + final SpacecraftState transitState = state.shiftedBy(deltaMTauD); + final TimeStampedPVCoordinates transitStateDS = pva.shiftedBy(deltaMTauD); + + // transform between secondary station topocentric frame (east-north-zenith) and inertial frame expressed as gradients + // The components of secondary station's position in offset frame are the 3 last derivative parameters + final AbsoluteDate transitDate = downlinkDate.shiftedBy(-tauD); + final Transform offsetToInertialTxApprox = getEmitterStation().getOffsetToInertial(state.getFrame(), transitDate, true); + + // Secondary station PV in inertial frame at transit time + final TimeStampedPVCoordinates transmitApprox = + offsetToInertialTxApprox.transformPVCoordinates(new TimeStampedPVCoordinates(transitDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Uplink time of flight from secondary station to transit state of leg2 + final double tauU = signalTimeOfFlight(transmitApprox, transitStateDS.getPosition(), transitStateDS.getDate()); + + // Total time of flight + final double tauTotal = tauU - deltaMTauD; + + // Absolute date of transmission + final AbsoluteDate transmitDate = downlinkDate.shiftedBy(tauTotal); + final Transform transmitToInert = emitter.getOffsetToInertial(state.getFrame(), transmitDate, true); + + // Secondary station PV in inertial frame at rebound date on secondary station + final TimeStampedPVCoordinates stationTransmitter = + transmitToInert.transformPVCoordinates(new TimeStampedPVCoordinates(transmitDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, + iteration, evaluation, + new SpacecraftState[] { + transitState + }, + new TimeStampedPVCoordinates[] { + stationReceiver, + transitStateDS, + stationTransmitter + }); + + // Range value + final double tau = tauD + tauU; + final double range = tau * Constants.SPEED_OF_LIGHT; + + estimated.setEstimatedValue(range); + + return estimated; } /** @@ -138,7 +214,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int it final Map indices = new HashMap<>(); for (ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } } } final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); @@ -149,7 +229,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int it // transform between station and inertial frame, expressed as a gradient // The components of station's position in offset frame are the 3 last derivative parameters final FieldTransform offsetToInertialRx = - receiver.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); + getReceiverStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); final FieldAbsoluteDate downlinkDateDS = offsetToInertialRx.getFieldDate(); // Station position in inertial frame at end of the downlink leg @@ -174,7 +254,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int it // The components of secondary station's position in offset frame are the 3 last derivative parameters final FieldAbsoluteDate transitDate = downlinkDateDS.shiftedBy(tauD.negate()); final FieldTransform offsetToInertialTxApprox = - emitter.getOffsetToInertial(state.getFrame(), transitDate, nbParams, indices); + getEmitterStation().getOffsetToInertial(state.getFrame(), transitDate, nbParams, indices); // Secondary station PV in inertial frame at transit time final TimeStampedFieldPVCoordinates transmitApprox = @@ -222,9 +302,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int it // set partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java b/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java index ad4667d660..cf8637d995 100644 --- a/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java +++ b/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,18 +17,20 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.frames.FieldTransform; +import org.orekit.frames.Transform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -54,7 +56,7 @@ * @author Pascal Parraud * @since 11.2 */ -public class BistaticRangeRate extends AbstractMeasurement { +public class BistaticRangeRate extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "BistaticRangeRate"; @@ -62,9 +64,6 @@ public class BistaticRangeRate extends AbstractMeasurement { /** Emitter ground station. */ private final GroundStation emitter; - /** Receiver ground station. */ - private final GroundStation receiver; - /** Simple constructor. * @param emitter emitter ground station * @param receiver receiver ground station @@ -77,7 +76,8 @@ public class BistaticRangeRate extends AbstractMeasurement { public BistaticRangeRate(final GroundStation emitter, final GroundStation receiver, final AbsoluteDate date, final double rangeRate, final double sigma, final double baseWeight, final ObservableSatellite satellite) { - super(date, rangeRate, sigma, baseWeight, Collections.singletonList(satellite)); + super(receiver, true, date, rangeRate, sigma, baseWeight, satellite); + // add parameter drivers for the emitter, clock offset is not used addParameterDriver(emitter.getEastOffsetDriver()); addParameterDriver(emitter.getNorthOffsetDriver()); @@ -88,19 +88,9 @@ public BistaticRangeRate(final GroundStation emitter, final GroundStation receiv addParameterDriver(emitter.getPolarDriftXDriver()); addParameterDriver(emitter.getPolarOffsetYDriver()); addParameterDriver(emitter.getPolarDriftYDriver()); - // add parameter drivers for the receiver - addParameterDriver(receiver.getClockOffsetDriver()); - addParameterDriver(receiver.getEastOffsetDriver()); - addParameterDriver(receiver.getNorthOffsetDriver()); - addParameterDriver(receiver.getZenithOffsetDriver()); - addParameterDriver(receiver.getPrimeMeridianOffsetDriver()); - addParameterDriver(receiver.getPrimeMeridianDriftDriver()); - addParameterDriver(receiver.getPolarOffsetXDriver()); - addParameterDriver(receiver.getPolarDriftXDriver()); - addParameterDriver(receiver.getPolarOffsetYDriver()); - addParameterDriver(receiver.getPolarDriftYDriver()); + this.emitter = emitter; - this.receiver = receiver; + } /** Get the emitter ground station. @@ -114,7 +104,83 @@ public GroundStation getEmitterStation() { * @return receiver ground station */ public GroundStation getReceiverStation() { - return receiver; + return getStation(); + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState state = states[0]; + + // coordinates of the spacecraft + final TimeStampedPVCoordinates pva = state.getPVCoordinates(); + + // transform between receiver station frame and inertial frame + // at the real date of measurement, i.e. taking station clock offset into account + final Transform receiverToInertial = getReceiverStation().getOffsetToInertial(state.getFrame(), getDate(), false); + final AbsoluteDate measurementDate = receiverToInertial.getDate(); + + // Receiver PV in inertial frame at the end of the downlink leg + final TimeStampedPVCoordinates receiverPV = + receiverToInertial.transformPVCoordinates(new TimeStampedPVCoordinates(measurementDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Compute propagation times + // (if state has already been set up to pre-compensate propagation delay, + // we will have delta == tauD and transitState will be the same as state) + + // Downlink delay + final double tauD = signalTimeOfFlight(pva, receiverPV.getPosition(), measurementDate); + final double delta = measurementDate.durationFrom(state.getDate()); + final double deltaMTauD = delta - tauD; + + // Transit state + final SpacecraftState transitState = state.shiftedBy(deltaMTauD); + + // Transit PV + final TimeStampedPVCoordinates transitPV = pva.shiftedBy(deltaMTauD); + + // Downlink range-rate + final EstimatedMeasurementBase evalDownlink = + oneWayTheoreticalEvaluation(iteration, evaluation, true, + receiverPV, transitPV, transitState); + + // transform between emitter station frame and inertial frame at the transit date + // clock offset from receiver is already compensated + final Transform emitterToInertial = getEmitterStation().getOffsetToInertial(state.getFrame(), transitPV.getDate(), true); + + // emitter PV in inertial frame at the end of the uplink leg + final TimeStampedPVCoordinates emitterPV = + emitterToInertial.transformPVCoordinates(new TimeStampedPVCoordinates(transitPV.getDate(), + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Uplink delay + final double tauU = signalTimeOfFlight(emitterPV, transitPV.getPosition(), transitPV.getDate()); + + // emitter position in inertial frame at the end of the uplink leg + final TimeStampedPVCoordinates emitterUplink = emitterPV.shiftedBy(-tauU); + + // Uplink range-rate + final EstimatedMeasurementBase evalUplink = + oneWayTheoreticalEvaluation(iteration, evaluation, false, + emitterUplink, transitPV, transitState); + + // combine uplink and downlink values + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + evalDownlink.getStates(), + new TimeStampedPVCoordinates[] { + evalUplink.getParticipants()[0], + evalDownlink.getParticipants()[0], + evalDownlink.getParticipants()[1] + }); + estimated.setEstimatedValue(evalDownlink.getEstimatedValue()[0] + evalUplink.getEstimatedValue()[0]); + + return estimated; + } /** {@inheritDoc} */ @@ -139,8 +205,13 @@ protected EstimatedMeasurement theoreticalEvaluation(final in // we have to check for duplicate keys because emitter and receiver stations share // pole and prime meridian parameters names that must be considered // as one set only (they are combined together by the estimation engine) - if (driver.isSelected() && !indices.containsKey(driver.getName())) { - indices.put(driver.getName(), nbParams++); + if (driver.isSelected()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } } } final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); @@ -151,7 +222,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final in // transform between receiver station frame and inertial frame // at the real date of measurement, i.e. taking station clock offset into account final FieldTransform receiverToInertial = - receiver.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); + getReceiverStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); final FieldAbsoluteDate measurementDateG = receiverToInertial.getFieldDate(); // Receiver PV in inertial frame at the end of the downlink leg @@ -182,7 +253,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final in // transform between emitter station frame and inertial frame at the transit date // clock offset from receiver is already compensated final FieldTransform emitterToInertial = - emitter.getOffsetToInertial(state.getFrame(), transitPV.getDate(), nbParams, indices); + getEmitterStation().getOffsetToInertial(state.getFrame(), transitPV.getDate(), nbParams, indices); // emitter PV in inertial frame at the end of the uplink leg final TimeStampedFieldPVCoordinates emitterPV = @@ -224,19 +295,66 @@ protected EstimatedMeasurement theoreticalEvaluation(final in // combine uplink and downlink partial derivatives with respect to parameters evalDownlink.getDerivativesDrivers().forEach(driver -> { - final double[] pd1 = evalDownlink.getParameterDerivatives(driver); - final double[] pd2 = evalUplink.getParameterDerivatives(driver); - final double[] pd = new double[pd1.length]; - for (int i = 0; i < pd.length; ++i) { - pd[i] = pd1[i] + pd2[i]; + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final double[] pd1 = evalDownlink.getParameterDerivatives(driver, span.getStart()); + final double[] pd2 = evalUplink.getParameterDerivatives(driver, span.getStart()); + final double[] pd = new double[pd1.length]; + for (int i = 0; i < pd.length; ++i) { + pd[i] = pd1[i] + pd2[i]; + } + estimated.setParameterDerivatives(driver, span.getStart(), pd); } - estimated.setParameterDerivatives(driver, pd); }); return estimated; } + /** Evaluate range rate measurement in one-way without derivatives. + * @param iteration iteration number + * @param evaluation evaluations counter + * @param downlink indicator for downlink leg + * @param stationPV station coordinates when signal is at station + * @param transitPV spacecraft coordinates at onboard signal transit + * @param transitState orbital state at onboard signal transit + * @return theoretical value for the current leg + * @since 12.0 + */ + private EstimatedMeasurementBase oneWayTheoreticalEvaluation(final int iteration, final int evaluation, + final boolean downlink, + final TimeStampedPVCoordinates stationPV, + final TimeStampedPVCoordinates transitPV, + final SpacecraftState transitState) { + + // prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + transitState + }, new TimeStampedPVCoordinates[] { + downlink ? transitPV : stationPV, + downlink ? stationPV : transitPV + }); + + // range rate value + final Vector3D stationPosition = stationPV.getPosition(); + final Vector3D relativePosition = stationPosition.subtract(transitPV.getPosition()); + + final Vector3D stationVelocity = stationPV.getVelocity(); + final Vector3D relativeVelocity = stationVelocity.subtract(transitPV.getVelocity()); + + // radial direction + final Vector3D lineOfSight = relativePosition.normalize(); + + // range rate + final double rangeRate = Vector3D.dotProduct(relativeVelocity, lineOfSight); + + estimated.setEstimatedValue(rangeRate); + + return estimated; + + } /** Evaluate range rate measurement in one-way. * @param iteration iteration number @@ -285,13 +403,16 @@ private EstimatedMeasurement oneWayTheoreticalEvaluation(fina // set partial derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } return estimated; } + } diff --git a/src/main/java/org/orekit/estimation/measurements/ComparableMeasurement.java b/src/main/java/org/orekit/estimation/measurements/ComparableMeasurement.java index 95b48f94c3..b9ba388bc2 100644 --- a/src/main/java/org/orekit/estimation/measurements/ComparableMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/ComparableMeasurement.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java b/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java index 5627f880c7..9222f17365 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,6 +30,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; @@ -290,6 +291,33 @@ public > FieldTransform getTransform(final } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + + // take parametric prime meridian shift into account + final T theta = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver); + final FieldStaticTransform meridianShift = FieldStaticTransform.of( + date, + new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), theta, RotationConvention.FRAME_TRANSFORM) + ); + + // take parametric pole shift into account + final T xpNeg = linearModel(date, polarOffsetXDriver, polarDriftXDriver).negate(); + final T ypNeg = linearModel(date, polarOffsetYDriver, polarDriftYDriver).negate(); + final FieldStaticTransform poleShift = FieldStaticTransform.compose( + date, + FieldStaticTransform.of( + date, + new FieldRotation<>(FieldVector3D.getPlusJ(date.getField()), xpNeg, RotationConvention.FRAME_TRANSFORM)), + FieldStaticTransform.of( + date, + new FieldRotation<>(FieldVector3D.getPlusI(date.getField()), ypNeg, RotationConvention.FRAME_TRANSFORM))); + + return FieldStaticTransform.compose(date, meridianShift, poleShift); + + } + /** Get the transform with derivatives. * @param date date of the transform * @param freeParameters total number of free parameters in the gradient @@ -305,15 +333,15 @@ public FieldTransform getTransform(final FieldAbsoluteDate d final Gradient theta = linearModel(freeParameters, date, primeMeridianOffsetDriver, primeMeridianDriftDriver, indices); - final Gradient thetaDot = primeMeridianDriftDriver.getValue(freeParameters, indices); + final Gradient thetaDot = primeMeridianDriftDriver.getValue(freeParameters, indices, date.toAbsoluteDate()); // pole shift parameters final Gradient xpNeg = linearModel(freeParameters, date, polarOffsetXDriver, polarDriftXDriver, indices).negate(); final Gradient ypNeg = linearModel(freeParameters, date, polarOffsetYDriver, polarDriftYDriver, indices).negate(); - final Gradient xpNegDot = polarDriftXDriver.getValue(freeParameters, indices).negate(); - final Gradient ypNegDot = polarDriftYDriver.getValue(freeParameters, indices).negate(); + final Gradient xpNegDot = polarDriftXDriver.getValue(freeParameters, indices, date.toAbsoluteDate()).negate(); + final Gradient ypNegDot = polarDriftYDriver.getValue(freeParameters, indices, date.toAbsoluteDate()).negate(); return getTransform(date, theta, thetaDot, xpNeg, xpNegDot, ypNeg, ypNegDot); @@ -415,8 +443,8 @@ private Gradient linearModel(final int freeParameters, final FieldAbsoluteDate the type of the measurement * @author Luc Maisonobe * @since 8.0 */ -public class EstimatedMeasurement> implements ComparableMeasurement { - - /** Associated observed measurement. */ - private final T observedMeasurement; - - /** Iteration number. */ - private final int iteration; - - /** Evaluations counter. */ - private final int count; - - /** States of the spacecrafts. */ - private final SpacecraftState[] states; - - /** Coordinates of the participants in signal travel order. */ - private final TimeStampedPVCoordinates[] participants; - - /** Estimated value. */ - private double[] estimatedValue; - - /** Measurement status. */ - private Status status; +public class EstimatedMeasurement> extends EstimatedMeasurementBase { /** Partial derivatives with respect to states. */ private double[][][] stateDerivatives; /** Partial derivatives with respect to parameters. */ - private final Map parametersDerivatives; + private final Map> parametersDerivatives; /** Simple constructor. * @param observedMeasurement associated observed measurement @@ -73,110 +55,9 @@ public EstimatedMeasurement(final T observedMeasurement, final int iteration, final int count, final SpacecraftState[] states, final TimeStampedPVCoordinates[] participants) { - this.observedMeasurement = observedMeasurement; - this.iteration = iteration; - this.count = count; - this.states = states.clone(); - this.participants = participants.clone(); - this.status = Status.PROCESSED; + super(observedMeasurement, iteration, count, states, participants); this.stateDerivatives = new double[states.length][][]; - this.parametersDerivatives = new IdentityHashMap(); - } - - /** Get the associated observed measurement. - * @return associated observed measurement - */ - public T getObservedMeasurement() { - return observedMeasurement; - } - - /** {@inheritDoc} */ - @Override - public AbsoluteDate getDate() { - return observedMeasurement.getDate(); - } - - /** Get the iteration number. - * @return iteration number - */ - public int getIteration() { - return iteration; - } - - /** Get the evaluations counter. - * @return evaluations counter - */ - public int getCount() { - return count; - } - - /** Get the states of the spacecrafts. - * @return states of the spacecrafts - */ - public SpacecraftState[] getStates() { - return states.clone(); - } - - /** Get the coordinates of the measurements participants in signal travel order. - *

    - * First participant (at index 0) emits the signal (it is for example a ground - * station for two-way range measurement). Last participant receives the signal - * (it is also the ground station for two-way range measurement, but a few - * milliseconds later). Intermediate participants relfect the signal (it is the - * spacecraft for two-way range measurement). - *

    - * @return coordinates of the measurements participants in signal travel order - * in inertial frame - */ - public TimeStampedPVCoordinates[] getParticipants() { - return participants.clone(); - } - - /** Get the time offset from first state date to measurement date. - * @return time offset from first state date to measurement date - */ - public double getTimeOffset() { - return observedMeasurement.getDate().durationFrom(states[0].getDate()); - } - - /** {@inheritDoc} */ - @Override - public double[] getObservedValue() { - return observedMeasurement.getObservedValue(); - } - - /** Get the estimated value. - * @return estimated value - */ - public double[] getEstimatedValue() { - return estimatedValue.clone(); - } - - /** Set the estimated value. - * @param estimatedValue estimated value - */ - public void setEstimatedValue(final double... estimatedValue) { - this.estimatedValue = estimatedValue.clone(); - } - - /** Get the status. - *

    - * The status is set to {@link Status#PROCESSED PROCESSED} at construction, and - * can be reset to {@link Status#REJECTED REJECTED} later on, typically by - * {@link org.orekit.estimation.measurements.modifiers.OutlierFilter OutlierFilter} - * or {@link org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter DynamicOutlierFilter} - *

    - * @return status - */ - public Status getStatus() { - return status; - } - - /** Set the status. - * @param status status to set - */ - public void setStatus(final Status status) { - this.status = status; + this.parametersDerivatives = new IdentityHashMap>(); } /** Get state size. @@ -199,8 +80,8 @@ public int getStateSize() { * {@link ObservedMeasurement#getDimension() dimension} x 6) */ public double[][] getStateDerivatives(final int index) { - final double[][] sd = new double[observedMeasurement.getDimension()][]; - for (int i = 0; i < observedMeasurement.getDimension(); ++i) { + final double[][] sd = new double[getObservedMeasurement().getDimension()][]; + for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) { sd[i] = stateDerivatives[index][i].clone(); } return sd; @@ -213,8 +94,8 @@ public double[][] getStateDerivatives(final int index) { * @param derivatives partial derivatives with respect to state */ public void setStateDerivatives(final int index, final double[]... derivatives) { - this.stateDerivatives[index] = new double[observedMeasurement.getDimension()][]; - for (int i = 0; i < observedMeasurement.getDimension(); ++i) { + this.stateDerivatives[index] = new double[getObservedMeasurement().getDimension()][]; + for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) { this.stateDerivatives[index][i] = derivatives[i].clone(); } } @@ -229,46 +110,99 @@ public Stream getDerivativesDrivers() { /** Get the partial derivatives of the {@link #getEstimatedValue() * simulated measurement} with respect to a parameter. - * @param driver driver for the parameter + * @param driver name of the span of the driver for the parameter for which + * the derivative wants to be known. * @return partial derivatives of the simulated value - * @exception OrekitIllegalArgumentException if parameter is unknown + * @exception OrekitIllegalArgumentException if parameter is unknown or + * OrekitIllegalStateException if this function is used on a PDriver having several + * values driven, in this case the method + * {@link #getParameterDerivatives(ParameterDriver, AbsoluteDate)} must be called */ public double[] getParameterDerivatives(final ParameterDriver driver) throws OrekitIllegalArgumentException { - final double[] p = parametersDerivatives.get(driver); + if (driver.getNbOfValues() == 1) { + final TimeSpanMap p = parametersDerivatives.get(driver); + if (p == null) { + final StringBuilder builder = new StringBuilder(); + for (final Map.Entry> entry : parametersDerivatives.entrySet()) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(entry.getKey()); + } + throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, + driver, + builder.length() > 0 ? builder.toString() : " "); + } + return p.get(AbsoluteDate.ARBITRARY_EPOCH); + } else { + throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, driver.getName(), "getParameterDerivatives(driver, date)"); + } + } + + /** Get the partial derivatives of the {@link #getEstimatedValue() + * simulated measurement} with respect to a parameter. + * @param driver name of the span of the driver for the parameter for which + * the derivative wants to be known. + * @param date date at which the parameter derivatives wants to be known + * @return partial derivatives of the simulated value + * @exception OrekitIllegalArgumentException if parameter is unknown + */ + public double[] getParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date) + throws OrekitIllegalArgumentException { + final TimeSpanMap p = parametersDerivatives.get(driver); if (p == null) { final StringBuilder builder = new StringBuilder(); - for (final Map.Entry entry : parametersDerivatives.entrySet()) { + for (final Map.Entry> entry : parametersDerivatives.entrySet()) { if (builder.length() > 0) { builder.append(", "); } - builder.append(entry.getKey().getName()); + builder.append(entry.getKey()); } throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, - driver.getName(), + driver, builder.length() > 0 ? builder.toString() : ""); } - return p; + return p.get(date); } /** Set the partial derivatives of the {@link #getEstimatedValue() * simulated measurement} with respect to parameter. - * @param driver driver for the parameter + * @param driver name of the span of the driver for the parameter for which + * the derivative wants to be known. + * @param date date at which the parameterDerivative wants to be set * @param parameterDerivatives partial derivatives with respect to parameter */ - public void setParameterDerivatives(final ParameterDriver driver, final double... parameterDerivatives) { - parametersDerivatives.put(driver, parameterDerivatives); - } + public void setParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date, final double... parameterDerivatives) { + if (!parametersDerivatives.containsKey(driver) || parametersDerivatives.get(driver) == null) { + final TimeSpanMap derivativeSpanMap = new TimeSpanMap(parameterDerivatives); + final TimeSpanMap driverNameSpan = driver.getNamesSpanMap(); + for (Span span = driverNameSpan.getSpan(driverNameSpan.getFirstSpan().getEnd()); span != null; span = span.next()) { + derivativeSpanMap.addValidAfter(parameterDerivatives, span.getStart(), false); + } + parametersDerivatives.put(driver, derivativeSpanMap); - /** Enumerate for the status of the measurement. */ - public enum Status { + } else { - /** Status for processed measurements. */ - PROCESSED, + AbsoluteDate dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getStart(); + if (dateToAddAfter.equals(AbsoluteDate.PAST_INFINITY)) { + dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getEnd(); + parametersDerivatives.get(driver).addValidBefore(parameterDerivatives, dateToAddAfter, false); + } else { + parametersDerivatives.get(driver).addValidAfter(parameterDerivatives, dateToAddAfter, false); + } + + } - /** Status for rejected measurements. */ - REJECTED; + } + /** Set the partial derivatives of the {@link #getEstimatedValue() + * simulated measurement} with respect to parameter. + * @param driver driver for the parameter + * @param parameterDerivativesMap partial derivatives with respect to parameter + */ + public void setParameterDerivatives(final ParameterDriver driver, final TimeSpanMap parameterDerivativesMap) { + parametersDerivatives.put(driver, parameterDerivativesMap); } } diff --git a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java new file mode 100644 index 0000000000..f9ef87ca9b --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java @@ -0,0 +1,178 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements; + +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Class holding an estimated theoretical value associated to an {@link ObservedMeasurement observed measurement}. + * @param the type of the measurement + * @author Luc Maisonobe + * @since 8.0 + */ +public class EstimatedMeasurementBase> implements ComparableMeasurement { + + /** Associated observed measurement. */ + private final T observedMeasurement; + + /** Iteration number. */ + private final int iteration; + + /** Evaluations counter. */ + private final int count; + + /** States of the spacecrafts. */ + private final SpacecraftState[] states; + + /** Coordinates of the participants in signal travel order. */ + private final TimeStampedPVCoordinates[] participants; + + /** Estimated value. */ + private double[] estimatedValue; + + /** Measurement status. */ + private Status status; + + /** Simple constructor. + * @param observedMeasurement associated observed measurement + * @param iteration iteration number + * @param count evaluations counter + * @param states states of the spacecrafts + * @param participants coordinates of the participants in signal travel order + * in inertial frame + */ + public EstimatedMeasurementBase(final T observedMeasurement, + final int iteration, final int count, + final SpacecraftState[] states, + final TimeStampedPVCoordinates[] participants) { + this.observedMeasurement = observedMeasurement; + this.iteration = iteration; + this.count = count; + this.states = states.clone(); + this.participants = participants.clone(); + this.status = Status.PROCESSED; + } + + /** Get the associated observed measurement. + * @return associated observed measurement + */ + public T getObservedMeasurement() { + return observedMeasurement; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return observedMeasurement.getDate(); + } + + /** Get the iteration number. + * @return iteration number + */ + public int getIteration() { + return iteration; + } + + /** Get the evaluations counter. + * @return evaluations counter + */ + public int getCount() { + return count; + } + + /** Get the states of the spacecrafts. + * @return states of the spacecrafts + */ + public SpacecraftState[] getStates() { + return states.clone(); + } + + /** Get the coordinates of the measurements participants in signal travel order. + *

    + * First participant (at index 0) emits the signal (it is for example a ground + * station for two-way range measurement). Last participant receives the signal + * (it is also the ground station for two-way range measurement, but a few + * milliseconds later). Intermediate participants relfect the signal (it is the + * spacecraft for two-way range measurement). + *

    + * @return coordinates of the measurements participants in signal travel order + * in inertial frame + */ + public TimeStampedPVCoordinates[] getParticipants() { + return participants.clone(); + } + + /** Get the time offset from first state date to measurement date. + * @return time offset from first state date to measurement date + */ + public double getTimeOffset() { + return observedMeasurement.getDate().durationFrom(states[0].getDate()); + } + + /** {@inheritDoc} */ + @Override + public double[] getObservedValue() { + return observedMeasurement.getObservedValue(); + } + + /** Get the estimated value. + * @return estimated value + */ + public double[] getEstimatedValue() { + return estimatedValue.clone(); + } + + /** Set the estimated value. + * @param estimatedValue estimated value + */ + public void setEstimatedValue(final double... estimatedValue) { + this.estimatedValue = estimatedValue.clone(); + } + + /** Get the status. + *

    + * The status is set to {@link Status#PROCESSED PROCESSED} at construction, and + * can be reset to {@link Status#REJECTED REJECTED} later on, typically by + * {@link org.orekit.estimation.measurements.modifiers.OutlierFilter OutlierFilter} + * or {@link org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter DynamicOutlierFilter} + *

    + * @return status + */ + public Status getStatus() { + return status; + } + + /** Set the status. + * @param status status to set + */ + public void setStatus(final Status status) { + this.status = status; + } + + /** Enumerate for the status of the measurement. */ + public enum Status { + + /** Status for processed measurements. */ + PROCESSED, + + /** Status for rejected measurements. */ + REJECTED; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/EstimationModifier.java b/src/main/java/org/orekit/estimation/measurements/EstimationModifier.java index 62829f1fac..12972ffa53 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimationModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimationModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,7 @@ */ package org.orekit.estimation.measurements; -import java.util.List; - -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** Interface for estimated measurements modifiers used for orbit determination. @@ -40,16 +38,19 @@ * @author Luc Maisonobe * @since 8.0 */ -public interface EstimationModifier> { +public interface EstimationModifier> extends ParameterDriversProvider { - /** Get the drivers for this modifier parameters. - * @return drivers for this modifier parameters + /** Apply a modifier to an estimated measurement without derivatives. + * @param estimated estimated measurement to modify + * @since 12.0 */ - List getParametersDrivers(); + void modifyWithoutDerivatives(EstimatedMeasurementBase estimated); /** Apply a modifier to an estimated measurement. * @param estimated estimated measurement to modify */ - void modify(EstimatedMeasurement estimated); + default void modify(EstimatedMeasurement estimated) { + modifyWithoutDerivatives(estimated); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/EstimationsProvider.java b/src/main/java/org/orekit/estimation/measurements/EstimationsProvider.java index 7d6fce9354..59fbb9d740 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimationsProvider.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimationsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/FDOA.java b/src/main/java/org/orekit/estimation/measurements/FDOA.java new file mode 100644 index 0000000000..ed3aae12f1 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/FDOA.java @@ -0,0 +1,466 @@ +/* Copyright 2002-2023 Mark Rutten + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Transform; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Class modeling a Frequency Difference of Arrival measurement with a satellite as emitter + * and two ground stations as receivers. + *

    + * FDOA measures the difference in signal arrival frequency between the emitter and receivers, + * corresponding to a difference in range-rate from the two receivers to the emitter. + *

    + * The date of the measurement corresponds to the reception of the signal by the prime station. + * The measurement corresponds to the frequency of the signal received at the prime station at + * the date of the measurement minus the frequency of the signal received at the second station: + * fdoa = f1 - f2 + *

    + * The motion of the stations and the satellite during the signal flight time are taken into account. + *

    + * @author Mark Rutten + * @since 12.0 + */ +public class FDOA extends GroundReceiverMeasurement { + + /** Type of the measurement. */ + public static final String MEASUREMENT_TYPE = "FDOA"; + + /** Centre frequency of the signal emitted from the satellite. */ + private final double centreFrequency; + + /** Second ground station, the one that gives the measurement, i.e. the delay. */ + private final GroundStation secondStation; + + /** Simple constructor. + * @param primeStation ground station that gives the date of the measurement + * @param secondStation ground station that gives the measurement + * @param centreFrequency satellite emitter frequency + * @param date date of the measurement + * @param fdoa observed value (s) + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellite satellite related to this measurement + */ + public FDOA(final GroundStation primeStation, final GroundStation secondStation, + final double centreFrequency, + final AbsoluteDate date, final double fdoa, final double sigma, + final double baseWeight, final ObservableSatellite satellite) { + super(primeStation, false, date, fdoa, sigma, baseWeight, satellite); + + // add parameter drivers for the secondary station + addParameterDriver(secondStation.getClockOffsetDriver()); + addParameterDriver(secondStation.getEastOffsetDriver()); + addParameterDriver(secondStation.getNorthOffsetDriver()); + addParameterDriver(secondStation.getZenithOffsetDriver()); + addParameterDriver(secondStation.getPrimeMeridianOffsetDriver()); + addParameterDriver(secondStation.getPrimeMeridianDriftDriver()); + addParameterDriver(secondStation.getPolarOffsetXDriver()); + addParameterDriver(secondStation.getPolarDriftXDriver()); + addParameterDriver(secondStation.getPolarOffsetYDriver()); + addParameterDriver(secondStation.getPolarDriftYDriver()); + this.secondStation = secondStation; + this.centreFrequency = centreFrequency; + } + + /** Get the prime ground station, the one that gives the date of the measurement. + * @return prime ground station + */ + public GroundStation getPrimeStation() { + return getStation(); + } + + /** Get the second ground station, the one that gives the measurement. + * @return second ground station + */ + public GroundStation getSecondStation() { + return secondStation; + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState state = states[0]; + + // coordinates of the spacecraft + final TimeStampedPVCoordinates pva = state.getPVCoordinates(); + + // transform between prime station frame and inertial frame + // at the real date of measurement, i.e. taking station clock offset into account + final Transform primeToInert = getStation().getOffsetToInertial(state.getFrame(), getDate(), false); + final AbsoluteDate measurementDate = primeToInert.getDate(); + + // prime station PV in inertial frame at the real date of the measurement + final TimeStampedPVCoordinates primePV = + primeToInert.transformPVCoordinates(new TimeStampedPVCoordinates(measurementDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // compute downlink delay from emitter to prime receiver + final double tau1 = signalTimeOfFlight(pva, primePV.getPosition(), measurementDate); + + // elapsed time between state date and signal arrival to the prime receiver + final double dtMtau1 = measurementDate.durationFrom(state.getDate()) - tau1; + + // satellite state at signal emission + final SpacecraftState emitterState = state.shiftedBy(dtMtau1); + + // satellite pv at signal emission (re)computed with gradient + final TimeStampedPVCoordinates emitterPV = pva.shiftedBy(dtMtau1); + + // second station PV in inertial frame at real date of signal reception + TimeStampedPVCoordinates secondPV; + // initialize search loop of the reception date by second station + double tau2 = tau1; + double delta; + int count = 0; + do { + final double previous = tau2; + // date of signal arrival on second receiver + final AbsoluteDate dateAt2 = emitterState.getDate().shiftedBy(previous); + // transform between second station frame and inertial frame + // at the date of signal arrival, taking clock offset into account + final Transform secondToInert = secondStation.getOffsetToInertial(state.getFrame(), dateAt2, false); + // second receiver position in inertial frame at the real date of signal reception + secondPV = secondToInert.transformPVCoordinates(new TimeStampedPVCoordinates(secondToInert.getDate(), + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + // downlink delay from emitter to second receiver + tau2 = linkDelay(emitterPV.getPosition(), secondPV.getPosition()); + + // Change in the computed downlink delay + delta = FastMath.abs(tau2 - previous); + } while (count++ < 10 && delta >= 2 * FastMath.ulp(tau2)); + + // The measured TDOA is (tau1 + clockOffset1) - (tau2 + clockOffset2) + final double offset1 = getStation().getClockOffsetDriver().getValue(emitterState.getDate()); + final double offset2 = secondStation.getClockOffsetDriver().getValue(emitterState.getDate()); + final double tdoa = (tau1 + offset1) - (tau2 + offset2); + + // Range-rate sat->primary station + final EstimatedMeasurementBase evalPrimary = oneWayTheoreticalEvaluation(iteration, evaluation, true, + primePV, emitterPV, emitterState); + + // Range-rate sat->secondary station + final EstimatedMeasurementBase evalSecondary = oneWayTheoreticalEvaluation(iteration, evaluation, true, + secondPV, emitterPV, emitterState); + + // Evaluate the FDOA value and derivatives + // ------------------------------------------- + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + emitterState + }, + new TimeStampedPVCoordinates[] { + emitterPV, + tdoa > 0 ? secondPV : primePV, + tdoa > 0 ? primePV : secondPV + }); + + // set FDOA value + final double rangeRateToHz = -centreFrequency / Constants.SPEED_OF_LIGHT; + estimated.setEstimatedValue((evalPrimary.getEstimatedValue()[0] - evalSecondary.getEstimatedValue()[0]) * rangeRateToHz); + + return estimated; + + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState state = states[0]; + + // TDOA derivatives are computed with respect to: + // - Spacecraft state in inertial frame + // - Prime station parameters + // - Second station parameters + // -------------------------- + // - 0..2 - Position of the spacecraft in inertial frame + // - 3..5 - Velocity of the spacecraft in inertial frame + // - 6..n - stations' parameters (clock offset, station offsets, pole, prime meridian...) + int nbParams = 6; + final Map indices = new HashMap<>(); + for (ParameterDriver driver : getParametersDrivers()) { + // we have to check for duplicate keys because primary and secondary station share + // pole and prime meridian parameters names that must be considered + // as one set only (they are combined together by the estimation engine) + if (driver.isSelected()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } + } + } + final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); + + // coordinates of the spacecraft as a gradient + final TimeStampedFieldPVCoordinates pvaG = getCoordinates(state, 0, nbParams); + + // transform between prime station frame and inertial frame + // at the real date of measurement, i.e. taking station clock offset into account + final FieldTransform primeToInert = + getStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); + final FieldAbsoluteDate measurementDateG = primeToInert.getFieldDate(); + + // prime station PV in inertial frame at the real date of the measurement + final TimeStampedFieldPVCoordinates primePV = + primeToInert.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(measurementDateG, + zero, zero, zero)); + + // compute downlink delay from emitter to prime receiver + final Gradient tau1 = signalTimeOfFlight(pvaG, primePV.getPosition(), measurementDateG); + + // elapsed time between state date and signal arrival to the prime receiver + final Gradient dtMtau1 = measurementDateG.durationFrom(state.getDate()).subtract(tau1); + + // satellite state at signal emission + final SpacecraftState emitterState = state.shiftedBy(dtMtau1.getValue()); + + // satellite pv at signal emission (re)computed with gradient + final TimeStampedFieldPVCoordinates emitterPV = pvaG.shiftedBy(dtMtau1); + + // second station PV in inertial frame at real date of signal reception + TimeStampedFieldPVCoordinates secondPV; + // initialize search loop of the reception date by second station + Gradient tau2 = tau1; + double delta; + int count = 0; + do { + final double previous = tau2.getValue(); + // date of signal arrival on second receiver + final AbsoluteDate dateAt2 = emitterState.getDate().shiftedBy(previous); + // transform between second station frame and inertial frame + // at the date of signal arrival, taking clock offset into account + final FieldTransform secondToInert = + secondStation.getOffsetToInertial(state.getFrame(), dateAt2, + nbParams, indices); + // second receiver position in inertial frame at the real date of signal reception + secondPV = secondToInert.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(secondToInert.getFieldDate(), + zero, zero, zero)); + // downlink delay from emitter to second receiver + tau2 = linkDelay(emitterPV.getPosition(), secondPV.getPosition()); + + // Change in the computed downlink delay + delta = FastMath.abs(tau2.getValue() - previous); + } while (count++ < 10 && delta >= 2 * FastMath.ulp(tau2.getValue())); + + // The measured TDOA is (tau1 + clockOffset1) - (tau2 + clockOffset2) + final Gradient offset1 = getStation().getClockOffsetDriver().getValue(nbParams, indices, emitterState.getDate()); + final Gradient offset2 = secondStation.getClockOffsetDriver().getValue(nbParams, indices, emitterState.getDate()); + final Gradient tdoaG = tau1.add(offset1).subtract(tau2.add(offset2)); + final double tdoa = tdoaG.getValue(); + + // Range-rate sat->primary station + final EstimatedMeasurement evalPrimary = oneWayTheoreticalEvaluation(iteration, evaluation, true, + primePV, emitterPV, emitterState, indices); + + // Range-rate sat->secondary station + final EstimatedMeasurement evalSecondary = oneWayTheoreticalEvaluation(iteration, evaluation, true, + secondPV, emitterPV, emitterState, indices); + + // Evaluate the FDOA value and derivatives + // ------------------------------------------- + final TimeStampedPVCoordinates pv1 = primePV.toTimeStampedPVCoordinates(); + final TimeStampedPVCoordinates pv2 = secondPV.toTimeStampedPVCoordinates(); + final EstimatedMeasurement estimated = + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + emitterState + }, + new TimeStampedPVCoordinates[] { + emitterPV.toTimeStampedPVCoordinates(), + tdoa > 0 ? pv2 : pv1, + tdoa > 0 ? pv1 : pv2 + }); + + // set FDOA value + final double rangeRateToHz = -centreFrequency / Constants.SPEED_OF_LIGHT; + estimated.setEstimatedValue((evalPrimary.getEstimatedValue()[0] - evalSecondary.getEstimatedValue()[0]) * rangeRateToHz); + + // combine primary and secondary partial derivatives with respect to state + final double[][] sd1 = evalPrimary.getStateDerivatives(0); + final double[][] sd2 = evalSecondary.getStateDerivatives(0); + final double[][] sd = new double[sd1.length][sd1[0].length]; + for (int i = 0; i < sd.length; ++i) { + for (int j = 0; j < sd[0].length; ++j) { + sd[i][j] = (sd1[i][j] - sd2[i][j]) * rangeRateToHz; + } + } + estimated.setStateDerivatives(0, sd); + + // combine primary and secondary partial derivatives with respect to parameters + evalPrimary.getDerivativesDrivers().forEach(driver -> { + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final double[] pd1 = evalPrimary.getParameterDerivatives(driver, span.getStart()); + final double[] pd2 = evalSecondary.getParameterDerivatives(driver, span.getStart()); + final double[] pd = new double[pd1.length]; + for (int i = 0; i < pd.length; ++i) { + pd[i] = (pd1[i] - pd2[i]) * rangeRateToHz; + } + estimated.setParameterDerivatives(driver, span.getStart(), pd); + } + }); + + return estimated; + + } + + /** Compute propagation delay on a link. + * @param emitter the position of the emitter + * @param receiver the position of the receiver (same frame as emitter) + * @return the propagation delay + */ + private double linkDelay(final Vector3D emitter, + final Vector3D receiver) { + return receiver.distance(emitter) / Constants.SPEED_OF_LIGHT; + } + + /** Compute propagation delay on a link. + * @param emitter the position of the emitter + * @param receiver the position of the receiver (same frame as emitter) + * @return the propagation delay + */ + private Gradient linkDelay(final FieldVector3D emitter, + final FieldVector3D receiver) { + return receiver.distance(emitter).divide(Constants.SPEED_OF_LIGHT); + } + + /** Evaluate range rate measurement in one-way. + * @param iteration iteration number + * @param evaluation evaluations counter + * @param downlink indicator for downlink leg + * @param stationPV station coordinates when signal is at station + * @param transitPV spacecraft coordinates at onboard signal transit + * @param transitState orbital state at onboard signal transit + * @return theoretical value for the current leg + */ + private EstimatedMeasurementBase oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink, + final TimeStampedPVCoordinates stationPV, + final TimeStampedPVCoordinates transitPV, + final SpacecraftState transitState) { + + // prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + transitState + }, new TimeStampedPVCoordinates[] { + downlink ? transitPV : stationPV, + downlink ? stationPV : transitPV + }); + + // range rate value + final Vector3D stationPosition = stationPV.getPosition(); + final Vector3D relativePosition = stationPosition.subtract(transitPV.getPosition()); + + final Vector3D stationVelocity = stationPV.getVelocity(); + final Vector3D relativeVelocity = stationVelocity.subtract(transitPV.getVelocity()); + + // radial direction + final Vector3D lineOfSight = relativePosition.normalize(); + + // range rate + final double rangeRate = Vector3D.dotProduct(relativeVelocity, lineOfSight); + + estimated.setEstimatedValue(rangeRate); + + return estimated; + + } + /** Evaluate range rate measurement in one-way. + * @param iteration iteration number + * @param evaluation evaluations counter + * @param downlink indicator for downlink leg + * @param stationPV station coordinates when signal is at station + * @param transitPV spacecraft coordinates at onboard signal transit + * @param transitState orbital state at onboard signal transit + * @param indices indices of the estimated parameters in derivatives computations + * @return theoretical value for the current leg + */ + private EstimatedMeasurement oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink, + final TimeStampedFieldPVCoordinates stationPV, + final TimeStampedFieldPVCoordinates transitPV, + final SpacecraftState transitState, + final Map indices) { + + // prepare the evaluation + final EstimatedMeasurement estimated = + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + transitState + }, new TimeStampedPVCoordinates[] { + (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(), + (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates() + }); + + // range rate value + final FieldVector3D stationPosition = stationPV.getPosition(); + final FieldVector3D relativePosition = stationPosition.subtract(transitPV.getPosition()); + + final FieldVector3D stationVelocity = stationPV.getVelocity(); + final FieldVector3D relativeVelocity = stationVelocity.subtract(transitPV.getVelocity()); + + // radial direction + final FieldVector3D lineOfSight = relativePosition.normalize(); + + // range rate + final Gradient rangeRate = FieldVector3D.dotProduct(relativeVelocity, lineOfSight); + + estimated.setEstimatedValue(rangeRate.getValue()); + + // compute partial derivatives of (rr) with respect to spacecraft state Cartesian coordinates + final double[] derivatives = rangeRate.getGradient(); + estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); + + // set partial derivatives with respect to parameters + for (final ParameterDriver driver : getParametersDrivers()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } + } + } + + return estimated; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java new file mode 100644 index 0000000000..fe3c550ab4 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java @@ -0,0 +1,127 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements; + +import java.util.Map; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.orekit.frames.FieldTransform; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +/** Common intermediate parameters used to estimate measurements where receiver is a ground station. + * @author Luc Maisonobe + * @since 12.0 + */ +public class GroundReceiverCommonParametersWithDerivatives { + + /** Spacecraft state. */ + private final SpacecraftState state; + + /** Derivatives indices map. */ + private final Map indices; + + /** Transform between station and inertial frame. */ + private final FieldTransform offsetToInertialDownlink; + + /** Station position in inertial frame at end of the downlink leg. */ + private final TimeStampedFieldPVCoordinates stationDownlink; + + /** Downlink delay. */ + private final Gradient tauD; + + /** Transit state. */ + private final SpacecraftState transitState; + + /** Transit state. */ + private final TimeStampedFieldPVCoordinates transitPV; + + /** Simple constructor. + * @param state spacecraft state + * @param indices derivatives indices map + * @param offsetToInertialDownlink transform between station and inertial frame + * @param stationDownlink station position in inertial frame at end of the downlink leg + * @param tauD downlink delay + * @param transitState transit state + * @param transitPV transit position/velocity as a gradient + */ + public GroundReceiverCommonParametersWithDerivatives(final SpacecraftState state, + final Map indices, + final FieldTransform offsetToInertialDownlink, + final TimeStampedFieldPVCoordinates stationDownlink, + final Gradient tauD, + final SpacecraftState transitState, + final TimeStampedFieldPVCoordinates transitPV) { + this.state = state; + this.indices = indices; + this.offsetToInertialDownlink = offsetToInertialDownlink; + this.stationDownlink = stationDownlink; + this.tauD = tauD; + this.transitState = transitState; + this.transitPV = transitPV; + } + + /** Get spacecraft state. + * @return spacecraft state + */ + public SpacecraftState getState() { + return state; + } + + /** Get derivatives indices map. + * @return derivatives indices map + */ + public Map getIndices() { + return indices; + } + + /** Get transform between station and inertial frame. + * @return transform between station and inertial frame + */ + public FieldTransform getOffsetToInertialDownlink() { + return offsetToInertialDownlink; + } + + /** Get station position in inertial frame at end of the downlink leg. + * @return station position in inertial frame at end of the downlink leg + */ + public TimeStampedFieldPVCoordinates getStationDownlink() { + return stationDownlink; + } + + /** Get downlink delay. + * @return ownlink delay + */ + public Gradient getTauD() { + return tauD; + } + + /** Get transit state. + * @return transit state + */ + public SpacecraftState getTransitState() { + return transitState; + } + + /** Get transit position/velocity. + * @return transit position/velocity + */ + public TimeStampedFieldPVCoordinates getTransitPV() { + return transitPV; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java new file mode 100644 index 0000000000..1013b670d3 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java @@ -0,0 +1,111 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements; + +import org.orekit.frames.Transform; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Common intermediate parameters used to estimate measurements where receiver is a ground station. + * @author Luc Maisonobe + * @since 12.0 + */ +public class GroundReceiverCommonParametersWithoutDerivatives { + + /** Spacecraft state. */ + private final SpacecraftState state; + + /** Transform between station and inertial frame. */ + private final Transform offsetToInertialDownlink; + + /** Station position in inertial frame at end of the downlink leg. */ + private final TimeStampedPVCoordinates stationDownlink; + + /** Downlink delay. */ + private final double tauD; + + /** Transit state. */ + private final SpacecraftState transitState; + + /** Transit position/velocity. */ + private final TimeStampedPVCoordinates transitPV; + + /** Simple constructor. + * @param state spacecraft state + * @param offsetToInertialDownlink transform between station and inertial frame + * @param stationDownlink station position in inertial frame at end of the downlink leg + * @param tauD downlink delay + * @param transitState transit state + * @param transitPV transit position/velocity + */ + public GroundReceiverCommonParametersWithoutDerivatives(final SpacecraftState state, + final Transform offsetToInertialDownlink, + final TimeStampedPVCoordinates stationDownlink, + final double tauD, + final SpacecraftState transitState, + final TimeStampedPVCoordinates transitPV) { + this.state = state; + this.offsetToInertialDownlink = offsetToInertialDownlink; + this.stationDownlink = stationDownlink; + this.tauD = tauD; + this.transitState = transitState; + this.transitPV = transitPV; + } + + /** Get spacecraft state. + * @return spacecraft state + */ + public SpacecraftState getState() { + return state; + } + + /** Get transform between station and inertial frame. + * @return transform between station and inertial frame + */ + public Transform getOffsetToInertialDownlink() { + return offsetToInertialDownlink; + } + + /** Get station position in inertial frame at end of the downlink leg. + * @return station position in inertial frame at end of the downlink leg + */ + public TimeStampedPVCoordinates getStationDownlink() { + return stationDownlink; + } + + /** Get downlink delay. + * @return ownlink delay + */ + public double getTauD() { + return tauD; + } + + /** Get transit state. + * @return transit state + */ + public SpacecraftState getTransitState() { + return transitState; + } + + /** Get transit position/velocity. + * @return transit position/velocity + */ + public TimeStampedPVCoordinates getTransitPV() { + return transitPV; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java b/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java new file mode 100644 index 0000000000..bb4abee303 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java @@ -0,0 +1,247 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; +import org.orekit.frames.Transform; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Base class modeling a measurement where receiver is a ground station. + * @author Thierry Ceolin + * @author Luc Maisonobe + * @author Maxime Journot + * @since 12.0 + * @param type of the measurement + */ +public abstract class GroundReceiverMeasurement> extends AbstractMeasurement { + + /** Ground station from which measurement is performed. */ + private final GroundStation station; + + /** Flag indicating whether it is a two-way measurement. */ + private final boolean twoway; + + /** Simple constructor. + * @param station ground station from which measurement is performed + * @param twoWay flag indicating whether it is a two-way measurement + * @param date date of the measurement + * @param observed observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellite satellite related to this measurement + */ + public GroundReceiverMeasurement(final GroundStation station, final boolean twoWay, final AbsoluteDate date, + final double observed, final double sigma, final double baseWeight, + final ObservableSatellite satellite) { + super(date, observed, sigma, baseWeight, Collections.singletonList(satellite)); + addParameterDriver(station.getClockOffsetDriver()); + addParameterDriver(station.getClockDriftDriver()); + addParameterDriver(station.getEastOffsetDriver()); + addParameterDriver(station.getNorthOffsetDriver()); + addParameterDriver(station.getZenithOffsetDriver()); + addParameterDriver(station.getPrimeMeridianOffsetDriver()); + addParameterDriver(station.getPrimeMeridianDriftDriver()); + addParameterDriver(station.getPolarOffsetXDriver()); + addParameterDriver(station.getPolarDriftXDriver()); + addParameterDriver(station.getPolarOffsetYDriver()); + addParameterDriver(station.getPolarDriftYDriver()); + if (!twoWay) { + // for one way measurements, the satellite clock offset affects the measurement + addParameterDriver(satellite.getClockOffsetDriver()); + addParameterDriver(satellite.getClockDriftDriver()); + } + this.station = station; + this.twoway = twoWay; + } + + /** Simple constructor. + * @param station ground station from which measurement is performed + * @param twoWay flag indicating whether it is a two-way measurement + * @param date date of the measurement + * @param observed observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellite satellite related to this measurement + */ + public GroundReceiverMeasurement(final GroundStation station, final boolean twoWay, final AbsoluteDate date, + final double[] observed, final double[] sigma, final double[] baseWeight, + final ObservableSatellite satellite) { + super(date, observed, sigma, baseWeight, Collections.singletonList(satellite)); + addParameterDriver(station.getClockOffsetDriver()); + addParameterDriver(station.getClockDriftDriver()); + addParameterDriver(station.getEastOffsetDriver()); + addParameterDriver(station.getNorthOffsetDriver()); + addParameterDriver(station.getZenithOffsetDriver()); + addParameterDriver(station.getPrimeMeridianOffsetDriver()); + addParameterDriver(station.getPrimeMeridianDriftDriver()); + addParameterDriver(station.getPolarOffsetXDriver()); + addParameterDriver(station.getPolarDriftXDriver()); + addParameterDriver(station.getPolarOffsetYDriver()); + addParameterDriver(station.getPolarDriftYDriver()); + if (!twoWay) { + // for one way measurements, the satellite clock offset affects the measurement + addParameterDriver(satellite.getClockOffsetDriver()); + addParameterDriver(satellite.getClockDriftDriver()); + } + this.station = station; + this.twoway = twoWay; + } + + /** Get the ground station from which measurement is performed. + * @return ground station from which measurement is performed + */ + public GroundStation getStation() { + return station; + } + + /** Check if the instance represents a two-way measurement. + * @return true if the instance represents a two-way measurement + */ + public boolean isTwoWay() { + return twoway; + } + + /** Compute common estimation parameters. + * @param state orbital state at measurement date + * @return common parameters + */ + protected GroundReceiverCommonParametersWithoutDerivatives computeCommonParametersWithout(final SpacecraftState state) { + + // Coordinates of the spacecraft + final TimeStampedPVCoordinates pva = state.getPVCoordinates(); + + // transform between station and inertial frame + final Transform offsetToInertialDownlink = + getStation().getOffsetToInertial(state.getFrame(), getDate(), false); + final AbsoluteDate downlinkDate = offsetToInertialDownlink.getDate(); + + // Station position in inertial frame at end of the downlink leg + final TimeStampedPVCoordinates origin = new TimeStampedPVCoordinates(downlinkDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO); + final TimeStampedPVCoordinates stationDownlink = offsetToInertialDownlink.transformPVCoordinates(origin); + + // Compute propagation times + // (if state has already been set up to pre-compensate propagation delay, + // we will have delta == tauD and transitState will be the same as state) + + // Downlink delay + final double tauD = signalTimeOfFlight(pva, stationDownlink.getPosition(), downlinkDate); + + // Transit state & Transit state (re)computed with gradients + final double delta = downlinkDate.durationFrom(state.getDate()); + final double deltaMTauD = delta - tauD; + final SpacecraftState transitState = state.shiftedBy(deltaMTauD); + + return new GroundReceiverCommonParametersWithoutDerivatives(state, + offsetToInertialDownlink, + stationDownlink, + tauD, + transitState, + transitState.getPVCoordinates()); + + } + + /** Compute common estimation parameters. + * @param state orbital state at measurement date + * @return common parameters + */ + protected GroundReceiverCommonParametersWithDerivatives computeCommonParametersWithDerivatives(final SpacecraftState state) { + int nbParams = 6; + final Map indices = new HashMap<>(); + for (ParameterDriver driver : getParametersDrivers()) { + if (driver.isSelected()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + indices.put(span.getData(), nbParams++); + } + } + } + final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); + + // Coordinates of the spacecraft expressed as a gradient + final TimeStampedFieldPVCoordinates pva = getCoordinates(state, 0, nbParams); + + // transform between station and inertial frame, expressed as a gradient + // The components of station's position in offset frame are the 3 last derivative parameters + final FieldTransform offsetToInertialDownlink = + getStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); + final FieldAbsoluteDate downlinkDate = offsetToInertialDownlink.getFieldDate(); + + // Station position in inertial frame at end of the downlink leg + final TimeStampedFieldPVCoordinates stationDownlink = + offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDate, + zero, zero, zero)); + + // Compute propagation times + // (if state has already been set up to pre-compensate propagation delay, + // we will have delta == tauD and transitState will be the same as state) + + // Downlink delay + final Gradient tauD = signalTimeOfFlight(pva, stationDownlink.getPosition(), downlinkDate); + + // Transit state & Transit state (re)computed with gradients + final Gradient delta = downlinkDate.durationFrom(state.getDate()); + final Gradient deltaMTauD = tauD.negate().add(delta); + final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue()); + final TimeStampedFieldPVCoordinates transitPV = pva.shiftedBy(deltaMTauD); + + return new GroundReceiverCommonParametersWithDerivatives(state, + indices, + offsetToInertialDownlink, + stationDownlink, + tauD, + transitState, + transitPV); + + } + + /** + * Get the station position for a given frame. + * @param frame inertial frame for station position + * @return the station position in the given inertial frame + * @since 12.0 + */ + public Vector3D getGroundStationPosition(final Frame frame) { + return station.getBaseFrame().getPosition(getDate(), frame); + } + + /** + * Get the station coordinates for a given frame. + * @param frame inertial frame for station position + * @return the station coordinates in the given inertial frame + * @since 12.0 + */ + public PVCoordinates getGroundStationCoordinates(final Frame frame) { + return station.getBaseFrame().getPVCoordinates(getDate(), frame); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/GroundStation.java b/src/main/java/org/orekit/estimation/measurements/GroundStation.java index 111b59b1f4..5e2e359695 100644 --- a/src/main/java/org/orekit/estimation/measurements/GroundStation.java +++ b/src/main/java/org/orekit/estimation/measurements/GroundStation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,6 +33,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.EOPHistory; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -434,15 +435,20 @@ public GeodeticPoint getOffsetGeodeticPoint(final AbsoluteDate date) { * a new offset frame. *

    * @param inertial inertial frame to transform to - * @param clockDate date of the transform as read by the ground station clock (i.e. clock offset not compensated) + * @param date date of the transform + * @param clockOffsetAlreadyApplied if true, the specified {@code date} is as read + * by the ground station clock (i.e. clock offset not compensated), if false, + * the specified {@code date} was already compensated and is a physical absolute date * @return transform between offset frame and inertial frame, at real measurement * date (i.e. with clock, Earth and station offsets applied) */ - public Transform getOffsetToInertial(final Frame inertial, final AbsoluteDate clockDate) { + public Transform getOffsetToInertial(final Frame inertial, + final AbsoluteDate date, final boolean clockOffsetAlreadyApplied) { // take clock offset into account - final double offset = clockOffsetDriver.getValue(); - final AbsoluteDate offsetCompensatedDate = new AbsoluteDate(clockDate, -offset); + final AbsoluteDate offsetCompensatedDate = clockOffsetAlreadyApplied ? + date : + new AbsoluteDate(date, -clockOffsetDriver.getValue()); // take Earth offsets into account final Transform intermediateToBody = estimatedEarthFrameProvider.getTransform(offsetCompensatedDate).getInverse(); @@ -483,7 +489,8 @@ public Transform getOffsetToInertial(final Frame inertial, final AbsoluteDate cl * @param inertial inertial frame to transform to * @param clockDate date of the transform as read by the ground station clock (i.e. clock offset not compensated) * @param freeParameters total number of free parameters in the gradient - * @param indices indices of the estimated parameters in derivatives computations + * @param indices indices of the estimated parameters in derivatives computations, must be driver + * span name in map, not driver name or will not give right results (see {@link ParameterDriver#getValue(int, Map)}) * @return transform between offset frame and inertial frame, at real measurement * date (i.e. with clock, Earth and station offsets applied) * @see #getOffsetToInertial(Frame, FieldAbsoluteDate, int, Map) @@ -494,7 +501,7 @@ public FieldTransform getOffsetToInertial(final Frame inertial, final int freeParameters, final Map indices) { // take clock offset into account - final Gradient offset = clockOffsetDriver.getValue(freeParameters, indices); + final Gradient offset = clockOffsetDriver.getValue(freeParameters, indices, clockDate); final FieldAbsoluteDate offsetCompensatedDate = new FieldAbsoluteDate<>(clockDate, offset.negate()); @@ -511,7 +518,8 @@ public FieldTransform getOffsetToInertial(final Frame inertial, * @param inertial inertial frame to transform to * @param offsetCompensatedDate date of the transform, clock offset and its derivatives already compensated * @param freeParameters total number of free parameters in the gradient - * @param indices indices of the estimated parameters in derivatives computations + * @param indices indices of the estimated parameters in derivatives computations, must be driver + * span name in map, not driver name or will not give right results (see {@link ParameterDriver#getValue(int, Map)}) * @return transform between offset frame and inertial frame, at specified date * @since 10.2 */ @@ -530,12 +538,11 @@ public FieldTransform getOffsetToInertial(final Frame inertial, estimatedEarthFrameProvider.getTransform(offsetCompensatedDate, freeParameters, indices).getInverse(); // take station offsets into account - final Gradient x = eastOffsetDriver.getValue(freeParameters, indices); - final Gradient y = northOffsetDriver.getValue(freeParameters, indices); - final Gradient z = zenithOffsetDriver.getValue(freeParameters, indices); - final BodyShape baseShape = baseFrame.getParentShape(); - final StaticTransform baseToBody = baseFrame - .getStaticTransformTo(baseShape.getBodyFrame(), null); + final Gradient x = eastOffsetDriver.getValue(freeParameters, indices); + final Gradient y = northOffsetDriver.getValue(freeParameters, indices); + final Gradient z = zenithOffsetDriver.getValue(freeParameters, indices); + final BodyShape baseShape = baseFrame.getParentShape(); + final FieldStaticTransform baseToBody = baseFrame.getStaticTransformTo(baseShape.getBodyFrame(), offsetCompensatedDate); FieldVector3D origin = baseToBody.transformPosition(new FieldVector3D<>(x, y, z)); origin = origin.add(computeDisplacement(offsetCompensatedDate.toAbsoluteDate(), origin.toVector3D())); diff --git a/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java b/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java index 9bcabdeaa5..519f77cce3 100644 --- a/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java +++ b/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,7 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeSpanMap.Span; /** One-way or two-way range measurements between two satellites. *

    @@ -107,6 +108,83 @@ public boolean isTwoWay() { return twoway; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + // coordinates of both satellites + final SpacecraftState local = states[0]; + final TimeStampedPVCoordinates pvaL = local.getPVCoordinates(); + final SpacecraftState remote = states[1]; + final TimeStampedPVCoordinates pvaR = remote.getPVCoordinates(); + + // compute propagation times + // (if state has already been set up to pre-compensate propagation delay, + // we will have delta == tauD and transitState will be the same as state) + + // downlink delay + final double dtl = getSatellites().get(0).getClockOffsetDriver().getValue(local.getDate()); + final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtl); + + final TimeStampedPVCoordinates s1Downlink = + pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); + final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate); + + // Transit state + final double delta = getDate().durationFrom(remote.getDate()); + final double deltaMTauD = delta - tauD; + + // prepare the evaluation + final EstimatedMeasurementBase estimated; + + final double range; + if (twoway) { + // Transit state (re)computed with derivative structures + final TimeStampedPVCoordinates transitState = pvaR.shiftedBy(deltaMTauD); + + // uplink delay + final double tauU = signalTimeOfFlight(pvaL, + transitState.getPosition(), + transitState.getDate()); + estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + local.shiftedBy(deltaMTauD), + remote.shiftedBy(deltaMTauD) + }, new TimeStampedPVCoordinates[] { + local.shiftedBy(delta - tauD - tauU).getPVCoordinates(), + remote.shiftedBy(delta - tauD).getPVCoordinates(), + local.shiftedBy(delta).getPVCoordinates() + }); + + // Range value + range = (tauD + tauU) * (0.5 * Constants.SPEED_OF_LIGHT); + + } else { + + estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + local.shiftedBy(deltaMTauD), + remote.shiftedBy(deltaMTauD) + }, new TimeStampedPVCoordinates[] { + remote.shiftedBy(delta - tauD).getPVCoordinates(), + local.shiftedBy(delta).getPVCoordinates() + }); + + // Clock offsets + final double dtr = getSatellites().get(1).getClockOffsetDriver().getValue(remote.getDate()); + + // Range value + range = (tauD + dtl - dtr) * Constants.SPEED_OF_LIGHT; + + } + estimated.setEstimatedValue(range); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, @@ -126,7 +204,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final final Map indices = new HashMap<>(); for (ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } } } @@ -141,7 +224,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final // we will have delta == tauD and transitState will be the same as state) // downlink delay - final Gradient dtl = getSatellites().get(0).getClockOffsetDriver().getValue(nbParams, indices); + final Gradient dtl = getSatellites().get(0).getClockOffsetDriver().getValue(nbParams, indices, local.getDate()); final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtl.negate()); @@ -190,7 +273,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final }); // Clock offsets - final Gradient dtr = getSatellites().get(1).getClockOffsetDriver().getValue(nbParams, indices); + final Gradient dtr = getSatellites().get(1).getClockOffsetDriver().getValue(nbParams, indices, remote.getDate()); // Range value range = tauD.add(dtl).subtract(dtr).multiply(Constants.SPEED_OF_LIGHT); @@ -205,9 +288,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final // Set partial derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/MultiplexedMeasurement.java b/src/main/java/org/orekit/estimation/measurements/MultiplexedMeasurement.java index 1991091802..8c7e07474a 100644 --- a/src/main/java/org/orekit/estimation/measurements/MultiplexedMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/MultiplexedMeasurement.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,9 +24,12 @@ import java.util.function.Function; import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; +import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeSpanMap.Span; /** Class multiplexing several measurements as one. *

    @@ -44,6 +47,10 @@ public class MultiplexedMeasurement extends AbstractMeasurement> observedMeasurements; + /** Multiplexed measurements without derivatives. + */ + private final List> estimatedMeasurementsWithoutDerivatives; + /** Multiplexed measurements. */ private final List> estimatedMeasurements; @@ -70,9 +77,10 @@ public MultiplexedMeasurement(final List> measurements) { multiplex(measurements, m -> m.getBaseWeight()), multiplex(measurements)); - this.observedMeasurements = measurements; - this.estimatedMeasurements = new ArrayList<>(); - this.parametersDrivers = new ParameterDriversList(); + this.observedMeasurements = measurements; + this.estimatedMeasurementsWithoutDerivatives = new ArrayList<>(); + this.estimatedMeasurements = new ArrayList<>(); + this.parametersDrivers = new ParameterDriversList(); // gather parameters drivers int dim = 0; @@ -115,6 +123,14 @@ public List> getMeasurements() { return observedMeasurements; } + /** Get the underlying estimated measurements without derivatives. + * @return underlying estimated measurements without derivatives + * @since 12.0 + */ + public List> getEstimatedMeasurementsWithoutDerivatives() { + return estimatedMeasurementsWithoutDerivatives; + } + /** Get the underlying estimated measurements. * @return underlying estimated measurements */ @@ -122,6 +138,57 @@ public List> getEstimatedMeasurements() { return estimatedMeasurements; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState[] evaluationStates = new SpacecraftState[nbSat]; + final List participants = new ArrayList<>(); + final double[] value = new double[dimension]; + + // loop over all multiplexed measurements + estimatedMeasurementsWithoutDerivatives.clear(); + int index = 0; + for (int i = 0; i < observedMeasurements.size(); ++i) { + + // filter states involved in the current measurement + final SpacecraftState[] filteredStates = new SpacecraftState[mapping[i].length]; + for (int j = 0; j < mapping[i].length; ++j) { + filteredStates[j] = states[mapping[i][j]]; + } + + // perform evaluation + final EstimatedMeasurementBase eI = observedMeasurements.get(i).estimateWithoutDerivatives(iteration, evaluation, filteredStates); + estimatedMeasurementsWithoutDerivatives.add(eI); + + // extract results + final double[] valueI = eI.getEstimatedValue(); + System.arraycopy(valueI, 0, value, index, valueI.length); + index += valueI.length; + + // extract states + final SpacecraftState[] statesI = eI.getStates(); + for (int j = 0; j < mapping[i].length; ++j) { + evaluationStates[mapping[i][j]] = statesI[j]; + } + + } + + // create multiplexed estimation + final EstimatedMeasurementBase multiplexed = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + evaluationStates, + participants.toArray(new TimeStampedPVCoordinates[0])); + + // copy multiplexed value + multiplexed.setEstimatedValue(value); + + return multiplexed; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, @@ -176,7 +243,7 @@ protected EstimatedMeasurement theoreticalEvaluation(fin Arrays.fill(m, zeroDerivative); } - final Map parametersDerivatives = new IdentityHashMap<>(); + final Map> parametersDerivatives = new IdentityHashMap<>(); index = 0; for (int i = 0; i < observedMeasurements.size(); ++i) { @@ -194,12 +261,32 @@ protected EstimatedMeasurement theoreticalEvaluation(fin // parameters derivatives eI.getDerivativesDrivers().forEach(driver -> { final ParameterDriversList.DelegatingDriver delegating = parametersDrivers.findByName(driver.getName()); - double[] derivatives = parametersDerivatives.get(delegating); - if (derivatives == null) { - derivatives = new double[dimension]; - parametersDerivatives.put(delegating, derivatives); + + if (parametersDerivatives.get(delegating) == null) { + final TimeSpanMap derivativeSpanMap = new TimeSpanMap(new double[dimension]); + parametersDerivatives.put(delegating, derivativeSpanMap); + } + + final TimeSpanMap driverNameSpan = delegating.getValueSpanMap(); + for (Span span = driverNameSpan.getSpan(driverNameSpan.getFirstSpan().getEnd()); span != null; span = span.next()) { + + double[] derivatives = parametersDerivatives.get(delegating).get(span.getStart()); + if (derivatives == null) { + derivatives = new double[dimension]; + } + if (!parametersDerivatives.get(delegating).getSpan(span.getStart()).getStart().equals(span.getStart())) { + if ((span.getStart()).equals(AbsoluteDate.PAST_INFINITY)) { + parametersDerivatives.get(delegating).addValidBefore(derivatives, span.getEnd(), false); + } else { + parametersDerivatives.get(delegating).addValidAfter(derivatives, span.getStart(), false); + } + + } + + System.arraycopy(eI.getParameterDerivatives(driver, span.getStart()), 0, derivatives, idx, dimI); + } - System.arraycopy(eI.getParameterDerivatives(driver), 0, derivatives, idx, dimI); + }); index += dimI; diff --git a/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java b/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java index e86ca0364f..45ddc631ba 100644 --- a/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java +++ b/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -92,4 +92,25 @@ public ParameterDriver getClockDriftDriver() { return clockDriftDriver; } + /** {@inheritDoc} + * @since 12.0 + */ + @Override + public boolean equals(final Object other) { + if (other instanceof ObservableSatellite) { + return propagatorIndex == ((ObservableSatellite) other).propagatorIndex; + } else { + return false; + + } + } + + /** {@inheritDoc} + * @since 12.0 + */ + @Override + public int hashCode() { + return propagatorIndex; + } + } diff --git a/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java b/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java index 796c4aee42..ea62c0b07a 100644 --- a/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.List; import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** Interface for measurements used for orbit determination. @@ -40,7 +40,7 @@ * @author Luc Maisonobe * @since 8.0 */ -public interface ObservedMeasurement> extends ComparableMeasurement { +public interface ObservedMeasurement> extends ComparableMeasurement, ParameterDriversProvider { /** Enable or disable a measurement. *

    @@ -113,18 +113,26 @@ public interface ObservedMeasurement> extends C */ List> getModifiers(); - /** Get the drivers for this measurement parameters, including its modifiers parameters. - * @return drivers for this measurement parameters, including its modifiers parameters - */ - List getParametersDrivers(); - /** Get the satellites related to this measurement. * @return satellites related to this measurement * @since 9.3 */ List getSatellites(); - /** Estimate the theoretical value of the measurement. + /** Estimate the theoretical value of the measurement, without derivatives. + *

    + * The estimated value is the combination of the raw estimated + * value and all the modifiers that apply to the measurement. + *

    + * @param iteration iteration number + * @param evaluation evaluations number + * @param states orbital states corresponding to {@link #getSatellites()} at measurement date + * @return estimated measurement + * @since 12.0 + */ + EstimatedMeasurementBase estimateWithoutDerivatives(int iteration, int evaluation, SpacecraftState[] states); + + /** Estimate the theoretical value of the measurement, with derivatives. *

    * The estimated value is the combination of the raw estimated * value and all the modifiers that apply to the measurement. diff --git a/src/main/java/org/orekit/estimation/measurements/PV.java b/src/main/java/org/orekit/estimation/measurements/PV.java index a7be477177..679b0564b9 100644 --- a/src/main/java/org/orekit/estimation/measurements/PV.java +++ b/src/main/java/org/orekit/estimation/measurements/PV.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -223,6 +223,30 @@ public double[][] getCorrelationCoefficientsMatrix() { return corrCoefMatrix; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, final int evaluation, + final SpacecraftState[] states) { + + // PV value + final TimeStampedPVCoordinates pv = states[0].getPVCoordinates(); + + // prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, states, + new TimeStampedPVCoordinates[] { + pv + }); + + estimated.setEstimatedValue(new double[] { + pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(), + pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ() + }); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, diff --git a/src/main/java/org/orekit/estimation/measurements/Position.java b/src/main/java/org/orekit/estimation/measurements/Position.java index 4171b9ec73..7d98ef1424 100644 --- a/src/main/java/org/orekit/estimation/measurements/Position.java +++ b/src/main/java/org/orekit/estimation/measurements/Position.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -156,6 +156,29 @@ public double[][] getCorrelationCoefficientsMatrix() { return corrCoefMatrix; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, final int evaluation, + final SpacecraftState[] states) { + + // PV value + final TimeStampedPVCoordinates pv = states[0].getPVCoordinates(); + + // prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, states, + new TimeStampedPVCoordinates[] { + pv + }); + + estimated.setEstimatedValue(new double[] { + pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ() + }); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, diff --git a/src/main/java/org/orekit/estimation/measurements/Range.java b/src/main/java/org/orekit/estimation/measurements/Range.java index 01601118c0..1e962d0c03 100644 --- a/src/main/java/org/orekit/estimation/measurements/Range.java +++ b/src/main/java/org/orekit/estimation/measurements/Range.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,19 +17,13 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.orekit.frames.FieldTransform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -69,23 +63,16 @@ * station clock is used for initial emission and final reception and therefore it evaluates * to zero. * - *

    * @author Thierry Ceolin * @author Luc Maisonobe * @author Maxime Journot * @since 8.0 */ -public class Range extends AbstractMeasurement { +public class Range extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "Range"; - /** Ground station from which measurement is performed. */ - private final GroundStation station; - - /** Flag indicating whether it is a two-way measurement. */ - private final boolean twoway; - /** Simple constructor. * @param station ground station from which measurement is performed * @param twoWay flag indicating whether it is a two-way measurement @@ -99,37 +86,69 @@ public class Range extends AbstractMeasurement { public Range(final GroundStation station, final boolean twoWay, final AbsoluteDate date, final double range, final double sigma, final double baseWeight, final ObservableSatellite satellite) { - super(date, range, sigma, baseWeight, Collections.singletonList(satellite)); - addParameterDriver(station.getClockOffsetDriver()); - addParameterDriver(station.getEastOffsetDriver()); - addParameterDriver(station.getNorthOffsetDriver()); - addParameterDriver(station.getZenithOffsetDriver()); - addParameterDriver(station.getPrimeMeridianOffsetDriver()); - addParameterDriver(station.getPrimeMeridianDriftDriver()); - addParameterDriver(station.getPolarOffsetXDriver()); - addParameterDriver(station.getPolarDriftXDriver()); - addParameterDriver(station.getPolarOffsetYDriver()); - addParameterDriver(station.getPolarDriftYDriver()); - if (!twoWay) { - // for one way measurements, the satellite clock offset affects the measurement - addParameterDriver(satellite.getClockOffsetDriver()); - } - this.station = station; - this.twoway = twoWay; + super(station, twoWay, date, range, sigma, baseWeight, satellite); } - /** Get the ground station from which measurement is performed. - * @return ground station from which measurement is performed - */ - public GroundStation getStation() { - return station; - } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final GroundReceiverCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states[0]); + final TimeStampedPVCoordinates transitPV = common.getTransitState().getPVCoordinates(); + + // prepare the evaluation + final EstimatedMeasurementBase estimated; + final double range; + + if (isTwoWay()) { + + // Station at transit state date (derivatives of tauD taken into account) + final TimeStampedPVCoordinates stationAtTransitDate = common.getStationDownlink().shiftedBy(-common.getTauD()); + // Uplink delay + final double tauU = signalTimeOfFlight(stationAtTransitDate, transitPV.getPosition(), transitPV.getDate()); + final TimeStampedPVCoordinates stationUplink = common.getStationDownlink().shiftedBy(-common.getTauD() - tauU); + + // Prepare the evaluation + estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getTransitState() + }, new TimeStampedPVCoordinates[] { + stationUplink, + transitPV, + common.getStationDownlink() + }); + + // Range value + final double cOver2 = 0.5 * Constants.SPEED_OF_LIGHT; + final double tau = common.getTauD() + tauU; + range = tau * cOver2; + + } else { + + estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getTransitState() + }, new TimeStampedPVCoordinates[] { + transitPV, + common.getStationDownlink() + }); + + // Clock offsets + final ObservableSatellite satellite = getSatellites().get(0); + final double dts = satellite.getClockOffsetDriver().getValue(common.getState().getDate()); + final double dtg = getStation().getClockOffsetDriver().getValue(common.getState().getDate()); + + // Range value + range = (common.getTauD() + dtg - dts) * Constants.SPEED_OF_LIGHT; + + } + + estimated.setEstimatedValue(range); + + return estimated; - /** Check if the instance represents a two-way measurement. - * @return true if the instance represents a two-way measurement - */ - public boolean isTwoWay() { - return twoway; } /** {@inheritDoc} */ @@ -148,89 +167,57 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // - 0..2 - Position of the spacecraft in inertial frame // - 3..5 - Velocity of the spacecraft in inertial frame // - 6..n - measurements parameters (clock offset, station offsets, pole, prime meridian, sat clock offset...) - int nbParams = 6; - final Map indices = new HashMap<>(); - for (ParameterDriver driver : getParametersDrivers()) { - if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); - } - } - final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); - - // Coordinates of the spacecraft expressed as a gradient - final TimeStampedFieldPVCoordinates pvaDS = getCoordinates(state, 0, nbParams); - - // transform between station and inertial frame, expressed as a gradient - // The components of station's position in offset frame are the 3 last derivative parameters - final FieldTransform offsetToInertialDownlink = - station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); - final FieldAbsoluteDate downlinkDateDS = offsetToInertialDownlink.getFieldDate(); - - // Station position in inertial frame at end of the downlink leg - final TimeStampedFieldPVCoordinates stationDownlink = - offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS, - zero, zero, zero)); - - // Compute propagation times - // (if state has already been set up to pre-compensate propagation delay, - // we will have delta == tauD and transitState will be the same as state) - - // Downlink delay - final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS); - - // Transit state & Transit state (re)computed with gradients - final Gradient delta = downlinkDateDS.durationFrom(state.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); - final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue()); - final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD); + final GroundReceiverCommonParametersWithDerivatives common = computeCommonParametersWithDerivatives(state); + final int nbParams = common.getTauD().getFreeParameters(); + final TimeStampedFieldPVCoordinates transitPV = common.getTransitPV(); // prepare the evaluation final EstimatedMeasurement estimated; final Gradient range; - if (twoway) { + if (isTwoWay()) { // Station at transit state date (derivatives of tauD taken into account) final TimeStampedFieldPVCoordinates stationAtTransitDate = - stationDownlink.shiftedBy(tauD.negate()); + common.getStationDownlink().shiftedBy(common.getTauD().negate()); // Uplink delay final Gradient tauU = - signalTimeOfFlight(stationAtTransitDate, transitStateDS.getPosition(), transitStateDS.getDate()); + signalTimeOfFlight(stationAtTransitDate, transitPV.getPosition(), transitPV.getDate()); final TimeStampedFieldPVCoordinates stationUplink = - stationDownlink.shiftedBy(-tauD.getValue() - tauU.getValue()); + common.getStationDownlink().shiftedBy(-common.getTauD().getValue() - tauU.getValue()); // Prepare the evaluation estimated = new EstimatedMeasurement(this, iteration, evaluation, new SpacecraftState[] { - transitState + common.getTransitState() }, new TimeStampedPVCoordinates[] { stationUplink.toTimeStampedPVCoordinates(), - transitStateDS.toTimeStampedPVCoordinates(), - stationDownlink.toTimeStampedPVCoordinates() + transitPV.toTimeStampedPVCoordinates(), + common.getStationDownlink().toTimeStampedPVCoordinates() }); // Range value final double cOver2 = 0.5 * Constants.SPEED_OF_LIGHT; - final Gradient tau = tauD.add(tauU); + final Gradient tau = common.getTauD().add(tauU); range = tau.multiply(cOver2); } else { estimated = new EstimatedMeasurement(this, iteration, evaluation, new SpacecraftState[] { - transitState + common.getTransitState() }, new TimeStampedPVCoordinates[] { - transitStateDS.toTimeStampedPVCoordinates(), - stationDownlink.toTimeStampedPVCoordinates() + transitPV.toTimeStampedPVCoordinates(), + common.getStationDownlink().toTimeStampedPVCoordinates() }); // Clock offsets final ObservableSatellite satellite = getSatellites().get(0); - final Gradient dts = satellite.getClockOffsetDriver().getValue(nbParams, indices); - final Gradient dtg = station.getClockOffsetDriver().getValue(nbParams, indices); + final Gradient dts = satellite.getClockOffsetDriver().getValue(nbParams, common.getIndices(), state.getDate()); + final Gradient dtg = getStation().getClockOffsetDriver().getValue(nbParams, common.getIndices(), state.getDate()); // Range value - range = tauD.add(dtg).subtract(dts).multiply(Constants.SPEED_OF_LIGHT); + range = common.getTauD().add(dtg).subtract(dts).multiply(Constants.SPEED_OF_LIGHT); } @@ -243,9 +230,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // set partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = common.getIndices().get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/RangeRate.java b/src/main/java/org/orekit/estimation/measurements/RangeRate.java index ec3a8b2ebf..567a88a696 100644 --- a/src/main/java/org/orekit/estimation/measurements/RangeRate.java +++ b/src/main/java/org/orekit/estimation/measurements/RangeRate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,19 +17,19 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.frames.FieldTransform; +import org.orekit.frames.Transform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -47,17 +47,11 @@ * @author Joris Olympio * @since 8.0 */ -public class RangeRate extends AbstractMeasurement { +public class RangeRate extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "RangeRate"; - /** Ground station from which measurement is performed. */ - private final GroundStation station; - - /** Flag indicating whether it is a two-way measurement. */ - private final boolean twoway; - /** Simple constructor. * @param station ground station from which measurement is performed * @param date date of the measurement @@ -71,35 +65,62 @@ public class RangeRate extends AbstractMeasurement { public RangeRate(final GroundStation station, final AbsoluteDate date, final double rangeRate, final double sigma, final double baseWeight, final boolean twoway, final ObservableSatellite satellite) { - super(date, rangeRate, sigma, baseWeight, Collections.singletonList(satellite)); - addParameterDriver(station.getClockOffsetDriver()); - addParameterDriver(station.getClockDriftDriver()); - addParameterDriver(satellite.getClockDriftDriver()); - addParameterDriver(station.getEastOffsetDriver()); - addParameterDriver(station.getNorthOffsetDriver()); - addParameterDriver(station.getZenithOffsetDriver()); - addParameterDriver(station.getPrimeMeridianOffsetDriver()); - addParameterDriver(station.getPrimeMeridianDriftDriver()); - addParameterDriver(station.getPolarOffsetXDriver()); - addParameterDriver(station.getPolarDriftXDriver()); - addParameterDriver(station.getPolarOffsetYDriver()); - addParameterDriver(station.getPolarDriftYDriver()); - this.station = station; - this.twoway = twoway; + super(station, twoway, date, rangeRate, sigma, baseWeight, satellite); } - /** Check if the instance represents a two-way measurement. - * @return true if the instance represents a two-way measurement - */ - public boolean isTwoWay() { - return twoway; - } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final GroundReceiverCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states[0]); + final TimeStampedPVCoordinates transitPV = common.getTransitPV(); + + // one-way (downlink) range-rate + final EstimatedMeasurementBase evalOneWay1 = + oneWayTheoreticalEvaluation(iteration, evaluation, true, + common.getStationDownlink(), + transitPV, + common.getTransitState()); + final EstimatedMeasurementBase estimated; + if (isTwoWay()) { + // one-way (uplink) light time correction + final Transform offsetToInertialApproxUplink = + getStation().getOffsetToInertial(common.getState().getFrame(), + common.getStationDownlink().getDate().shiftedBy(-2 * common.getTauD()), + false); + final AbsoluteDate approxUplinkDate = offsetToInertialApproxUplink.getDate(); + + final TimeStampedPVCoordinates stationApproxUplink = + offsetToInertialApproxUplink.transformPVCoordinates(new TimeStampedPVCoordinates(approxUplinkDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + final double tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), transitPV.getDate()); + + final TimeStampedPVCoordinates stationUplink = + stationApproxUplink.shiftedBy(transitPV.getDate().durationFrom(approxUplinkDate) - tauU); + + final EstimatedMeasurementBase evalOneWay2 = + oneWayTheoreticalEvaluation(iteration, evaluation, false, + stationUplink, transitPV, common.getTransitState()); + + // combine uplink and downlink values + estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, + evalOneWay1.getStates(), + new TimeStampedPVCoordinates[] { + evalOneWay2.getParticipants()[0], + evalOneWay1.getParticipants()[0], + evalOneWay1.getParticipants()[1] + }); + estimated.setEstimatedValue(0.5 * (evalOneWay1.getEstimatedValue()[0] + evalOneWay2.getEstimatedValue()[0])); + + } else { + estimated = evalOneWay1; + } + + return estimated; - /** Get the ground station from which measurement is performed. - * @return ground station from which measurement is performed - */ - public GroundStation getStation() { - return station; } /** {@inheritDoc} */ @@ -117,58 +138,26 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iterat // - 0..2 - Position of the spacecraft in inertial frame // - 3..5 - Velocity of the spacecraft in inertial frame // - 6..n - station parameters (clock offset, clock drift, station offsets, pole, prime meridian...) - int nbParams = 6; - final Map indices = new HashMap<>(); - for (ParameterDriver driver : getParametersDrivers()) { - if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); - } - } - final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); - - // Coordinates of the spacecraft expressed as a gradient - final TimeStampedFieldPVCoordinates pvaDS = getCoordinates(state, 0, nbParams); - - // transform between station and inertial frame, expressed as a gradient - // The components of station's position in offset frame are the 3 last derivative parameters - final FieldTransform offsetToInertialDownlink = - station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); - final FieldAbsoluteDate downlinkDateDS = - offsetToInertialDownlink.getFieldDate(); - - // Station position in inertial frame at end of the downlink leg - final TimeStampedFieldPVCoordinates stationDownlink = - offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS, - zero, zero, zero)); - - // Compute propagation times - // (if state has already been set up to pre-compensate propagation delay, - // we will have delta == tauD and transitState will be the same as state) - - // Downlink delay - final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS); - - // Transit state - final Gradient delta = downlinkDateDS.durationFrom(state.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); - final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue()); - - // Transit state (re)computed with gradients - final TimeStampedFieldPVCoordinates transitPV = pvaDS.shiftedBy(deltaMTauD); + final GroundReceiverCommonParametersWithDerivatives common = computeCommonParametersWithDerivatives(state); + final int nbParams = common.getTauD().getFreeParameters(); + final TimeStampedFieldPVCoordinates transitPV = common.getTransitPV(); // one-way (downlink) range-rate final EstimatedMeasurement evalOneWay1 = oneWayTheoreticalEvaluation(iteration, evaluation, true, - stationDownlink, transitPV, transitState, indices, nbParams); + common.getStationDownlink(), transitPV, + common.getTransitState(), common.getIndices(), nbParams); final EstimatedMeasurement estimated; - if (twoway) { + if (isTwoWay()) { // one-way (uplink) light time correction final FieldTransform offsetToInertialApproxUplink = - station.getOffsetToInertial(state.getFrame(), - downlinkDateDS.shiftedBy(tauD.multiply(-2)), nbParams, indices); + getStation().getOffsetToInertial(state.getFrame(), + common.getStationDownlink().getDate().shiftedBy(common.getTauD().multiply(-2)), + nbParams, common.getIndices()); final FieldAbsoluteDate approxUplinkDateDS = offsetToInertialApproxUplink.getFieldDate(); + final FieldVector3D zero = FieldVector3D.getZero(common.getTauD().getField()); final TimeStampedFieldPVCoordinates stationApproxUplink = offsetToInertialApproxUplink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(approxUplinkDateDS, zero, zero, zero)); @@ -180,7 +169,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iterat final EstimatedMeasurement evalOneWay2 = oneWayTheoreticalEvaluation(iteration, evaluation, false, - stationUplink, transitPV, transitState, indices, nbParams); + stationUplink, transitPV, common.getTransitState(), + common.getIndices(), nbParams); // combine uplink and downlink values estimated = new EstimatedMeasurement<>(this, iteration, evaluation, @@ -205,13 +195,15 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iterat // combine uplink and downlink partial derivatives with respect to parameters evalOneWay1.getDerivativesDrivers().forEach(driver -> { - final double[] pd1 = evalOneWay1.getParameterDerivatives(driver); - final double[] pd2 = evalOneWay2.getParameterDerivatives(driver); - final double[] pd = new double[pd1.length]; - for (int i = 0; i < pd.length; ++i) { - pd[i] = 0.5 * (pd1[i] + pd2[i]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final double[] pd1 = evalOneWay1.getParameterDerivatives(driver, span.getStart()); + final double[] pd2 = evalOneWay2.getParameterDerivatives(driver, span.getStart()); + final double[] pd = new double[pd1.length]; + for (int i = 0; i < pd.length; ++i) { + pd[i] = 0.5 * (pd1[i] + pd2[i]); + } + estimated.setParameterDerivatives(driver, span.getStart(), pd); } - estimated.setParameterDerivatives(driver, pd); }); } else { @@ -222,6 +214,65 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iterat } + /** Evaluate measurement in one-way without derivatives. + * @param iteration iteration number + * @param evaluation evaluations counter + * @param downlink indicator for downlink leg + * @param stationPV station coordinates when signal is at station + * @param transitPV spacecraft coordinates at onboard signal transit + * @param transitState orbital state at onboard signal transit + * @return theoretical value + * @see #evaluate(SpacecraftStatet) + * @since 12.0 + */ + private EstimatedMeasurementBase oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink, + final TimeStampedPVCoordinates stationPV, + final TimeStampedPVCoordinates transitPV, + final SpacecraftState transitState) { + + // prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + transitState + }, new TimeStampedPVCoordinates[] { + downlink ? transitPV : stationPV, + downlink ? stationPV : transitPV + }); + + // range rate value + final Vector3D stationPosition = stationPV.getPosition(); + final Vector3D relativePosition = stationPosition.subtract(transitPV.getPosition()); + + final Vector3D stationVelocity = stationPV.getVelocity(); + final Vector3D relativeVelocity = stationVelocity.subtract(transitPV.getVelocity()); + + // radial direction + final Vector3D lineOfSight = relativePosition.normalize(); + + // line of sight velocity + final double lineOfSightVelocity = Vector3D.dotProduct(relativeVelocity, lineOfSight); + + // range rate + double rangeRate = lineOfSightVelocity; + + if (!isTwoWay()) { + // clock drifts, taken in account only in case of one way + final ObservableSatellite satellite = getSatellites().get(0); + final double dtsDot = satellite.getClockDriftDriver().getValue(transitState.getDate()); + final double dtgDot = getStation().getClockDriftDriver().getValue(stationPV.getDate()); + + final double clockDriftBiais = (dtgDot - dtsDot) * Constants.SPEED_OF_LIGHT; + + rangeRate = rangeRate + clockDriftBiais; + } + + estimated.setEstimatedValue(rangeRate); + + return estimated; + + } + /** Evaluate measurement in one-way. * @param iteration iteration number * @param evaluation evaluations counter @@ -267,11 +318,11 @@ private EstimatedMeasurement oneWayTheoreticalEvaluation(final int it // range rate Gradient rangeRate = lineOfSightVelocity; - if (!twoway) { + if (!isTwoWay()) { // clock drifts, taken in account only in case of one way final ObservableSatellite satellite = getSatellites().get(0); - final Gradient dtsDot = satellite.getClockDriftDriver().getValue(nbParams, indices); - final Gradient dtgDot = station.getClockDriftDriver().getValue(nbParams, indices); + final Gradient dtsDot = satellite.getClockDriftDriver().getValue(nbParams, indices, transitState.getDate()); + final Gradient dtgDot = getStation().getClockDriftDriver().getValue(nbParams, indices, stationPV.getDate().toAbsoluteDate()); final Gradient clockDriftBiais = dtgDot.subtract(dtsDot).multiply(Constants.SPEED_OF_LIGHT); @@ -287,9 +338,11 @@ private EstimatedMeasurement oneWayTheoreticalEvaluation(final int it // set partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/TDOA.java b/src/main/java/org/orekit/estimation/measurements/TDOA.java index 05509b1ff4..7761240381 100644 --- a/src/main/java/org/orekit/estimation/measurements/TDOA.java +++ b/src/main/java/org/orekit/estimation/measurements/TDOA.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,20 +17,22 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.frames.FieldTransform; +import org.orekit.frames.Transform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -50,14 +52,11 @@ * @author Pascal Parraud * @since 11.2 */ -public class TDOA extends AbstractMeasurement { +public class TDOA extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "TDOA"; - /** Prime ground station, the one that gives the date of the measurement. */ - private final GroundStation primeStation; - /** Second ground station, the one that gives the measurement, i.e. the delay. */ private final GroundStation secondStation; @@ -73,18 +72,8 @@ public class TDOA extends AbstractMeasurement { public TDOA(final GroundStation primeStation, final GroundStation secondStation, final AbsoluteDate date, final double tdoa, final double sigma, final double baseWeight, final ObservableSatellite satellite) { - super(date, tdoa, sigma, baseWeight, Collections.singletonList(satellite)); - // add parameter drivers for the primary station - addParameterDriver(primeStation.getClockOffsetDriver()); - addParameterDriver(primeStation.getEastOffsetDriver()); - addParameterDriver(primeStation.getNorthOffsetDriver()); - addParameterDriver(primeStation.getZenithOffsetDriver()); - addParameterDriver(primeStation.getPrimeMeridianOffsetDriver()); - addParameterDriver(primeStation.getPrimeMeridianDriftDriver()); - addParameterDriver(primeStation.getPolarOffsetXDriver()); - addParameterDriver(primeStation.getPolarDriftXDriver()); - addParameterDriver(primeStation.getPolarOffsetYDriver()); - addParameterDriver(primeStation.getPolarDriftYDriver()); + super(primeStation, false, date, tdoa, sigma, baseWeight, satellite); + // add parameter drivers for the secondary station addParameterDriver(secondStation.getClockOffsetDriver()); addParameterDriver(secondStation.getEastOffsetDriver()); @@ -96,15 +85,15 @@ public TDOA(final GroundStation primeStation, final GroundStation secondStation, addParameterDriver(secondStation.getPolarDriftXDriver()); addParameterDriver(secondStation.getPolarOffsetYDriver()); addParameterDriver(secondStation.getPolarDriftYDriver()); - this.primeStation = primeStation; this.secondStation = secondStation; + } /** Get the prime ground station, the one that gives the date of the measurement. * @return prime ground station */ public GroundStation getPrimeStation() { - return primeStation; + return getStation(); } /** Get the second ground station, the one that gives the measurement. @@ -114,6 +103,87 @@ public GroundStation getSecondStation() { return secondStation; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState state = states[0]; + + // coordinates of the spacecraft + final TimeStampedPVCoordinates pva = state.getPVCoordinates(); + + // transform between prime station frame and inertial frame + // at the real date of measurement, i.e. taking station clock offset into account + final Transform primeToInert = getStation().getOffsetToInertial(state.getFrame(), getDate(), false); + final AbsoluteDate measurementDate = primeToInert.getDate(); + + // prime station PV in inertial frame at the real date of the measurement + final TimeStampedPVCoordinates primePV = + primeToInert.transformPVCoordinates(new TimeStampedPVCoordinates(measurementDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // compute downlink delay from emitter to prime receiver + final double tau1 = signalTimeOfFlight(pva, primePV.getPosition(), measurementDate); + + // elapsed time between state date and signal arrival to the prime receiver + final double dtMtau1 = measurementDate.durationFrom(state.getDate()) - tau1; + + // satellite state at signal emission + final SpacecraftState emitterState = state.shiftedBy(dtMtau1); + + // satellite pv at signal emission (re)computed with gradient + final TimeStampedPVCoordinates emitterPV = pva.shiftedBy(dtMtau1); + + // second station PV in inertial frame at real date of signal reception + TimeStampedPVCoordinates secondPV; + // initialize search loop of the reception date by second station + double tau2 = tau1; + double delta; + int count = 0; + do { + final double previous = tau2; + // date of signal arrival on second receiver + final AbsoluteDate dateAt2 = emitterState.getDate().shiftedBy(previous); + // transform between second station frame and inertial frame + // at the date of signal arrival, taking clock offset into account + final Transform secondToInert = + secondStation.getOffsetToInertial(state.getFrame(), dateAt2, true); + // second receiver position in inertial frame at the real date of signal reception + secondPV = secondToInert.transformPVCoordinates(new TimeStampedPVCoordinates(secondToInert.getDate(), + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + // downlink delay from emitter to second receiver + tau2 = linkDelay(emitterPV.getPosition(), secondPV.getPosition()); + + // Change in the computed downlink delay + delta = FastMath.abs(tau2 - previous); + } while (count++ < 10 && delta >= 2 * FastMath.ulp(tau2)); + + // The measured TDOA is (tau1 + clockOffset1) - (tau2 + clockOffset2) + final double offset1 = getStation().getClockOffsetDriver().getValue(emitterState.getDate()); + final double offset2 = secondStation.getClockOffsetDriver().getValue(emitterState.getDate()); + final double tdoa = (tau1 + offset1) - (tau2 + offset2); + + // Evaluate the TDOA value and derivatives + // ------------------------------------------- + final EstimatedMeasurement estimated = + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + emitterState + }, + new TimeStampedPVCoordinates[] { + emitterPV, + tdoa > 0 ? secondPV : primePV, + tdoa > 0 ? primePV : secondPV + }); + + // set TDOA value + estimated.setEstimatedValue(tdoa); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, @@ -135,8 +205,13 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // we have to check for duplicate keys because primary and secondary station share // pole and prime meridian parameters names that must be considered // as one set only (they are combined together by the estimation engine) - if (driver.isSelected() && !indices.containsKey(driver.getName())) { - indices.put(driver.getName(), nbParams++); + if (driver.isSelected()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } } } final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); @@ -147,7 +222,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // transform between prime station frame and inertial frame // at the real date of measurement, i.e. taking station clock offset into account final FieldTransform primeToInert = - primeStation.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); + getStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); final FieldAbsoluteDate measurementDateG = primeToInert.getFieldDate(); // prime station PV in inertial frame at the real date of the measurement @@ -193,8 +268,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, } while (count++ < 10 && delta >= 2 * FastMath.ulp(tau2.getValue())); // The measured TDOA is (tau1 + clockOffset1) - (tau2 + clockOffset2) - final Gradient offset1 = primeStation.getClockOffsetDriver().getValue(nbParams, indices); - final Gradient offset2 = secondStation.getClockOffsetDriver().getValue(nbParams, indices); + final Gradient offset1 = getStation().getClockOffsetDriver().getValue(nbParams, indices, emitterState.getDate()); + final Gradient offset2 = secondStation.getClockOffsetDriver().getValue(nbParams, indices, emitterState.getDate()); final Gradient tdoaG = tau1.add(offset1).subtract(tau2.add(offset2)); final double tdoa = tdoaG.getValue(); @@ -222,9 +297,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // set partial derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } @@ -232,6 +309,17 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, } + /** Compute propagation delay on a link. + * @param emitter the position of the emitter + * @param receiver the position of the receiver (same frame as emitter) + * @return the propagation delay + * @since 12.0 + */ + private double linkDelay(final Vector3D emitter, + final Vector3D receiver) { + return receiver.distance(emitter) / Constants.SPEED_OF_LIGHT; + } + /** Compute propagation delay on a link. * @param emitter the position of the emitter * @param receiver the position of the receiver (same frame as emitter) @@ -241,4 +329,5 @@ private Gradient linkDelay(final FieldVector3D emitter, final FieldVector3D receiver) { return receiver.distance(emitter).divide(Constants.SPEED_OF_LIGHT); } + } diff --git a/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java b/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java index baf2caa162..e76477315a 100644 --- a/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java +++ b/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,6 @@ package org.orekit.estimation.measurements; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -25,7 +24,9 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.frames.FieldTransform; +import org.orekit.frames.Transform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -33,6 +34,7 @@ import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -57,14 +59,11 @@ * * @since 9.0 */ -public class TurnAroundRange extends AbstractMeasurement { +public class TurnAroundRange extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "TurnAroundRange"; - /** Primary ground station from which measurement is performed. */ - private final GroundStation primaryStation; - /** Secondary ground station reflecting the signal. */ private final GroundStation secondaryStation; @@ -82,17 +81,8 @@ public TurnAroundRange(final GroundStation primaryStation, final GroundStation s final AbsoluteDate date, final double turnAroundRange, final double sigma, final double baseWeight, final ObservableSatellite satellite) { - super(date, turnAroundRange, sigma, baseWeight, Collections.singletonList(satellite)); - addParameterDriver(primaryStation.getClockOffsetDriver()); - addParameterDriver(primaryStation.getEastOffsetDriver()); - addParameterDriver(primaryStation.getNorthOffsetDriver()); - addParameterDriver(primaryStation.getZenithOffsetDriver()); - addParameterDriver(primaryStation.getPrimeMeridianOffsetDriver()); - addParameterDriver(primaryStation.getPrimeMeridianDriftDriver()); - addParameterDriver(primaryStation.getPolarOffsetXDriver()); - addParameterDriver(primaryStation.getPolarDriftXDriver()); - addParameterDriver(primaryStation.getPolarOffsetYDriver()); - addParameterDriver(primaryStation.getPolarDriftYDriver()); + super(primaryStation, true, date, turnAroundRange, sigma, baseWeight, satellite); + // the secondary station clock is not used at all, we ignore the corresponding parameter driver addParameterDriver(secondaryStation.getEastOffsetDriver()); addParameterDriver(secondaryStation.getNorthOffsetDriver()); @@ -103,15 +93,15 @@ public TurnAroundRange(final GroundStation primaryStation, final GroundStation s addParameterDriver(secondaryStation.getPolarDriftXDriver()); addParameterDriver(secondaryStation.getPolarOffsetYDriver()); addParameterDriver(secondaryStation.getPolarDriftYDriver()); - this.primaryStation = primaryStation; this.secondaryStation = secondaryStation; + } /** Get the primary ground station from which measurement is performed. * @return primary ground station from which measurement is performed */ public GroundStation getPrimaryStation() { - return primaryStation; + return getStation(); } /** Get the secondary ground station reflecting the signal. @@ -121,6 +111,164 @@ public GroundStation getSecondaryStation() { return secondaryStation; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final SpacecraftState state = states[0]; + + // Time-stamped PV + final TimeStampedPVCoordinates pva = state.getPVCoordinates(); + + // The path of the signal is divided in two legs. + // Leg1: Emission from primary station to satellite in primaryTauU seconds + // + Reflection from satellite to secondary station in secondaryTauD seconds + // Leg2: Reflection from secondary station to satellite in secondaryTauU seconds + // + Reflection from satellite to primary station in primaryTaudD seconds + // The measurement is considered to be time stamped at reception on ground + // by the primary station. All times are therefore computed as backward offsets + // with respect to this reception time. + // + // Two intermediate spacecraft states are defined: + // - transitStateLeg2: State of the satellite when it bounced back the signal + // from secondary station to primary station during the 2nd leg + // - transitStateLeg1: State of the satellite when it bounced back the signal + // from primary station to secondary station during the 1st leg + + // Compute propagation time for the 2nd leg of the signal path + // -- + + // Time difference between t (date of the measurement) and t' (date tagged in spacecraft state) + // (if state has already been set up to pre-compensate propagation delay, + // we will have delta = primaryTauD + secondaryTauU) + final double delta = getDate().durationFrom(state.getDate()); + + // transform between primary station topocentric frame (east-north-zenith) and inertial frame expressed as gradients + final Transform primaryToInert = + getStation().getOffsetToInertial(state.getFrame(), getDate(), false); + final AbsoluteDate measurementDate = primaryToInert.getDate(); + + // Primary station PV in inertial frame at measurement date + final TimeStampedPVCoordinates primaryArrival = + primaryToInert.transformPVCoordinates(new TimeStampedPVCoordinates(measurementDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Compute propagation times + final double primaryTauD = signalTimeOfFlight(pva, primaryArrival.getPosition(), measurementDate); + + // Elapsed time between state date t' and signal arrival to the transit state of the 2nd leg + final double dtLeg2 = delta - primaryTauD; + + // Transit state where the satellite reflected the signal from secondary to primary station + final SpacecraftState transitStateLeg2 = state.shiftedBy(dtLeg2); + + // Transit state pv of leg2 (re)computed with gradient + final TimeStampedPVCoordinates transitStateLeg2PV = pva.shiftedBy(dtLeg2); + + // transform between secondary station topocentric frame (east-north-zenith) and inertial frame expressed as gradients + // The components of secondary station's position in offset frame are the 3 last derivative parameters + final AbsoluteDate approxReboundDate = measurementDate.shiftedBy(-delta); + final Transform secondaryToInertApprox = + secondaryStation.getOffsetToInertial(state.getFrame(), approxReboundDate, true); + + // Secondary station PV in inertial frame at approximate rebound date on secondary station + final TimeStampedPVCoordinates QSecondaryApprox = + secondaryToInertApprox.transformPVCoordinates(new TimeStampedPVCoordinates(approxReboundDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Uplink time of flight from secondary station to transit state of leg2 + final double secondaryTauU = signalTimeOfFlight(QSecondaryApprox, + transitStateLeg2PV.getPosition(), + transitStateLeg2PV.getDate()); + + // Total time of flight for leg 2 + final double tauLeg2 = primaryTauD + secondaryTauU; + + // Compute propagation time for the 1st leg of the signal path + // -- + + // Absolute date of rebound of the signal to secondary station + final AbsoluteDate reboundDate = measurementDate.shiftedBy(-tauLeg2); + final Transform secondaryToInert = secondaryStation.getOffsetToInertial(state.getFrame(), reboundDate, true); + + // Secondary station PV in inertial frame at rebound date on secondary station + final TimeStampedPVCoordinates secondaryRebound = + secondaryToInert.transformPVCoordinates(new TimeStampedPVCoordinates(reboundDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Downlink time of flight from transitStateLeg1 to secondary station at rebound date + final double secondaryTauD = signalTimeOfFlight(transitStateLeg2PV, + secondaryRebound.getPosition(), + reboundDate); + + + // Elapsed time between state date t' and signal arrival to the transit state of the 1st leg + final double dtLeg1 = dtLeg2 - secondaryTauU - secondaryTauD; + + // Transit state pv of leg2 (re)computed + final TimeStampedPVCoordinates transitStateLeg1PV = pva.shiftedBy(dtLeg1); + + // transform between primary station topocentric frame (east-north-zenith) and inertial frame + final AbsoluteDate approxEmissionDate = measurementDate.shiftedBy(-2 * (secondaryTauU + primaryTauD)); + final Transform primaryToInertApprox = getStation().getOffsetToInertial(state.getFrame(), approxEmissionDate, true); + + // Primary station PV in inertial frame at approximate emission date + final TimeStampedPVCoordinates QPrimaryApprox = + primaryToInertApprox.transformPVCoordinates(new TimeStampedPVCoordinates(approxEmissionDate, + Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); + + // Uplink time of flight from primary station to transit state of leg1 + final double primaryTauU = signalTimeOfFlight(QPrimaryApprox, + transitStateLeg1PV.getPosition(), + transitStateLeg1PV.getDate()); + + // Primary station PV in inertial frame at exact emission date + final AbsoluteDate emissionDate = transitStateLeg1PV.getDate().shiftedBy(-primaryTauU); + final TimeStampedPVCoordinates primaryDeparture = + primaryToInertApprox.shiftedBy(emissionDate.durationFrom(primaryToInertApprox.getDate())). + transformPVCoordinates(new TimeStampedPVCoordinates(emissionDate, PVCoordinates.ZERO)); + + // Total time of flight for leg 1 + final double tauLeg1 = secondaryTauD + primaryTauU; + + + // -- + // Evaluate the turn-around range value and its derivatives + // -------------------------------------------------------- + + // The state we use to define the estimated measurement is a middle ground between the two transit states + // This is done to avoid calling "SpacecraftState.shiftedBy" function on long duration + // Thus we define the state at the date t" = date of rebound of the signal at the secondary station + // Or t" = t -primaryTauD -secondaryTauU + // The iterative process in the estimation ensures that, after several iterations, the date stamped in the + // state S in input of this function will be close to t" + // Therefore we will shift state S by: + // - +secondaryTauU to get transitStateLeg2 + // - -secondaryTauD to get transitStateLeg1 + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + transitStateLeg2.shiftedBy(-secondaryTauU) + }, + new TimeStampedPVCoordinates[] { + primaryDeparture, + transitStateLeg1PV, + secondaryRebound, + transitStateLeg2.getPVCoordinates(), + primaryArrival + }); + + // Turn-around range value = Total time of flight for the 2 legs divided by 2 and multiplied by c + final double cOver2 = 0.5 * Constants.SPEED_OF_LIGHT; + final double turnAroundRange = (tauLeg2 + tauLeg1) * cOver2; + estimated.setEstimatedValue(turnAroundRange); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final int evaluation, @@ -143,8 +291,13 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // we have to check for duplicate keys because primary and secondary station share // pole and prime meridian parameters names that must be considered // as one set only (they are combined together by the estimation engine) - if (driver.isSelected() && !indices.containsKey(driver.getName())) { - indices.put(driver.getName(), nbParams++); + if (driver.isSelected()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } } } final Field field = GradientField.getField(nbParams); @@ -178,7 +331,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // transform between primary station topocentric frame (east-north-zenith) and inertial frame expressed as gradients final FieldTransform primaryToInert = - primaryStation.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); + getStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); final FieldAbsoluteDate measurementDateDS = primaryToInert.getFieldDate(); // Primary station PV in inertial frame at measurement date @@ -247,7 +400,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final FieldAbsoluteDate approxEmissionDate = measurementDateDS.shiftedBy(-2 * (secondaryTauU.getValue() + primaryTauD.getValue())); final FieldTransform primaryToInertApprox = - primaryStation.getOffsetToInertial(state.getFrame(), approxEmissionDate, nbParams, indices); + getStation().getOffsetToInertial(state.getFrame(), approxEmissionDate, nbParams, indices); // Primary station PV in inertial frame at approximate emission date final TimeStampedFieldPVCoordinates QPrimaryApprox = @@ -308,9 +461,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // set partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencyHatchFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencyHatchFilter.java index a6ac101d59..ce27a30795 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencyHatchFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencyHatchFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.ArrayList; import org.hipparchus.util.FastMath; -import org.orekit.gnss.ObservationData; +import org.orekit.files.rinex.observation.ObservationData; import org.orekit.utils.Constants; /** diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencySmoother.java b/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencySmoother.java index c79e534788..3368a5c6c1 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencySmoother.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/DualFrequencySmoother.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,9 @@ import java.util.HashMap; import java.util.List; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.ChronologicalComparator; @@ -135,7 +135,7 @@ public void filterDataSet(final List listODS, final Satellit // For each data set, work on those corresping to the PRN and Satellite system. for (final ObservationDataSet obsSet : sortedListODS) { - if (obsSet.getSatelliteSystem() == satSystem && obsSet.getPrnNumber() == prnNumber) { + if (obsSet.getSatellite().getSystem() == satSystem && obsSet.getSatellite().getPRN() == prnNumber) { // Get all observation data final List listObsData = obsSet.getObservationData(); // For each ObservationData check if usable (SNR and !(isNaN)) diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/ElevationFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/ElevationFilter.java index 02464ee8e9..bab9e27b4c 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/ElevationFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/ElevationFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -49,9 +49,9 @@ public ElevationFilter(final GroundStation station, final double threshold) { @Override public void filter(final ObservedMeasurement measurement, final SpacecraftState state) { // Current elevation of the satellite - final double trueElevation = station.getBaseFrame().getElevation(state.getPVCoordinates().getPosition(), - state.getFrame(), - state.getDate()); + final double trueElevation = station.getBaseFrame(). + getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()). + getElevation(); // Filter if (trueElevation < threshold) { measurement.setEnabled(false); diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/HatchFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/HatchFilter.java index 1fceca7d07..c4b3ed25a0 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/HatchFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/HatchFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/MeasurementFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/MeasurementFilter.java index 96016e0806..ca6d80e13b 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/MeasurementFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/MeasurementFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java index b98b6578ef..47897121c0 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,7 @@ package org.orekit.estimation.measurements.filtering; import org.hipparchus.util.FastMath; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.SpacecraftState; @@ -50,13 +50,13 @@ public ResidualFilter(final double threshold) { public void filter(final ObservedMeasurement measurement, final SpacecraftState state) { // Computation of the estimated value of the measurement - final SpacecraftState[] sc = new SpacecraftState[] {state}; - final EstimatedMeasurement estimated = measurement.estimate(0, 0, sc); - final double[] estimatedValue = estimated.getEstimatedValue(); + final SpacecraftState[] sc = new SpacecraftState[] {state}; + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, sc); + final double[] estimatedValue = estimated.getEstimatedValue(); // Observed parameters (i.e. value and standard deviation) - final double[] observedValue = measurement.getObservedValue(); - final double[] sigma = measurement.getTheoreticalStandardDeviation(); + final double[] observedValue = measurement.getObservedValue(); + final double[] sigma = measurement.getTheoreticalStandardDeviation(); // Check if observed value is not too far from estimation for (int i = 0; i < observedValue.length; i++) { diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencyHatchFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencyHatchFilter.java index 284382e7c2..372d68f135 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencyHatchFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencyHatchFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,8 +20,8 @@ import java.util.Map; import org.hipparchus.util.FastMath; +import org.orekit.files.rinex.observation.ObservationData; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; /** * Single frequency Hatch filter. diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencySmoother.java b/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencySmoother.java index ecb85c6842..433ef7db50 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencySmoother.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/SingleFrequencySmoother.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,9 +20,9 @@ import java.util.HashMap; import java.util.List; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.ChronologicalComparator; @@ -139,7 +139,7 @@ public void filterDataSet(final List listODS, final Satellit // For each data set, work on those corresponding to the PRN and Satellite system. for (ObservationDataSet obsSet : sortedListODS) { - if (obsSet.getSatelliteSystem() == satSystem && obsSet.getPrnNumber() == prnNumber) { + if (obsSet.getSatellite().getSystem() == satSystem && obsSet.getSatellite().getPRN() == prnNumber) { // Get all observation data final List listObsData = obsSet.getObservationData(); // For each ObservationData check if usable (SNR and !(isNaN)) diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/SmoothedObservationDataSet.java b/src/main/java/org/orekit/estimation/measurements/filtering/SmoothedObservationDataSet.java index f0b8ca904a..0bee3890f4 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/SmoothedObservationDataSet.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/SmoothedObservationDataSet.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,8 @@ package org.orekit.estimation.measurements.filtering; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; /** * Container used to store smoothed observation data along with the original data set it originates from. diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/package-info.java b/src/main/java/org/orekit/estimation/measurements/filtering/package-info.java index b6e948bb05..303a4430cb 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/package-info.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/generation/AbstractMeasurementBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/AbstractMeasurementBuilder.java index a178bb0cf8..3dc1d59159 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/AbstractMeasurementBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/AbstractMeasurementBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -163,10 +163,9 @@ protected double[] getBaseWeight() { return baseWeight.clone(); } - /** Get the satellites related to this measurement. - * @return satellites related to this measurement - */ - protected ObservableSatellite[] getSatellites() { + /** {@inheritDoc} */ + @Override + public ObservableSatellite[] getSatellites() { return satellites.clone(); } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/AbstractScheduler.java b/src/main/java/org/orekit/estimation/measurements/generation/AbstractScheduler.java index f8c4d7bc60..21eb85a3dc 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/AbstractScheduler.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/AbstractScheduler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,14 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.time.DatesSelector; @@ -54,9 +61,8 @@ public void init(final AbsoluteDate start, final AbsoluteDate end) { builder.init(start, end); } - /** Get the measurements builder. - * @return measurements builder - */ + /** {@inheritDoc} */ + @Override public MeasurementBuilder getBuilder() { return builder; } @@ -68,4 +74,34 @@ public DatesSelector getSelector() { return selector; } + /** {@inheritDoc} */ + @Override + public SortedSet generate(final Map interpolators) { + + // select dates in the current step, using arbitrarily first interpolator + // as all interpolators cover the same range + final Map.Entry first = interpolators.entrySet().iterator().next(); + final List dates = getSelector().selectDates(first.getValue().getPreviousState().getDate(), + first.getValue().getCurrentState().getDate()); + + // generate measurements when feasible + final SortedSet measurements = new TreeSet<>(); + for (final AbsoluteDate date : dates) { + if (measurementIsFeasible(date)) { + // a measurement is feasible at this date + measurements.add(getBuilder().build(date, interpolators)); + } + } + + return measurements; + + } + + /** Check if a measurement is feasible at some date. + * @param date date to check + * @return true if measurement if feasible + * @since 12.0 + */ + protected abstract boolean measurementIsFeasible(AbsoluteDate date); + } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java index b06f3ac50e..14704ba7b2 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.AngularAzEl; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -35,6 +38,11 @@ public class AngularAzElBuilder extends AbstractMeasurementBuilder /** Ground station from which measurement is performed. */ private final GroundStation station; + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param station ground station from which measurement is performed @@ -47,17 +55,17 @@ public AngularAzElBuilder(final CorrelatedRandomVectorGenerator noiseSource, final double[] sigma, final double[] baseWeight, final ObservableSatellite satellite) { super(noiseSource, sigma, baseWeight, satellite); - this.station = station; + this.station = station; + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public AngularAzEl build(final SpacecraftState[] states) { + public AngularAzEl build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double[] sigma = getTheoreticalStandardDeviation(); final double[] baseWeight = getBaseWeight(); - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final AngularAzEl dummy = new AngularAzEl(station, relevant[0].getDate(), @@ -78,7 +86,7 @@ public AngularAzEl build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - final double[] angular = dummy.estimate(0, 0, relevant).getEstimatedValue(); + final double[] angular = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue(); // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/AngularRaDecBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/AngularRaDecBuilder.java index 62236c6170..043d4b55cd 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/AngularRaDecBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/AngularRaDecBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,8 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.AngularRaDec; import org.orekit.estimation.measurements.EstimationModifier; @@ -23,6 +25,7 @@ import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.frames.Frame; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -39,6 +42,11 @@ public class AngularRaDecBuilder extends AbstractMeasurementBuilder interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double[] sigma = getTheoreticalStandardDeviation(); final double[] baseWeight = getBaseWeight(); - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final AngularRaDec dummy = new AngularRaDec(station, referenceFrame, relevant[0].getDate(), @@ -84,7 +92,7 @@ public AngularRaDec build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - final double[] angular = dummy.estimate(0, 0, relevant).getEstimatedValue(); + final double[] angular = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue(); // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilder.java index 53aefec1dd..bf7b15c769 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Mark Rutten +/* Copyright 2002-2023 Mark Rutten * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.BistaticRange; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -39,6 +42,11 @@ public class BistaticRangeBuilder extends AbstractMeasurementBuilder interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final BistaticRange dummy = new BistaticRange(emitter, receiver, relevant[0].getDate(), @@ -82,7 +90,7 @@ public BistaticRange build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double range = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double range = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilder.java index dfbd67e791..98b0e9499a 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.BistaticRangeRate; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -38,6 +41,11 @@ public class BistaticRangeRateBuilder extends AbstractMeasurementBuilder interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final BistaticRangeRate dummy = new BistaticRangeRate(emitter, receiver, relevant[0].getDate(), @@ -81,7 +89,7 @@ public BistaticRangeRate build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double brr = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double brr = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/ContinuousScheduler.java b/src/main/java/org/orekit/estimation/measurements/generation/ContinuousScheduler.java index 2e1413475f..08d7a7c834 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/ContinuousScheduler.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/ContinuousScheduler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,13 +16,7 @@ */ package org.orekit.estimation.measurements.generation; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; - import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.time.DatesSelector; @@ -58,30 +52,9 @@ public ContinuousScheduler(final MeasurementBuilder builder, final DatesSelec /** {@inheritDoc} */ @Override - public SortedSet generate(final List interpolators) { - - // select dates in the current step, using arbitrarily interpolator 0 - // as all interpolators cover the same range - final List dates = getSelector().selectDates(interpolators.get(0).getPreviousState().getDate(), - interpolators.get(0).getCurrentState().getDate()); - - // generate measurements when feasible - final SortedSet measurements = new TreeSet<>(); - for (final AbsoluteDate date : dates) { - - // interpolate states at measurement date - final SpacecraftState[] states = new SpacecraftState[interpolators.size()]; - for (int i = 0; i < states.length; ++i) { - states[i] = interpolators.get(i).getInterpolatedState(date); - } - - // generate measurement - measurements.add(getBuilder().build(states)); - - } - - return measurements; - + public boolean measurementIsFeasible(final AbsoluteDate date) { + return true; } + } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/EventBasedScheduler.java b/src/main/java/org/orekit/estimation/measurements/generation/EventBasedScheduler.java index 88fe0d3e77..88f97445df 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/EventBasedScheduler.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/EventBasedScheduler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,17 +16,12 @@ */ package org.orekit.estimation.measurements.generation; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; - -import org.hipparchus.ode.events.Action; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.AdapterDetector; import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.DatesSelector; import org.orekit.utils.TimeSpanMap; @@ -101,33 +96,8 @@ public EventBasedScheduler(final MeasurementBuilder builder, final DatesSelec /** {@inheritDoc} */ @Override - public SortedSet generate(final List interpolators) { - - // select dates in the current step, using arbitrarily interpolator 0 - // as all interpolators cover the same range - final List dates = getSelector().selectDates(interpolators.get(0).getPreviousState().getDate(), - interpolators.get(0).getCurrentState().getDate()); - - // generate measurements when feasible - final SortedSet measurements = new TreeSet<>(); - for (final AbsoluteDate date : dates) { - if (feasibility.get(date)) { - // a measurement is feasible at this date - - // interpolate states at measurement date - final SpacecraftState[] states = new SpacecraftState[interpolators.size()]; - for (int i = 0; i < states.length; ++i) { - states[i] = interpolators.get(i).getInterpolatedState(date); - } - - // generate measurement - measurements.add(getBuilder().build(states)); - - } - } - - return measurements; - + public boolean measurementIsFeasible(final AbsoluteDate date) { + return feasibility.get(date); } /** Adapter for managing feasibility status changes. */ @@ -150,22 +120,29 @@ public void init(final SpacecraftState s0, final AbsoluteDate t) { /** {@inheritDoc} */ @Override - public Action eventOccurred(final SpacecraftState s, final boolean increasing) { - - // find the feasibility status AFTER the current date - final boolean statusAfter = signSemantic.measurementIsFeasible(increasing ? +1 : -1); - - // store either status or its opposite according to propagation direction - if (forward) { - // forward propagation - feasibility.addValidAfter(statusAfter, s.getDate(), false); - } else { - // backward propagation - feasibility.addValidBefore(!statusAfter, s.getDate(), false); - } - - // delegate to wrapped detector - return super.eventOccurred(s, increasing); + public EventHandler getHandler() { + + final EventDetector rawDetector = getDetector(); + final EventHandler rawHandler = rawDetector.getHandler(); + + return (state, detector, increasing) -> { + + // find the feasibility status AFTER the current date + final boolean statusAfter = signSemantic.measurementIsFeasible(increasing ? +1 : -1); + + // store either status or its opposite according to propagation direction + if (forward) { + // forward propagation + feasibility.addValidAfter(statusAfter, state.getDate(), false); + } else { + // backward propagation + feasibility.addValidBefore(!statusAfter, state.getDate(), false); + } + + // delegate to wrapped detector + return rawHandler.eventOccurred(state, rawDetector, increasing); + + }; } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/FDOABuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/FDOABuilder.java new file mode 100644 index 0000000000..8375b34cb5 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/generation/FDOABuilder.java @@ -0,0 +1,114 @@ +/* Copyright 2023 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.generation; + +import java.util.Map; + +import org.hipparchus.random.CorrelatedRandomVectorGenerator; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.FDOA; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; + +/** Builder for {@link FDOA} measurements. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class FDOABuilder extends AbstractMeasurementBuilder { + + /** Prime ground station. */ + private final GroundStation primeStation; + + /** Second ground station. */ + private final GroundStation secondStation; + + /** Centre frequency of the signal emitted from the satellite. */ + private final double centreFrequency; + + /** Satellite related to this builder. */ + private final ObservableSatellite satellite; + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param primeStation ground station that gives the date of the measurement + * @param secondStation ground station that gives the measurement + * @param centreFrequency satellite emitter frequency + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellite satellite related to this builder + */ + public FDOABuilder(final CorrelatedRandomVectorGenerator noiseSource, + final GroundStation primeStation, + final GroundStation secondStation, + final double centreFrequency, + final double sigma, final double baseWeight, + final ObservableSatellite satellite) { + super(noiseSource, sigma, baseWeight, satellite); + this.primeStation = primeStation; + this.secondStation = secondStation; + this.centreFrequency = centreFrequency; + this.satellite = satellite; + } + + /** {@inheritDoc} */ + @Override + public FDOA build(final AbsoluteDate date, final Map interpolators) { + + final double sigma = getTheoreticalStandardDeviation()[0]; + final double baseWeight = getBaseWeight()[0]; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; + + // create a dummy measurement + final FDOA dummy = new FDOA(primeStation, secondStation, centreFrequency, relevant[0].getDate(), + Double.NaN, sigma, baseWeight, satellite); + for (final EstimationModifier modifier : getModifiers()) { + dummy.addModifier(modifier); + } + + // set a reference date for parameters missing one + for (final ParameterDriver driver : dummy.getParametersDrivers()) { + if (driver.getReferenceDate() == null) { + final AbsoluteDate start = getStart(); + final AbsoluteDate end = getEnd(); + driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end); + } + } + + // estimate the perfect value of the measurement + double fdoa = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; + + // add the noise + final double[] noise = getNoise(); + if (noise != null) { + fdoa += noise[0]; + } + + // generate measurement + final FDOA measurement = new FDOA(primeStation, secondStation, centreFrequency, relevant[0].getDate(), + fdoa, sigma, baseWeight, satellite); + for (final EstimationModifier modifier : getModifiers()) { + measurement.addModifier(modifier); + } + return measurement; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/GatheringSubscriber.java b/src/main/java/org/orekit/estimation/measurements/generation/GatheringSubscriber.java new file mode 100644 index 0000000000..530d9872e2 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/generation/GatheringSubscriber.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.generation; + +import java.util.Collections; +import java.util.Comparator; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.orekit.estimation.measurements.ComparableMeasurement; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.time.AbsoluteDate; + + +/** Subscriber that gather all generated measurements in a sorted set. + * @author Luc Maisonobe + * @since 12.0 + */ +public class GatheringSubscriber implements GeneratedMeasurementSubscriber { + + /** Set for holding measurements. */ + private SortedSet> measurements; + + /** Simple constructor. + */ + public GatheringSubscriber() { + measurements = Collections.emptySortedSet(); + } + + /** {@inheritDoc} */ + @Override + public void init(final AbsoluteDate start, final AbsoluteDate end) { + final Comparator> comparator = end.isAfterOrEqualTo(start) ? + Comparator.naturalOrder() : + Comparator.reverseOrder(); + measurements = new TreeSet<>(comparator); + } + + /** {@inheritDoc} */ + @Override + public void handleGeneratedMeasurement(final ObservedMeasurement measurement) { + measurements.add(measurement); + } + + /** Get generated measurements. + *

    + * The measurements are sorted according to {@link ComparableMeasurement} if + * generation was chronological, or reversed {@link ComparableMeasurement} if + * generation was non-chronological. + *

    + * @return unmodifiable view of generated measurements + */ + public SortedSet> getGeneratedMeasurements() { + return Collections.unmodifiableSortedSet(measurements); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/GeneratedMeasurementSubscriber.java b/src/main/java/org/orekit/estimation/measurements/generation/GeneratedMeasurementSubscriber.java new file mode 100644 index 0000000000..465ca5d8b2 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/generation/GeneratedMeasurementSubscriber.java @@ -0,0 +1,45 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.generation; + +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.time.AbsoluteDate; + + +/** Interface for subscribing to generated {@link ObservedMeasurement measurements} events. + * @author Luc Maisonobe + * @since 12.0 + */ +public interface GeneratedMeasurementSubscriber { + + /** Initialize subscriber at the start of a measurements generation. + *

    + * This method is called once at the start of the measurements generation. It + * may be used by the subscriber to initialize some internal data + * if needed. + *

    + * @param start start of the measurements time span + * @param end end of the measurements time span + */ + void init(AbsoluteDate start, AbsoluteDate end); + + /** Handle a generated measurement. + * @param measurement measurements that has just been generated + */ + void handleGeneratedMeasurement(ObservedMeasurement measurement); + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/Generator.java b/src/main/java/org/orekit/estimation/measurements/generation/Generator.java index 689b73d52e..59eb8b07f1 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/Generator.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/Generator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,12 @@ package org.orekit.estimation.measurements.generation; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; @@ -27,7 +32,9 @@ import org.orekit.propagation.PropagatorsParallelizer; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.sampling.MultiSatStepHandler; +import org.orekit.propagation.sampling.OrekitStepHandler; import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.propagation.sampling.StepHandlerMultiplexer; import org.orekit.time.AbsoluteDate; @@ -37,17 +44,33 @@ */ public class Generator { + /** Observable satellites. + * @since 12.0 + */ + private final List observableSatellites; + /** Propagators. */ private final List propagators; - /** Sequences generators. */ - private final List> schedulers; + /** Schedulers for multiple satellites measurements. */ + private final List>> multiSatSchedulers; + + /** Schedulers for single satellite measurements. */ + private final Map>>> singleSatSchedulers; + + /** Subscribers for generated measurements events. + * @since 12.0 + */ + private final List subscribers; /** Build a generator with no sequences generator. */ public Generator() { - this.propagators = new ArrayList<>(); - this.schedulers = new ArrayList<>(); + this.observableSatellites = new ArrayList<>(); + this.propagators = new ArrayList<>(); + this.multiSatSchedulers = new ArrayList<>(); + this.singleSatSchedulers = new HashMap<>(); + this.subscribers = new ArrayList<>(); } /** Add a propagator. @@ -55,8 +78,10 @@ public Generator() { * @return satellite satellite propagated by the propagator */ public ObservableSatellite addPropagator(final Propagator propagator) { + final ObservableSatellite os = new ObservableSatellite(propagators.size()); + observableSatellites.add(os); propagators.add(propagator); - return new ObservableSatellite(propagators.size() - 1); + return os; } /** Get a registered propagator. @@ -72,70 +97,251 @@ public Propagator getPropagator(final ObservableSatellite satellite) { * @param the type of the measurement */ public > void addScheduler(final Scheduler scheduler) { - schedulers.add(scheduler); + final ObservableSatellite[] satellites = scheduler.getBuilder().getSatellites(); + if (satellites.length == 1) { + // this scheduler manages only one satellite + // we can let the individual propagator handle it + List>> list = singleSatSchedulers.get(satellites[0]); + if (list == null) { + list = new ArrayList<>(); + singleSatSchedulers.put(satellites[0], list); + } + list.add(scheduler); + } else { + // this scheduler manages several satellites at once + // we need to handle it at top level + multiSatSchedulers.add(scheduler); + } + } + + /** Add a subscriber. + * @param subscriber to add + * @see GatheringSubscriber + * @since 12.0 + */ + public void addSubscriber(final GeneratedMeasurementSubscriber subscriber) { + subscribers.add(subscriber); } /** Generate measurements. * @param start start of the measurements time span * @param end end of the measurements time span - * @return generated measurements */ - public SortedSet> generate(final AbsoluteDate start, final AbsoluteDate end) { + public void generate(final AbsoluteDate start, final AbsoluteDate end) { - // initialize schedulers - for (final Scheduler scheduler : schedulers) { - scheduler.init(start, end); + // set up top level handler + final MultipleSatGeneratorHandler globalHandler = + new MultipleSatGeneratorHandler(multiSatSchedulers, subscribers, + observableSatellites, end.isAfterOrEqualTo(start)); + + // set up low level handlers + for (final Map.Entry>>> entry : singleSatSchedulers.entrySet()) { + final StepHandlerMultiplexer multiplexer = propagators.get(entry.getKey().getPropagatorIndex()).getMultiplexer(); + for (final Scheduler scheduler : entry.getValue()) { + multiplexer.add(new SingleSatGeneratorHandler<>(scheduler, globalHandler)); + } } - // set up parallelized propagators - final GeneratorHandler handler = new GeneratorHandler(schedulers); - final PropagatorsParallelizer parallelizer = new PropagatorsParallelizer(propagators, handler); + // prepare parallelized generation + final PropagatorsParallelizer parallelizer = new PropagatorsParallelizer(propagators, globalHandler); // generate the measurements parallelizer.propagate(start, end); - return handler.getMeasurements(); + // clean up low level handlers + for (final Map.Entry>>> entry : singleSatSchedulers.entrySet()) { + // we need to clean up the step handlers in two loops to avoid concurrent modification exception + final StepHandlerMultiplexer multiplexer = propagators.get(entry.getKey().getPropagatorIndex()).getMultiplexer(); + final List toBeRemoved = new ArrayList<>(); + for (final OrekitStepHandler handler : multiplexer.getHandlers()) { + if (handler instanceof SingleSatGeneratorHandler && + ((SingleSatGeneratorHandler) handler).globalHandler == globalHandler) { + toBeRemoved.add(handler); + } + } + for (final OrekitStepHandler handler : toBeRemoved) { + multiplexer.remove(handler); + } + } + + } + + /** Handler for measurements generation steps, single satellite case. + *

    + * These handlers are called from the individual propagators threads. + * This means they generate measurements in parallel. + *

    + * @param the type of the measurement + * @since 12.0 + */ + private static class SingleSatGeneratorHandler> implements OrekitStepHandler { + + /** Scheduler. */ + private final Scheduler scheduler; + + /** Satellite related to this scheduler. */ + private final ObservableSatellite satellite; + + /** Global handler. */ + private final MultipleSatGeneratorHandler globalHandler; + + /** Simple constructor. + * @param scheduler scheduler + * @param globalHandler global handler + */ + SingleSatGeneratorHandler(final Scheduler scheduler, final MultipleSatGeneratorHandler globalHandler) { + this.scheduler = scheduler; + this.satellite = scheduler.getBuilder().getSatellites()[0]; + this.globalHandler = globalHandler; + } + + /** {@inheritDoc} */ + @Override + public void init(final SpacecraftState state0, final AbsoluteDate t) { + scheduler.init(state0.getDate(), t); + } + + /** {@inheritDoc} */ + @Override + public void handleStep(final OrekitStepInterpolator interpolator) { + globalHandler.addMeasurements(scheduler.generate(Collections.singletonMap(satellite, interpolator))); + } } - /** Handler for measurements generation steps. */ - private static class GeneratorHandler implements MultiSatStepHandler { + /** Handler for measurements generation steps. + *

    + * This handler is called from the propagator parallelizer thread. + * The parallelizer thread is called after the individual propagators thread, + * which may already have produced measurements ahead of time, so we must + * take care than within each step we handle only the measurements that belong + * to this step. + *

    + */ + private static class MultipleSatGeneratorHandler implements MultiSatStepHandler { /** Sequences generators. */ - private final List> schedulers; + private final List>> schedulers; + + /** Subscribers for generated measurements events. + * @since 12.0 + */ + private final List subscribers; + + /** Observable satellites. + * @since 12.0 + */ + private final List observableSatellites; - /** Set for holding measurements. */ - private final SortedSet> measurements; + /** Storage for sorted measurements within one step. + * @since 12.0 + */ + private final SortedSet> generated; + + /** Forward generation indicator. + * @since 12.0 + */ + private final boolean forward; /** Simple constructor. * @param schedulers sequences generators + * @param subscribers subscribers for generated measurements events + * @param observableSatellites observable satellites + * @param forward if true, generation is forward + * @since 12.0 */ - GeneratorHandler(final List> schedulers) { - this.schedulers = schedulers; - this.measurements = new TreeSet<>(); + MultipleSatGeneratorHandler(final List>> schedulers, + final List subscribers, + final List observableSatellites, final boolean forward) { + + // measurements comparator, consistent with generation direction + final Comparator> comparator = forward ? Comparator.naturalOrder() : Comparator.reverseOrder(); + + this.schedulers = schedulers; + this.subscribers = subscribers; + this.observableSatellites = observableSatellites; + this.generated = new TreeSet<>(comparator); + this.forward = forward; + } /** {@inheritDoc} */ @Override public void init(final List states0, final AbsoluteDate t) { + + final AbsoluteDate start = states0.get(0).getDate(); + + // initialize schedulers for (final Scheduler scheduler : schedulers) { - scheduler.init(states0.get(0).getDate(), t); + scheduler.init(start, t); } + + // initialize subscribers + for (final GeneratedMeasurementSubscriber subscriber : subscribers) { + subscriber.init(start, t); + } + } /** {@inheritDoc} */ @Override public void handleStep(final List interpolators) { - for (final Scheduler scheduler : schedulers) { - measurements.addAll(scheduler.generate(interpolators)); + + // prepare interpolators map + final Map interpolatorsMap = + new HashMap<>(interpolators.size()); + for (int i = 0; i < interpolators.size(); ++i) { + interpolatorsMap.put(observableSatellites.get(i), interpolators.get(i)); + } + final AbsoluteDate lastDate = interpolators.get(0).getCurrentState().getDate(); + + synchronized (generated) { + + // generate measurements, looping over schedulers + for (final Scheduler> scheduler : schedulers) { + generated.addAll(scheduler.generate(interpolatorsMap)); + } + + // now that we have all measurements properly sorted, we can feed them to subscribers + for (final Iterator> iterator = generated.iterator(); iterator.hasNext();) { + final ObservedMeasurement measurement = iterator.next(); + if (forward == lastDate.isAfterOrEqualTo(measurement)) { + // this measurement belongs to the current step + for (final GeneratedMeasurementSubscriber subscriber : subscribers) { + subscriber.handleGeneratedMeasurement(measurement); + } + iterator.remove(); + } else { + // this measurement belongs to an upcoming step ; we don't handle it yet as more + // intermediate measurements may be produced by low level propagators threads + break; + } + } + + } + + } + + /** {@inheritDoc} */ + public void finish(final List finalStates) { + synchronized (generated) { + for (final ObservedMeasurement measurement : generated) { + for (final GeneratedMeasurementSubscriber subscriber : subscribers) { + subscriber.handleGeneratedMeasurement(measurement); + } + } + generated.clear(); } } - /** Get the generated measurements. - * @return generated measurements + /** Add measurements performed by a low level handler. + * @param measurements measurements to add + * @since 12.0 */ - public SortedSet> getMeasurements() { - return measurements; + private void addMeasurements(final SortedSet> measurements) { + synchronized (generated) { + generated.addAll(measurements); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java index b05dcb31c0..e43fe5e5c5 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,11 +16,14 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -34,11 +37,20 @@ public class InterSatellitesPhaseBuilder extends AbstractMeasurementBuilder interpolators) { - final ObservableSatellite[] satellites = getSatellites(); final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; final SpacecraftState[] relevant = new SpacecraftState[] { - states[satellites[0].getPropagatorIndex()], - states[satellites[1].getPropagatorIndex()] + interpolators.get(local).getInterpolatedState(date), + interpolators.get(remote).getInterpolatedState(date) }; - final SpacecraftState state = states[satellites[0].getPropagatorIndex()]; // create a dummy measurement - final InterSatellitesPhase dummy = new InterSatellitesPhase(satellites[0], satellites[1], state.getDate(), + final InterSatellitesPhase dummy = new InterSatellitesPhase(local, remote, relevant[0].getDate(), Double.NaN, wavelength, sigma, baseWeight); for (final EstimationModifier modifier : getModifiers()) { dummy.addModifier(modifier); @@ -80,7 +92,7 @@ public InterSatellitesPhase build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double phase = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double phase = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); @@ -89,7 +101,7 @@ public InterSatellitesPhase build(final SpacecraftState[] states) { } // generate measurement - final InterSatellitesPhase measurement = new InterSatellitesPhase(satellites[0], satellites[1], state.getDate(), + final InterSatellitesPhase measurement = new InterSatellitesPhase(local, remote, relevant[0].getDate(), phase, wavelength, sigma, baseWeight); for (final EstimationModifier modifier : getModifiers()) { measurement.addModifier(modifier); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java index 184b729233..dadcbf7c81 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,11 +16,14 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.InterSatellitesRange; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -34,6 +37,17 @@ public class InterSatellitesRangeBuilder extends AbstractMeasurementBuilder interpolators) { - final ObservableSatellite[] satellites = getSatellites(); final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; final SpacecraftState[] relevant = new SpacecraftState[] { - states[satellites[0].getPropagatorIndex()], - states[satellites[1].getPropagatorIndex()] + interpolators.get(local).getInterpolatedState(date), + interpolators.get(remote).getInterpolatedState(date) }; - final SpacecraftState state = states[satellites[0].getPropagatorIndex()]; // create a dummy measurement - final InterSatellitesRange dummy = new InterSatellitesRange(satellites[0], satellites[1], twoway, state.getDate(), + final InterSatellitesRange dummy = new InterSatellitesRange(local, remote, twoway, relevant[0].getDate(), Double.NaN, sigma, baseWeight); for (final EstimationModifier modifier : getModifiers()) { dummy.addModifier(modifier); @@ -80,7 +94,7 @@ public InterSatellitesRange build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double range = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double range = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); @@ -89,7 +103,7 @@ public InterSatellitesRange build(final SpacecraftState[] states) { } // generate measurement - final InterSatellitesRange measurement = new InterSatellitesRange(satellites[0], satellites[1], twoway, state.getDate(), + final InterSatellitesRange measurement = new InterSatellitesRange(local, remote, twoway, relevant[0].getDate(), range, sigma, baseWeight); for (final EstimationModifier modifier : getModifiers()) { measurement.addModifier(modifier); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/MeasurementBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/MeasurementBuilder.java index 3068564785..b867346972 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/MeasurementBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/MeasurementBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,10 +17,12 @@ package org.orekit.estimation.measurements.generation; import java.util.List; +import java.util.Map; import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; @@ -53,10 +55,18 @@ public interface MeasurementBuilder> { */ List> getModifiers(); + /** Get the satellites related to this measurement. + * @return satellites related to this measurement + * @since 12.0 + */ + ObservableSatellite[] getSatellites(); + /** Generate a single measurement. - * @param states all spacecraft states (i.e. including ones that may not be relevant for the current builder) + * @param date measurement date + * @param interpolators interpolators relevant for this builder * @return generated measurement + * @since 12.0 */ - T build(SpacecraftState[] states); + T build(AbsoluteDate date, Map interpolators); } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/MultiplexedMeasurementBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/MultiplexedMeasurementBuilder.java new file mode 100644 index 0000000000..0626628bcb --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/generation/MultiplexedMeasurementBuilder.java @@ -0,0 +1,115 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.generation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.MultiplexedMeasurement; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.time.AbsoluteDate; + + +/** Builder for {@link MultiplexedMeasurement} measurements. + * @author Luc Maisonobe + * @since 12.0 + */ +public class MultiplexedMeasurementBuilder implements MeasurementBuilder { + + /** Builders for individual measurements. */ + private final List> builders; + + /** Satellites related to this builder. */ + private final ObservableSatellite[] satellites; + + /** Modifiers that apply to the measurement.*/ + private final List> modifiers; + + /** Simple constructor. + * @param builders builders for multiplexed measurements + */ + public MultiplexedMeasurementBuilder(final List> builders) { + this.builders = builders; + this.modifiers = new ArrayList<>(); + + final List list = new ArrayList<>(); + for (final MeasurementBuilder builder : builders) { + for (final ObservableSatellite satellite : builder.getSatellites()) { + if (!list.contains(satellite)) { + list.add(satellite); + } + } + } + this.satellites = list.toArray(new ObservableSatellite[0]); + + } + + /** {@inheritDoc} + *

    + * This implementation stores the time span of the measurements generation. + *

    + */ + @Override + public void init(final AbsoluteDate start, final AbsoluteDate end) { + for (final MeasurementBuilder builder : builders) { + builder.init(start, end); + } + } + + /** {@inheritDoc} */ + @Override + public void addModifier(final EstimationModifier modifier) { + modifiers.add(modifier); + } + + /** {@inheritDoc} */ + @Override + public List> getModifiers() { + return Collections.unmodifiableList(modifiers); + } + + /** {@inheritDoc} */ + @Override + public MultiplexedMeasurement build(final AbsoluteDate date, final Map interpolators) { + + final List> measurements = new ArrayList<>(builders.size()); + for (final MeasurementBuilder builder : builders) { + measurements.add(builder.build(date, interpolators)); + } + + + // generate measurement + final MultiplexedMeasurement measurement = new MultiplexedMeasurement(measurements); + for (final EstimationModifier modifier : getModifiers()) { + measurement.addModifier(modifier); + } + return measurement; + + } + + /** {@inheritDoc} */ + @Override + public ObservableSatellite[] getSatellites() { + return satellites.clone(); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java new file mode 100644 index 0000000000..15823ec5da --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java @@ -0,0 +1,117 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.generation; + +import java.util.Map; +import java.util.function.ToDoubleFunction; + +import org.hipparchus.random.CorrelatedRandomVectorGenerator; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; + + +/** Builder for {@link OneWayGNSSPhase} measurements. + * @author Luc Maisonobe + * @since 12.0 + */ +public class OneWayGNSSPhaseBuilder extends AbstractMeasurementBuilder { + + /** Wavelength of the phase observed value [m]. */ + private final double wavelength; + + /** Satellite which receives the signal and performs the measurement. */ + private final ObservableSatellite local; + + /** Satellite which simply emits the signal. */ + private final ObservableSatellite remote; + + /** Clock model of the remote satellite that provides clock offset. */ + private ToDoubleFunction remoteClockModel; + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param local satellite which receives the signal and performs the measurement + * @param remote satellite which simply emits the signal + * @param remoteClockModel clock model of the remote satellite that provides clock offset + * @param wavelength phase observed value wavelength (m) + * @param sigma theoretical standard deviation + * @param baseWeight base weight + */ + public OneWayGNSSPhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource, + final ObservableSatellite local, final ObservableSatellite remote, + final ToDoubleFunction remoteClockModel, + final double wavelength, final double sigma, final double baseWeight) { + super(noiseSource, sigma, baseWeight, local, remote); + this.wavelength = wavelength; + this.local = local; + this.remote = remote; + this.remoteClockModel = remoteClockModel; + } + + /** {@inheritDoc} */ + @Override + public OneWayGNSSPhase build(final AbsoluteDate date, final Map interpolators) { + + final double sigma = getTheoreticalStandardDeviation()[0]; + final double baseWeight = getBaseWeight()[0]; + final SpacecraftState[] relevant = new SpacecraftState[] { + interpolators.get(local).getInterpolatedState(date), + interpolators.get(remote).getInterpolatedState(date) + }; + final double offset = remoteClockModel.applyAsDouble(date); + + // create a dummy measurement + final OneWayGNSSPhase dummy = new OneWayGNSSPhase(interpolators.get(remote), offset, date, + Double.NaN, wavelength, sigma, baseWeight, local); + for (final EstimationModifier modifier : getModifiers()) { + dummy.addModifier(modifier); + } + + // set a reference date for parameters missing one + for (final ParameterDriver driver : dummy.getParametersDrivers()) { + if (driver.getReferenceDate() == null) { + final AbsoluteDate start = getStart(); + final AbsoluteDate end = getEnd(); + driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end); + } + } + + // estimate the perfect value of the measurement + double phase = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; + + // add the noise + final double[] noise = getNoise(); + if (noise != null) { + phase += noise[0]; + } + + // generate measurement + final OneWayGNSSPhase measurement = new OneWayGNSSPhase(interpolators.get(remote), offset, date, + phase, wavelength, sigma, baseWeight, local); + for (final EstimationModifier modifier : getModifiers()) { + measurement.addModifier(modifier); + } + return measurement; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java new file mode 100644 index 0000000000..5b044af2f4 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java @@ -0,0 +1,112 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.generation; + +import java.util.Map; +import java.util.function.ToDoubleFunction; + +import org.hipparchus.random.CorrelatedRandomVectorGenerator; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.OneWayGNSSRange; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; + + +/** Builder for {@link OneWayGNSSRange} measurements. + * @author Luc Maisonobe + * @since 12.0 + */ +public class OneWayGNSSRangeBuilder extends AbstractMeasurementBuilder { + + /** Satellite which receives the signal and performs the measurement. */ + private final ObservableSatellite local; + + /** Satellite which simply emits the signal. */ + private final ObservableSatellite remote; + + /** Clock model of the remote satellite that provides clock offset. */ + private ToDoubleFunction remoteClockModel; + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param local satellite which receives the signal and performs the measurement + * @param remote satellite which simply emits the signal + * @param remoteClockModel clock model of the remote satellite that provides clock offset + * @param sigma theoretical standard deviation + * @param baseWeight base weight + */ + public OneWayGNSSRangeBuilder(final CorrelatedRandomVectorGenerator noiseSource, + final ObservableSatellite local, final ObservableSatellite remote, + final ToDoubleFunction remoteClockModel, + final double sigma, final double baseWeight) { + super(noiseSource, sigma, baseWeight, local, remote); + this.local = local; + this.remote = remote; + this.remoteClockModel = remoteClockModel; + } + + /** {@inheritDoc} */ + @Override + public OneWayGNSSRange build(final AbsoluteDate date, final Map interpolators) { + + final double sigma = getTheoreticalStandardDeviation()[0]; + final double baseWeight = getBaseWeight()[0]; + final SpacecraftState[] relevant = new SpacecraftState[] { + interpolators.get(local).getInterpolatedState(date), + interpolators.get(remote).getInterpolatedState(date) + }; + final double offset = remoteClockModel.applyAsDouble(date); + + // create a dummy measurement + final OneWayGNSSRange dummy = new OneWayGNSSRange(interpolators.get(remote), offset, date, + Double.NaN, sigma, baseWeight, local); + for (final EstimationModifier modifier : getModifiers()) { + dummy.addModifier(modifier); + } + + // set a reference date for parameters missing one + for (final ParameterDriver driver : dummy.getParametersDrivers()) { + if (driver.getReferenceDate() == null) { + final AbsoluteDate start = getStart(); + final AbsoluteDate end = getEnd(); + driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end); + } + } + + // estimate the perfect value of the measurement + double range = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; + + // add the noise + final double[] noise = getNoise(); + if (noise != null) { + range += noise[0]; + } + + // generate measurement + final OneWayGNSSRange measurement = new OneWayGNSSRange(interpolators.get(remote), offset, date, + range, sigma, baseWeight, local); + for (final EstimationModifier modifier : getModifiers()) { + measurement.addModifier(modifier); + } + return measurement; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java index e8315f61ce..0956dc8010 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.PV; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -32,6 +35,11 @@ */ public class PVBuilder extends AbstractMeasurementBuilder { + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param sigmaPosition theoretical standard deviation on position components @@ -48,16 +56,16 @@ public PVBuilder(final CorrelatedRandomVectorGenerator noiseSource, }, new double[] { baseWeight }, satellite); + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public PV build(final SpacecraftState[] states) { + public PV build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double[] sigma = getTheoreticalStandardDeviation(); final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final PV dummy = new PV(relevant[0].getDate(), Vector3D.NaN, Vector3D.NaN, @@ -76,7 +84,7 @@ public PV build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - final double[] pv = dummy.estimate(0, 0, relevant).getEstimatedValue(); + final double[] pv = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue(); // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/PositionBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/PositionBuilder.java index 81325e59bd..230da99115 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/PositionBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/PositionBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.Position; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -32,6 +35,11 @@ */ public class PositionBuilder extends AbstractMeasurementBuilder { + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param sigma theoretical standard deviation @@ -42,16 +50,16 @@ public PositionBuilder(final CorrelatedRandomVectorGenerator noiseSource, final double sigma, final double baseWeight, final ObservableSatellite satellite) { super(noiseSource, sigma, baseWeight, satellite); + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public Position build(final SpacecraftState[] states) { + public Position build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final Position dummy = new Position(relevant[0].getDate(), Vector3D.NaN, sigma, baseWeight, satellite); @@ -69,7 +77,7 @@ public Position build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - final double[] position = dummy.estimate(0, 0, relevant).getEstimatedValue(); + final double[] position = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue(); // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/RangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/RangeBuilder.java index e979e74791..c4eaa4b472 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/RangeBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/RangeBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.Range; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -38,6 +41,11 @@ public class RangeBuilder extends AbstractMeasurementBuilder { /** Flag indicating whether it is a two-way measurement. */ private final boolean twoway; + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param station ground station from which measurement is performed @@ -51,18 +59,18 @@ public RangeBuilder(final CorrelatedRandomVectorGenerator noiseSource, final double sigma, final double baseWeight, final ObservableSatellite satellite) { super(noiseSource, sigma, baseWeight, satellite); - this.station = station; - this.twoway = twoWay; + this.station = station; + this.twoway = twoWay; + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public Range build(final SpacecraftState[] states) { + public Range build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final Range dummy = new Range(station, twoway, relevant[0].getDate(), Double.NaN, sigma, baseWeight, satellite); @@ -80,7 +88,7 @@ public Range build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double range = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double range = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/RangeRateBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/RangeRateBuilder.java index f11b2ea72a..c061198a08 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/RangeRateBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/RangeRateBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.RangeRate; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -38,6 +41,11 @@ public class RangeRateBuilder extends AbstractMeasurementBuilder { /** Flag indicating whether it is a two-way measurement. */ private final boolean twoway; + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param station ground station from which measurement is performed @@ -51,18 +59,18 @@ public RangeRateBuilder(final CorrelatedRandomVectorGenerator noiseSource, final double sigma, final double baseWeight, final ObservableSatellite satellite) { super(noiseSource, sigma, baseWeight, satellite); - this.station = station; - this.twoway = twoWay; + this.station = station; + this.twoway = twoWay; + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public RangeRate build(final SpacecraftState[] states) { + public RangeRate build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final RangeRate dummy = new RangeRate(station, relevant[0].getDate(), Double.NaN, sigma, baseWeight, twoway, satellite); @@ -80,7 +88,7 @@ public RangeRate build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double rangeRate = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double rangeRate = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/Scheduler.java b/src/main/java/org/orekit/estimation/measurements/generation/Scheduler.java index 76c7ac2a69..50391d7a38 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/Scheduler.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/Scheduler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,10 @@ */ package org.orekit.estimation.measurements.generation; -import java.util.List; +import java.util.Map; import java.util.SortedSet; +import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; @@ -31,6 +32,12 @@ */ public interface Scheduler> { + /** Get the builder associated with this scheduler. + * @return builder associated with this scheduler + * @since 12.0 + */ + MeasurementBuilder getBuilder(); + /** Initialize scheduler at the start of a measurements generation. *

    * This method is called once at the start of the measurements generation. It @@ -46,7 +53,8 @@ public interface Scheduler> { /** Generate a sequence of measurements. * @param interpolators interpolators for spacecraft states * @return generated measurements + * @since 12.0 */ - SortedSet generate(List interpolators); + SortedSet generate(Map interpolators); } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/SignSemantic.java b/src/main/java/org/orekit/estimation/measurements/generation/SignSemantic.java index 32cfe382d0..6162fc6e3e 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/SignSemantic.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/SignSemantic.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/generation/TDOABuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/TDOABuilder.java index 7dad4f2204..6c71770286 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/TDOABuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/TDOABuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.TDOA; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -38,6 +41,11 @@ public class TDOABuilder extends AbstractMeasurementBuilder { /** Second ground station. */ private final GroundStation secondStation; + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param primeStation ground station that gives the date of the measurement @@ -54,16 +62,16 @@ public TDOABuilder(final CorrelatedRandomVectorGenerator noiseSource, super(noiseSource, sigma, baseWeight, satellite); this.primeStation = primeStation; this.secondStation = secondStation; + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public TDOA build(final SpacecraftState[] states) { + public TDOA build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final TDOA dummy = new TDOA(primeStation, secondStation, relevant[0].getDate(), @@ -82,7 +90,7 @@ public TDOA build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double tdoa = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double tdoa = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilder.java index 8d711f4734..1143f5114d 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.generation; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.TurnAroundRange; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -38,6 +41,11 @@ public class TurnAroundRangeBuilder extends AbstractMeasurementBuilder interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final TurnAroundRange dummy = new TurnAroundRange(primaryStation, secondaryStation, relevant[0].getDate(), @@ -81,7 +89,7 @@ public TurnAroundRange build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double range = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double range = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/generation/package-info.java b/src/main/java/org/orekit/estimation/measurements/generation/package-info.java index b0eb250963..e81a02c3e8 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/package-info.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractCycleSlipDetector.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractCycleSlipDetector.java index fc6eebe420..95b77a5244 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractCycleSlipDetector.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractCycleSlipDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,8 +21,8 @@ import java.util.List; import java.util.Map; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.Frequency; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractDualFrequencyCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractDualFrequencyCombination.java index 8b667207ea..5b3e62876a 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractDualFrequencyCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractDualFrequencyCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,12 +22,10 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.gnss.CombinedObservationData; -import org.orekit.gnss.CombinedObservationDataSet; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.Frequency; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.utils.Constants; @@ -156,8 +154,9 @@ public CombinedObservationDataSet combine(final ObservationDataSet observations) } } - return new CombinedObservationDataSet(observations.getHeader(), observations.getSatelliteSystem(), - observations.getPrnNumber(), observations.getDate(), + return new CombinedObservationDataSet(observations.getSatellite().getSystem(), + observations.getSatellite().getPRN(), + observations.getDate(), observations.getRcvrClkOffset(), combined); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractLambdaMethod.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractLambdaMethod.java index 21f7013839..9aaf523712 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractLambdaMethod.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractLambdaMethod.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractSingleFrequencyCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractSingleFrequencyCombination.java index e277616db3..b13782384d 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractSingleFrequencyCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractSingleFrequencyCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,14 +22,11 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.gnss.CombinedObservationData; -import org.orekit.gnss.CombinedObservationDataSet; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.Frequency; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.ObservationType; -import org.orekit.gnss.RinexObservationHeader; import org.orekit.gnss.SatelliteSystem; /** Base class for single frequency combination of measurements. @@ -64,11 +61,6 @@ public String getName() { @Override public CombinedObservationDataSet combine(final ObservationDataSet observations) { - // Rinex file header - final RinexObservationHeader header = observations.getHeader(); - // Rinex version to integer - final int version = (int) header.getRinexVersion(); - // Initialize list of measurements final List pseudoRanges = new ArrayList<>(); final List phases = new ArrayList<>(); @@ -87,17 +79,19 @@ public CombinedObservationDataSet combine(final ObservationDataSet observations) // Initialize list of combined observation data final List combined = new ArrayList<>(); - for (int i = 0; i < phases.size(); i++) { - for (int j = 0; j < pseudoRanges.size(); j++) { - final boolean combine = isCombinationPossible(version, phases.get(i), pseudoRanges.get(j)); - if (combine) { - combined.add(combine(phases.get(i), pseudoRanges.get(j))); + for (final ObservationData phase : phases) { + for (final ObservationData pseudoRange : pseudoRanges) { + // Single-frequency combination is possible only if data frequencies are the same + if (phase.getObservationType().getFrequency(system) == pseudoRange.getObservationType().getFrequency(system) && + phase.getObservationType().getSignalCode() == pseudoRange.getObservationType().getSignalCode()) { + combined.add(combine(phase, pseudoRange)); } } } - return new CombinedObservationDataSet(observations.getHeader(), observations.getSatelliteSystem(), - observations.getPrnNumber(), observations.getDate(), + return new CombinedObservationDataSet(observations.getSatellite().getSystem(), + observations.getSatellite().getPRN(), + observations.getDate(), observations.getRcvrClkOffset(), combined); } @@ -152,29 +146,4 @@ public CombinedObservationData combine(final ObservationData phase, final Observ */ protected abstract double getCombinedValue(double phase, double pseudoRange); - /** - * Verifies if two observation data can be combine. - * @param version Rinex file version (integer part) - * @param phase phase measurement - * @param pseudoRange pseudoRange measurement - * @return true if observation data can be combined - */ - private boolean isCombinationPossible(final int version, final ObservationData phase, final ObservationData pseudoRange) { - // Observation types - final ObservationType obsType1 = phase.getObservationType(); - final ObservationType obsType2 = pseudoRange.getObservationType(); - // Single-frequency combination is possible only if data frequencies are the same - if (obsType1.getFrequency(system) == obsType2.getFrequency(system)) { - // Switch on Rinex version - switch (version) { - case 2 : return true; - case 3 : return obsType1.getSignalCode() == obsType2.getSignalCode(); - default: return false; - } - } else { - // False because observation data have different frequency - return false; - } - } - } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java new file mode 100644 index 0000000000..2288e13edf --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java @@ -0,0 +1,117 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.gnss; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Base class for wind-up effect computation. + * @see Carrier Phase Wind-up Effect + * @param the type of the measurement + * @author Luc Maisonobe + * @since 12.0 + */ +public abstract class AbstractWindUp> implements EstimationModifier { + + /** Emitter dipole. */ + private Dipole emitter; + + /** Receiver dipole. */ + private Dipole receiver; + + /** Cached angular value of wind-up. */ + private double angularWindUp; + + /** Simple constructor. + * @param emitter emitter dipole + * @param receiver receiver dipole + */ + protected AbstractWindUp(final Dipole emitter, final Dipole receiver) { + this.emitter = emitter; + this.receiver = receiver; + angularWindUp = 0.0; + } + + /** {@inheritDoc} + *

    + * Wind-up effect has no parameters, the returned list is always empty. + *

    + */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** Compute rotation from emitter to inertial frame. + * @param estimated estimated measurement to modify + * @return rotation from emitter to inertial frame + */ + protected abstract Rotation emitterToInert(EstimatedMeasurementBase estimated); + + /** Compute rotation from receiver to inertial frame. + * @param estimated estimated measurement to modify + * @return rotation from receiver to inertial frame + */ + protected abstract Rotation receiverToInert(EstimatedMeasurementBase estimated); + + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + // signal line of sight + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + final Vector3D los = participants[1].getPosition().subtract(participants[0].getPosition()).normalize(); + + // get receiver dipole + final Rotation receiverToInert = receiverToInert(estimated); + final Vector3D iReceiver = receiverToInert.applyTo(receiver.getPrimary()); + final Vector3D jReceiver = receiverToInert.applyTo(receiver.getSecondary()); + final Vector3D dReceiver = new Vector3D(1.0, iReceiver, -Vector3D.dotProduct(iReceiver, los), los). + add(Vector3D.crossProduct(los, jReceiver)); + + // get emitter dipole + final Rotation emitterToInert = emitterToInert(estimated); + final Vector3D iEmitter = emitterToInert.applyTo(emitter.getPrimary()); + final Vector3D jEmitter = emitterToInert.applyTo(emitter.getSecondary()); + final Vector3D dEmitter = new Vector3D(1.0, iEmitter, -Vector3D.dotProduct(iEmitter, los), los). + subtract(Vector3D.crossProduct(los, jEmitter)); + + // raw correction + final double correction = FastMath.copySign(Vector3D.angle(dEmitter, dReceiver), + Vector3D.dotProduct(los, Vector3D.crossProduct(dEmitter, dReceiver))); + + // ensure continuity accross measurements + // we assume the various measurements are close enough in time + // (less the one satellite half-turn) so the angles remain close + angularWindUp = MathUtils.normalizeAngle(correction, angularWindUp); + + // update estimate + estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + angularWindUp / MathUtils.TWO_PI); + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AlternatingSampler.java b/src/main/java/org/orekit/estimation/measurements/gnss/AlternatingSampler.java index 0c11155cb4..3d66e834ea 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AlternatingSampler.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AlternatingSampler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityAcceptance.java b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityAcceptance.java index f0a0abaef0..472ae79ef4 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityAcceptance.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityAcceptance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguitySolver.java b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguitySolver.java index 319dc1c1e2..131e776755 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguitySolver.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguitySolver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,7 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; /** Class for solving integer ambiguity problems. * @see LambdaMethod @@ -75,6 +76,9 @@ protected List getFreeAmbiguityDrivers() { stream(). filter(d -> { if (d.isSelected()) { + // in order to make the code generic and compatible with pDriver having + // 1 or several values driven getValue is called with a "random date" + // it should be OK as we take the near number final double near = FastMath.rint(d.getValue()); final double gapMin = near - d.getMinValue(); final double gapMax = d.getMaxValue() - near; @@ -96,28 +100,47 @@ protected int[] getFreeAmbiguityIndirection(final int startIndex, // set up indirection array final List freeDrivers = getFreeAmbiguityDrivers(); + final List measurementsPDriversNames = new ArrayList(); + int totalValuesToEstimate = 0; + for (ParameterDriver driver : freeDrivers) { + totalValuesToEstimate += driver.getNbOfValues(); + } + for (ParameterDriver measDriver : measurementsParametersDrivers) { + for (Span spanMeasurementsParametersDrivers = measDriver.getNamesSpanMap().getFirstSpan(); + spanMeasurementsParametersDrivers != null; spanMeasurementsParametersDrivers = spanMeasurementsParametersDrivers.next()) { + measurementsPDriversNames.add(spanMeasurementsParametersDrivers.getData()); + } + + } + final int n = freeDrivers.size(); - final int[] indirection = new int[n]; + final int[] indirection = new int[totalValuesToEstimate]; + int nb = 0; for (int i = 0; i < n; ++i) { - indirection[i] = -1; - final String name = freeDrivers.get(i).getName(); - for (int k = 0; k < measurementsParametersDrivers.size(); ++k) { - if (name.equals(measurementsParametersDrivers.get(k).getName())) { - indirection[i] = startIndex + k; - break; + + for (Span spanFreeDriver = freeDrivers.get(i).getNamesSpanMap().getFirstSpan(); spanFreeDriver != null; spanFreeDriver = spanFreeDriver.next()) { + indirection[nb] = -1; + + for (int k = 0; k < measurementsPDriversNames.size(); ++k) { + if (spanFreeDriver.getData().equals(measurementsPDriversNames.get(k))) { + indirection[nb] = startIndex + k; + break; + } } - } - if (indirection[i] < 0) { - // the parameter was not found - final StringBuilder builder = new StringBuilder(); - for (final ParameterDriver driver : measurementsParametersDrivers) { - if (builder.length() > 0) { - builder.append(", "); + + if (indirection[nb] < 0) { + // the parameter was not found + final StringBuilder builder = new StringBuilder(); + for (final String driverName : measurementsPDriversNames) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(driverName); } - builder.append(driver.getName()); + throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, + spanFreeDriver.getData(), builder.toString()); } - throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, - name, builder.toString()); + nb++; } } @@ -145,9 +168,21 @@ public List fixIntegerAmbiguities(final int startIndex, // set up Integer Least Square problem final List ambiguities = getAllAmbiguityDrivers(); - final double[] floatAmbiguities = ambiguities.stream().mapToDouble(d -> d.getValue()).toArray(); - final int[] indirection = getFreeAmbiguityIndirection(startIndex, measurementsParametersDrivers); + // construct floatambiguities array + int nbPDriver = 0; + for (ParameterDriver pDriver : ambiguities) { + nbPDriver += pDriver.getNbOfValues(); + } + final double[] floatAmbiguities = new double[nbPDriver]; + int floatAmbRank = 0; + for (ParameterDriver pDriver : ambiguities) { + for (Span span = pDriver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + floatAmbiguities[floatAmbRank++] = span.getData(); + } + } + + final int[] indirection = getFreeAmbiguityIndirection(startIndex, measurementsParametersDrivers); // solve the ILS problem final IntegerLeastSquareSolution[] candidates = solver.solveILS(acceptance.numberOfCandidates(), floatAmbiguities, indirection, covariance); @@ -173,11 +208,13 @@ public List fixIntegerAmbiguities(final int startIndex, // fix the ambiguities final long[] fixedAmbiguities = bestCandidate.getSolution(); final List fixedDrivers = new ArrayList<>(indirection.length); - for (int i = 0; i < indirection.length; ++i) { - final ParameterDriver driver = measurementsParametersDrivers.get(indirection[i] - startIndex); + int nb = 0; + for (int i = 0; i < measurementsParametersDrivers.size(); ++i) { + final ParameterDriver driver = measurementsParametersDrivers.get(indirection[nb] - startIndex); driver.setMinValue(fixedAmbiguities[i]); driver.setMaxValue(fixedAmbiguities[i]); fixedDrivers.add(driver); + nb += driver.getNbOfValues(); } // Update the others parameter drivers accordingly to the fixed integer ambiguity @@ -188,10 +225,14 @@ public List fixIntegerAmbiguities(final int startIndex, subtract(MatrixUtils.createRealVector(toDoubleArray(fixedAmbiguities.length, fixedAmbiguities)))); final RealVector Y = Qab.preMultiply(X); + int entry = 0; for (int i = startIndex + 1; i < covariance.getColumnDimension(); i++) { if (!belongTo(indirection, i)) { final ParameterDriver driver = measurementsParametersDrivers.get(i - startIndex); - driver.setValue(driver.getValue() - Y.getEntry(i - startIndex)); + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + + driver.setValue(driver.getValue(span.getStart()) - Y.getEntry(entry++ - startIndex), span.getStart()); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/CombinationType.java b/src/main/java/org/orekit/estimation/measurements/gnss/CombinationType.java index abc50a9a21..d4093fe394 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/CombinationType.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/CombinationType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/CombinedObservationData.java b/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java similarity index 95% rename from src/main/java/org/orekit/gnss/CombinedObservationData.java rename to src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java index ebff78a18a..c8c2bb016c 100644 --- a/src/main/java/org/orekit/gnss/CombinedObservationData.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,11 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.estimation.measurements.gnss; import java.util.List; -import org.orekit.estimation.measurements.gnss.CombinationType; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.gnss.MeasurementType; /** * Combined observation data. diff --git a/src/main/java/org/orekit/gnss/CombinedObservationDataSet.java b/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationDataSet.java similarity index 83% rename from src/main/java/org/orekit/gnss/CombinedObservationDataSet.java rename to src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationDataSet.java index 1be78bdc9c..1a3fa2c04c 100644 --- a/src/main/java/org/orekit/gnss/CombinedObservationDataSet.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationDataSet.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,11 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.estimation.measurements.gnss; import java.util.Collections; import java.util.List; +import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; @@ -29,9 +30,6 @@ */ public class CombinedObservationDataSet implements TimeStamped { - /** Rinex header associated with this data set. */ - private final RinexObservationHeader header; - /** Satellite System. */ private final SatelliteSystem satelliteSystem; @@ -49,17 +47,15 @@ public class CombinedObservationDataSet implements TimeStamped { /** * Simple constructor. - * @param header Rinex header associated with this data set * @param satelliteSystem Satellite system * @param prnNumber PRN number * @param tObs Observation date * @param rcvrClkOffset Receiver clock offset (optional, 0 by default) * @param observationData List of combined observation data */ - public CombinedObservationDataSet(final RinexObservationHeader header, final SatelliteSystem satelliteSystem, + public CombinedObservationDataSet(final SatelliteSystem satelliteSystem, final int prnNumber, final AbsoluteDate tObs, final double rcvrClkOffset, final List observationData) { - this.header = header; this.satelliteSystem = satelliteSystem; this.prnNumber = prnNumber; this.tObs = tObs; @@ -67,14 +63,6 @@ public CombinedObservationDataSet(final RinexObservationHeader header, final Sat this.rcvrClkOffset = rcvrClkOffset; } - /** Get the Rinex header associated with this data set. - * @return Rinex header associated with this data set - * @since 9.3 - */ - public RinexObservationHeader getHeader() { - return header; - } - /** Get Satellite System. * @return satellite system of observed satellite */ diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectorResults.java b/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectorResults.java index c84370e168..43da343414 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectorResults.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectorResults.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectors.java b/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectors.java index d861d1391b..73745f5cc7 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectors.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/CycleSlipDetectors.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ import java.util.List; -import org.orekit.gnss.ObservationDataSet; +import org.orekit.files.rinex.observation.ObservationDataSet; /** * Interface for phase measurement cycle-slip detection. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java b/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java new file mode 100644 index 0000000000..bf9780a78e --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java @@ -0,0 +1,66 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.gnss; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; + +/** Dipole configuration for satellite-to-ground and inter-satellites wind-up effects. + *

    + * The dipole configuration is given by two vectors. + *

    + * @see WindUp + * @see InterSatellitesWindUp + * @author Luc Maisonobe + * @since 12.0 + */ +public class Dipole { + + /** Canonical dipole, with primary vector set to {@link Vector3D#PLUS_I} + * and secondary vector set to {@link Vector3D#PLUS_J}. + */ + public static final Dipole CANONICAL_I_J = new Dipole(Vector3D.PLUS_I, Vector3D.PLUS_J); + + /** Primary dipole vector. */ + private final Vector3D primary; + + /** Secondary dipole vector. */ + private final Vector3D secondary; + + /** Simple constructor. + * @param primary primary dipole vector + * @param secondary secondary dipole vector + */ + Dipole(final Vector3D primary, final Vector3D secondary) { + this.primary = primary; + this.secondary = secondary; + } + + /** Get the primary dipole vector. + * @return primary dipole vector + */ + public Vector3D getPrimary() { + return primary; + } + + /** Get the secondary dipole vector. + * @return secondary dipole vector + */ + public Vector3D getSecondary() { + return secondary; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/GRAPHICCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/GRAPHICCombination.java index dfe73a0234..b9bf08f535 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/GRAPHICCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/GRAPHICCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCombination.java index d954198ba2..514f2d189e 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCycleSlipDetector.java b/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCycleSlipDetector.java index e7546c854b..56655a4141 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCycleSlipDetector.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/GeometryFreeCycleSlipDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,11 +23,9 @@ import org.hipparchus.fitting.PolynomialCurveFitter; import org.hipparchus.fitting.WeightedObservedPoint; import org.hipparchus.util.FastMath; -import org.orekit.gnss.CombinedObservationData; -import org.orekit.gnss.CombinedObservationDataSet; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.Frequency; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; @@ -46,7 +44,6 @@ * one has access to the begin and end of availability, and a sorted set which contains all the date at which * cycle-slip have been detected *

    - *

    * @author David Soulard * @author Bryan Cazabonne * @since 10.2 @@ -73,9 +70,9 @@ public GeometryFreeCycleSlipDetector(final double dt, final double threshold, fi protected void manageData(final ObservationDataSet observation) { // Extract observation data - final int prn = observation.getPrnNumber(); + final int prn = observation.getSatellite().getPRN(); final AbsoluteDate date = observation.getDate(); - final SatelliteSystem system = observation.getSatelliteSystem(); + final SatelliteSystem system = observation.getSatellite().getSystem(); // Geometry-free combination of measurements final GeometryFreeCombination geometryFree = MeasurementCombinationFactory.getGeometryFreeCombination(system); @@ -93,7 +90,7 @@ protected void manageData(final ObservationDataSet observation) { // Loop on Geometry-free phase measurements for (CombinedObservationData cod : phasesGF) { - final String nameSat = setName(prn, observation.getSatelliteSystem()); + final String nameSat = setName(prn, observation.getSatellite().getSystem()); // Check for cycle-slip detection final Frequency frequency = cod.getUsedObservationData().get(0).getObservationType().getFrequency(system); final boolean slip = cycleSlipDetection(nameSat, date, cod.getValue(), frequency); diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerBootstrapping.java b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerBootstrapping.java index 35078b98be..604831575c 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerBootstrapping.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerBootstrapping.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareComparator.java b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareComparator.java index ac3f0fbcf1..f47f55828b 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareComparator.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareComparator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolution.java b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolution.java index 168d9f6be3..9a08a9398b 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolution.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolution.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolver.java b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolver.java index f7dff139db..7090d0fbc5 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolver.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/IntegerLeastSquareSolver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java index f0022016f4..181ee3ca5e 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,12 +23,14 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -60,7 +62,7 @@ public class InterSatellitesPhase extends AbstractMeasurement theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + // Coordinates of both satellites + final SpacecraftState local = states[0]; + final TimeStampedPVCoordinates pvaL = local.getPVCoordinates(); + final SpacecraftState remote = states[1]; + final TimeStampedPVCoordinates pvaR = remote.getPVCoordinates(); + + // Compute propagation times + // Downlink delay + final double dtl = getSatellites().get(0).getClockOffsetDriver().getValue(AbsoluteDate.ARBITRARY_EPOCH); + final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtl); + + final TimeStampedPVCoordinates s1Downlink = pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); + final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate); + + // Transit state + final double delta = getDate().durationFrom(remote.getDate()); + final double deltaMTauD = delta - tauD; + + // prepare the evaluation + final EstimatedMeasurementBase estimatedPhase = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + local.shiftedBy(deltaMTauD), + remote.shiftedBy(deltaMTauD) + }, new TimeStampedPVCoordinates[] { + remote.shiftedBy(delta - tauD).getPVCoordinates(), + local.shiftedBy(delta).getPVCoordinates() + }); + + // Clock offsets + final double dtr = getSatellites().get(1).getClockOffsetDriver().getValue(AbsoluteDate.ARBITRARY_EPOCH); + + // Phase value + final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; + final double ambiguity = ambiguityDriver.getValue(AbsoluteDate.ARBITRARY_EPOCH); + final double phase = (tauD + dtl - dtr) * cOverLambda + ambiguity; + + estimatedPhase.setEstimatedValue(phase); + + // Return the estimated measurement + return estimatedPhase; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, @@ -121,7 +173,10 @@ protected EstimatedMeasurement theoreticalEvaluation(final final Map indices = new HashMap<>(); for (ParameterDriver phaseMeasurementDriver : getParametersDrivers()) { if (phaseMeasurementDriver.isSelected()) { - indices.put(phaseMeasurementDriver.getName(), nbParams++); + for (Span span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + indices.put(span.getData(), nbParams++); + } } } @@ -133,7 +188,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final // Compute propagation times // Downlink delay - final Gradient dtl = getSatellites().get(0).getClockOffsetDriver().getValue(nbParams, indices); + final Gradient dtl = getSatellites().get(0).getClockOffsetDriver().getValue(nbParams, indices, AbsoluteDate.ARBITRARY_EPOCH); final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtl.negate()); final TimeStampedFieldPVCoordinates s1Downlink = @@ -156,11 +211,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final }); // Clock offsets - final Gradient dtr = getSatellites().get(1).getClockOffsetDriver().getValue(nbParams, indices); + final Gradient dtr = getSatellites().get(1).getClockOffsetDriver().getValue(nbParams, indices, AbsoluteDate.ARBITRARY_EPOCH); // Phase value final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final Gradient ambiguity = ambiguityDriver.getValue(nbParams, indices); + final Gradient ambiguity = ambiguityDriver.getValue(nbParams, indices, AbsoluteDate.ARBITRARY_EPOCH); final Gradient phase = tauD.add(dtl).subtract(dtr).multiply(cOverLambda).add(ambiguity); estimatedPhase.setEstimatedValue(phase.getValue()); @@ -172,9 +227,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final // Set partial derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimatedPhase.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final Integer index = indices.get(span.getData()); + if (index != null) { + estimatedPhase.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUp.java b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUp.java new file mode 100644 index 0000000000..3cf20fc368 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUp.java @@ -0,0 +1,60 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.gnss; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; + +/** Modifier for wind-up effect in GNSS {@link InterSatellitesPhase inter-satellites phase measurements}. + * @see InterSatellitesWindUpFactory + * @author Luc Maisonobe + * @since 12.0 + */ +public class InterSatellitesWindUp extends AbstractWindUp { + + /** Simple constructor. + *

    + * The constructor is package protected to enforce use of {@link WindUpFactory} + * and preserve phase continuity for successive measurements involving the same + * satellite/receiver pair. + *

    + * @param emitter emitter dipole + * @param receiver receiver dipole + */ + InterSatellitesWindUp(final Dipole emitter, final Dipole receiver) { + super(emitter, receiver); + } + + /** {@inheritDoc} */ + @Override + protected Rotation emitterToInert(final EstimatedMeasurementBase estimated) { + // we don't use the basic yaw steering attitude model from ESA navipedia page + // but rely on the attitude that was computed by the propagator, which takes + // into account the proper noon and midnight turns for each satellite model + return estimated.getStates()[1].toStaticTransform().getRotation().revert(); + } + + /** {@inheritDoc} */ + @Override + protected Rotation receiverToInert(final EstimatedMeasurementBase estimated) { + // we don't use the basic yaw steering attitude model from ESA navipedia page + // but rely on the attitude that was computed by the propagator, which takes + // into account the proper noon and midnight turns for each satellite model + return estimated.getStates()[0].toStaticTransform().getRotation().revert(); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpFactory.java b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpFactory.java new file mode 100644 index 0000000000..20a6d173c7 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpFactory.java @@ -0,0 +1,97 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.gnss; + +import java.util.HashMap; +import java.util.Map; + +import org.orekit.gnss.SatelliteSystem; + +/** Factory for {@link InterSatellitesWindUp wind-up} modifiers. + *

    + * The factory ensures the same instance is returned for all + * emitter/receiver pair, thus preserving phase continuity + * for successive measurements involving the same pair. + *

    + * @author Luc Maisonobe + * @since 12.0 + */ +public class InterSatellitesWindUpFactory { + + /** Modifiers cache. */ + private final Map>>> modifiers; + + /** Simple constructor. + */ + public InterSatellitesWindUpFactory() { + this.modifiers = new HashMap<>(); + } + + /** Get a modifier for an emitter/receiver pair. + * @param emitterSystem system the emitter satellite belongs to + * @param emitterPrnNumber emitter satellite PRN number + * @param emitterDipole emitter dipole + * @param receiverSystem system the receiver satellite belongs to + * @param receiverPrnNumber receiver satellite PRN number + * @param receiverDipole receiver dipole + * @return modifier for the emitter/receiver pair + */ + public InterSatellitesWindUp getWindUp(final SatelliteSystem emitterSystem, final int emitterPrnNumber, + final Dipole emitterDipole, + final SatelliteSystem receiverSystem, final int receiverPrnNumber, + final Dipole receiverDipole) { + + // select emitter satellite system + Map>> emitterSystemModifiers = + modifiers.get(emitterSystem); + if (emitterSystemModifiers == null) { + // build a new map for this satellite system + emitterSystemModifiers = new HashMap<>(); + modifiers.put(emitterSystem, emitterSystemModifiers); + } + + // select emitter satellite + Map> emitterSatelliteModifiers = + emitterSystemModifiers.get(emitterPrnNumber); + if (emitterSatelliteModifiers == null) { + // build a new map for this satellite + emitterSatelliteModifiers = new HashMap<>(); + emitterSystemModifiers.put(emitterPrnNumber, emitterSatelliteModifiers); + } + + // select receiver satellite system + Map receiverSystemModifiers = + emitterSatelliteModifiers.get(receiverSystem); + if (receiverSystemModifiers == null) { + // build a new map for this satellite system + receiverSystemModifiers = new HashMap<>(); + emitterSatelliteModifiers.put(receiverSystem, receiverSystemModifiers); + } + + // select receiver satellite + InterSatellitesWindUp receiverSatelliteModifier = receiverSystemModifiers.get(receiverPrnNumber); + if (receiverSatelliteModifier == null) { + // build a new wind-up modifier + receiverSatelliteModifier = new InterSatellitesWindUp(emitterDipole, receiverDipole); + receiverSystemModifiers.put(receiverPrnNumber, receiverSatelliteModifier); + } + + return receiverSatelliteModifier; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/IonosphereFreeCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/IonosphereFreeCombination.java index 4001649d1f..eca3ddcfa4 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/IonosphereFreeCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/IonosphereFreeCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/LambdaMethod.java b/src/main/java/org/orekit/estimation/measurements/gnss/LambdaMethod.java index b5e5196bcb..eaa8b87bdc 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/LambdaMethod.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/LambdaMethod.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -50,6 +50,17 @@ public class LambdaMethod extends AbstractLambdaMethod { /** Margin factor to apply to estimated search limit parameter. */ private static final double CHI2_MARGIN_FACTOR = 1.1; + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public LambdaMethod() { + // nothing to do + } + /** {@inheritDoc} */ @Override protected void ltdlDecomposition() { diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombination.java index f0e23c483c..ba32448e93 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,7 @@ */ package org.orekit.estimation.measurements.gnss; -import org.orekit.gnss.CombinedObservationDataSet; -import org.orekit.gnss.ObservationDataSet; +import org.orekit.files.rinex.observation.ObservationDataSet; /** * Interface for combination of measurements. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactory.java b/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactory.java index ac0b4d710e..479ce350a9 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactory.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java index f1f3733cfe..b2a55fdd1c 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,11 +20,9 @@ import java.util.List; import org.hipparchus.util.FastMath; -import org.orekit.gnss.CombinedObservationData; -import org.orekit.gnss.CombinedObservationDataSet; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.SatelliteSystem; /** @@ -120,8 +118,9 @@ public CombinedObservationDataSet combine(final ObservationDataSet observations) } } - return new CombinedObservationDataSet(observations.getHeader(), observations.getSatelliteSystem(), - observations.getPrnNumber(), observations.getDate(), + return new CombinedObservationDataSet(observations.getSatellite().getSystem(), + observations.getSatellite().getPRN(), + observations.getDate(), observations.getRcvrClkOffset(), combined); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/ModifiedLambdaMethod.java b/src/main/java/org/orekit/estimation/measurements/gnss/ModifiedLambdaMethod.java index a0eaabca4a..15700e57b4 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/ModifiedLambdaMethod.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/ModifiedLambdaMethod.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,6 +33,17 @@ */ public class ModifiedLambdaMethod extends AbstractLambdaMethod { + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public ModifiedLambdaMethod() { + // nothing to do + } + /** Compute the LᵀDL factorization with symmetric pivoting decomposition of Q * (symmetric definite positive matrix) with a minimum symmetric pivoting: Q = ZᵀLᵀDLZ. */ diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/NarrowLaneCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/NarrowLaneCombination.java index 953a6de875..b4d3f34e1a 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/NarrowLaneCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/NarrowLaneCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java index 20a66c389f..3eae848778 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,6 +24,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -31,6 +32,7 @@ import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -119,6 +121,52 @@ public ParameterDriver getAmbiguityDriver() { return ambiguityDriver; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + // Coordinates of both satellites + final SpacecraftState localState = states[0]; + final TimeStampedPVCoordinates pvaLocal = localState.getPVCoordinates(); + final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); + + // Downlink delay + final double dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(localState.getDate()); + final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtLocal); + + final TimeStampedPVCoordinates s1Downlink = + pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); + final double tauD = signalTimeOfFlight(pvaRemote, s1Downlink.getPosition(), arrivalDate); + + // Transit state + final double delta = getDate().durationFrom(pvaRemote.getDate()); + final double deltaMTauD = delta - tauD; + + // prepare the evaluation + final EstimatedMeasurementBase estimatedPhase = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + localState.shiftedBy(deltaMTauD) + }, new TimeStampedPVCoordinates[] { + pvaRemote.shiftedBy(delta - tauD), + localState.shiftedBy(delta).getPVCoordinates() + }); + + // Phase value + final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; + final double ambiguity = ambiguityDriver.getValue(localState.getDate()); + final double phase = (tauD + dtLocal - dtRemote) * cOverLambda + ambiguity; + + // Set value of the estimated measurement + estimatedPhase.setEstimatedValue(phase); + + // Return the estimated measurement + return estimatedPhase; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, @@ -134,7 +182,9 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final Map parameterIndicesPhase = new HashMap<>(); for (ParameterDriver phaseMeasurementDriver : getParametersDrivers()) { if (phaseMeasurementDriver.isSelected()) { - parameterIndicesPhase.put(phaseMeasurementDriver.getName(), nbEstimatedParamsPhase++); + for (Span span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + parameterIndicesPhase.put(span.getData(), nbEstimatedParamsPhase++); + } } } @@ -144,7 +194,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); // Downlink delay - final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParamsPhase, parameterIndicesPhase); + final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParamsPhase, parameterIndicesPhase, localState.getDate()); final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtLocal.negate()); final TimeStampedFieldPVCoordinates s1Downlink = @@ -168,7 +218,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // Phase value final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final Gradient ambiguity = ambiguityDriver.getValue(nbEstimatedParamsPhase, parameterIndicesPhase); + final Gradient ambiguity = ambiguityDriver.getValue(nbEstimatedParamsPhase, parameterIndicesPhase, localState.getDate()); final Gradient phase = tauD.add(dtLocal).subtract(dtRemote).multiply(cOverLambda).add(ambiguity); final double[] phaseDerivatives = phase.getGradient(); @@ -178,9 +228,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // Set partial derivatives with respect to parameters for (final ParameterDriver phaseMeasurementDriver : getParametersDrivers()) { - final Integer index = parameterIndicesPhase.get(phaseMeasurementDriver.getName()); - if (index != null) { - estimatedPhase.setParameterDerivatives(phaseMeasurementDriver, phaseDerivatives[index]); + for (Span span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final Integer index = parameterIndicesPhase.get(span.getData()); + if (index != null) { + estimatedPhase.setParameterDerivatives(phaseMeasurementDriver, span.getStart(), phaseDerivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java index 0aff6eb40f..b18454c964 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,6 +24,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.InterSatellitesRange; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; @@ -32,6 +33,7 @@ import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -88,6 +90,49 @@ public OneWayGNSSRange(final PVCoordinatesProvider remote, this.remote = remote; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + // Coordinates of both satellites in local satellite frame + final SpacecraftState localState = states[0]; + final TimeStampedPVCoordinates pvaLocal = localState.getPVCoordinates(); + final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); + + // Downlink delay + final double dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(localState.getDate()); + final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtLocal); + + final TimeStampedPVCoordinates s1Downlink = pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); + final double tauD = signalTimeOfFlight(pvaRemote, s1Downlink.getPosition(), arrivalDate); + + // Transit state + final double delta = getDate().durationFrom(pvaRemote.getDate()); + final double deltaMTauD = delta - tauD; + + // Estimated measurement + final EstimatedMeasurementBase estimatedRange = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + localState.shiftedBy(deltaMTauD) + }, new TimeStampedPVCoordinates[] { + pvaRemote.shiftedBy(delta - tauD), + localState.shiftedBy(delta).getPVCoordinates() + }); + + // Range value + final double range = (tauD + dtLocal - dtRemote) * Constants.SPEED_OF_LIGHT; + + // Set value of the estimated measurement + estimatedRange.setEstimatedValue(range); + + // Return the estimated measurement + return estimatedRange; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, @@ -103,7 +148,9 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final Map parameterIndices = new HashMap<>(); for (ParameterDriver measurementDriver : getParametersDrivers()) { if (measurementDriver.isSelected()) { - parameterIndices.put(measurementDriver.getName(), nbEstimatedParams++); + for (Span span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + parameterIndices.put(span.getData(), nbEstimatedParams++); + } } } @@ -113,7 +160,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); // Downlink delay - final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParams, parameterIndices); + final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParams, parameterIndices, localState.getDate()); final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtLocal.negate()); final TimeStampedFieldPVCoordinates s1Downlink = pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); @@ -144,9 +191,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // Set partial derivatives with respect to parameters for (final ParameterDriver measurementDriver : getParametersDrivers()) { - final Integer index = parameterIndices.get(measurementDriver.getName()); - if (index != null) { - estimatedRange.setParameterDerivatives(measurementDriver, rangeDerivatives[index]); + for (Span span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final Integer index = parameterIndices.get(span.getData()); + if (index != null) { + estimatedRange.setParameterDerivatives(measurementDriver, span.getStart(), rangeDerivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java b/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java index ec8cbb8396..393f251cdf 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,24 +17,20 @@ package org.orekit.estimation.measurements.gnss; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.GroundReceiverCommonParametersWithDerivatives; +import org.orekit.estimation.measurements.GroundReceiverCommonParametersWithoutDerivatives; +import org.orekit.estimation.measurements.GroundReceiverMeasurement; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; -import org.orekit.frames.FieldTransform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedPVCoordinates; /** Class modeling a phase measurement from a ground station. @@ -52,7 +48,7 @@ * @author Maxime Journot * @since 9.2 */ -public class Phase extends AbstractMeasurement { +public class Phase extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "Phase"; @@ -63,9 +59,6 @@ public class Phase extends AbstractMeasurement { /** Driver for ambiguity. */ private final ParameterDriver ambiguityDriver; - /** Ground station from which measurement is performed. */ - private final GroundStation station; - /** Wavelength of the phase observed value [m]. */ private final double wavelength; @@ -82,33 +75,14 @@ public class Phase extends AbstractMeasurement { public Phase(final GroundStation station, final AbsoluteDate date, final double phase, final double wavelength, final double sigma, final double baseWeight, final ObservableSatellite satellite) { - super(date, phase, sigma, baseWeight, Collections.singletonList(satellite)); + super(station, false, date, phase, sigma, baseWeight, satellite); ambiguityDriver = new ParameterDriver(AMBIGUITY_NAME, 0.0, 1.0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); addParameterDriver(ambiguityDriver); - addParameterDriver(satellite.getClockOffsetDriver()); - addParameterDriver(station.getClockOffsetDriver()); - addParameterDriver(station.getEastOffsetDriver()); - addParameterDriver(station.getNorthOffsetDriver()); - addParameterDriver(station.getZenithOffsetDriver()); - addParameterDriver(station.getPrimeMeridianOffsetDriver()); - addParameterDriver(station.getPrimeMeridianDriftDriver()); - addParameterDriver(station.getPolarOffsetXDriver()); - addParameterDriver(station.getPolarDriftXDriver()); - addParameterDriver(station.getPolarOffsetYDriver()); - addParameterDriver(station.getPolarDriftYDriver()); - this.station = station; this.wavelength = wavelength; } - /** Get the ground station from which measurement is performed. - * @return ground station from which measurement is performed - */ - public GroundStation getStation() { - return station; - } - /** Get the wavelength. * @return wavelength (m) */ @@ -124,6 +98,40 @@ public ParameterDriver getAmbiguityDriver() { return ambiguityDriver; } + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final GroundReceiverCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states[0]); + + // prepare the evaluation + final EstimatedMeasurementBase estimated = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getTransitState() + }, new TimeStampedPVCoordinates[] { + common.getTransitPV(), + common.getStationDownlink() + }); + + // Clock offsets + final ObservableSatellite satellite = getSatellites().get(0); + final double dts = satellite.getClockOffsetDriver().getValue(common.getState().getDate()); + final double dtg = getStation().getClockOffsetDriver().getValue(getDate()); + + // Phase value + final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; + final double ambiguity = ambiguityDriver.getValue(common.getState().getDate()); + final double phase = (common.getTauD() + dtg - dts) * cOverLambda + ambiguity; + + estimated.setEstimatedValue(phase); + + return estimated; + + } + /** {@inheritDoc} */ @Override protected EstimatedMeasurement theoreticalEvaluation(final int iteration, @@ -140,62 +148,28 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // - 0..2 - Position of the spacecraft in inertial frame // - 3..5 - Velocity of the spacecraft in inertial frame // - 6..n - station parameters (ambiguity, clock offset, station offsets, pole, prime meridian...) - int nbParams = 6; - final Map indices = new HashMap<>(); - for (ParameterDriver driver : getParametersDrivers()) { - if (driver.isSelected()) { - indices.put(driver.getName(), nbParams++); - } - } - final FieldVector3D zero = FieldVector3D.getZero(GradientField.getField(nbParams)); - - // Coordinates of the spacecraft expressed as a gradient - final TimeStampedFieldPVCoordinates pvaDS = getCoordinates(state, 0, nbParams); - - // transform between station and inertial frame, expressed as a gradient - // The components of station's position in offset frame are the 3 last derivative parameters - final FieldTransform offsetToInertialDownlink = - station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices); - final FieldAbsoluteDate downlinkDateDS = - offsetToInertialDownlink.getFieldDate(); - - // Station position in inertial frame at end of the downlink leg - final TimeStampedFieldPVCoordinates stationDownlink = - offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS, - zero, zero, zero)); - - // Compute propagation times - // (if state has already been set up to pre-compensate propagation delay, - // we will have delta == tauD and transitState will be the same as state) - - // Downlink delay - final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS); - - // Transit state & Transit state (re)computed with gradients - final Gradient delta = downlinkDateDS.durationFrom(state.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); - final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue()); - final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD); + final GroundReceiverCommonParametersWithDerivatives common = computeCommonParametersWithDerivatives(state); + final int nbParams = common.getTauD().getFreeParameters(); // prepare the evaluation final EstimatedMeasurement estimated = new EstimatedMeasurement(this, iteration, evaluation, new SpacecraftState[] { - transitState + common.getTransitState() }, new TimeStampedPVCoordinates[] { - transitStateDS.toTimeStampedPVCoordinates(), - stationDownlink.toTimeStampedPVCoordinates() + common.getTransitPV().toTimeStampedPVCoordinates(), + common.getStationDownlink().toTimeStampedPVCoordinates() }); // Clock offsets final ObservableSatellite satellite = getSatellites().get(0); - final Gradient dts = satellite.getClockOffsetDriver().getValue(nbParams, indices); - final Gradient dtg = station.getClockOffsetDriver().getValue(nbParams, indices); + final Gradient dts = satellite.getClockOffsetDriver().getValue(nbParams, common.getIndices(), state.getDate()); + final Gradient dtg = getStation().getClockOffsetDriver().getValue(nbParams, common.getIndices(), getDate()); // Phase value final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final Gradient ambiguity = ambiguityDriver.getValue(nbParams, indices); - final Gradient phase = tauD.add(dtg).subtract(dts).multiply(cOverLambda).add(ambiguity); + final Gradient ambiguity = ambiguityDriver.getValue(nbParams, common.getIndices(), state.getDate()); + final Gradient phase = common.getTauD().add(dtg).subtract(dts).multiply(cOverLambda).add(ambiguity); estimated.setEstimatedValue(phase.getValue()); @@ -206,9 +180,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // set partial derivatives with respect to parameters // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { - final Integer index = indices.get(driver.getName()); - if (index != null) { - estimated.setParameterDerivatives(driver, derivatives[index]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final Integer index = common.getIndices().get(span.getData()); + if (index != null) { + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java index 145aa9a7cf..80e8d88c11 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,15 @@ */ package org.orekit.estimation.measurements.gnss; +import java.util.Map; + import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.generation.AbstractMeasurementBuilder; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -38,6 +41,11 @@ public class PhaseBuilder extends AbstractMeasurementBuilder { /** Wavelength of the phase observed value [m]. */ private final double wavelength; + /** Satellite related to this builder. + * @since 12.0 + */ + private final ObservableSatellite satellite; + /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements * @param station ground station from which measurement is performed @@ -53,16 +61,16 @@ public PhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource, super(noiseSource, sigma, baseWeight, satellite); this.station = station; this.wavelength = wavelength; + this.satellite = satellite; } /** {@inheritDoc} */ @Override - public Phase build(final SpacecraftState[] states) { + public Phase build(final AbsoluteDate date, final Map interpolators) { - final ObservableSatellite satellite = getSatellites()[0]; final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; - final SpacecraftState[] relevant = new SpacecraftState[] { states[satellite.getPropagatorIndex()] }; + final SpacecraftState[] relevant = new SpacecraftState[] { interpolators.get(satellite).getInterpolatedState(date) }; // create a dummy measurement final Phase dummy = new Phase(station, relevant[0].getDate(), Double.NaN, wavelength, sigma, baseWeight, satellite); @@ -80,7 +88,7 @@ public Phase build(final SpacecraftState[] states) { } // estimate the perfect value of the measurement - double phase = dummy.estimate(0, 0, relevant).getEstimatedValue()[0]; + double phase = dummy.estimateWithoutDerivatives(0, 0, relevant).getEstimatedValue()[0]; // add the noise final double[] noise = getNoise(); diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCombination.java index c388af30d0..5c38b5fb0e 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java index 17aef883e8..b0082767fa 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,11 +23,10 @@ import org.hipparchus.fitting.PolynomialCurveFitter; import org.hipparchus.fitting.WeightedObservedPoint; import org.hipparchus.util.FastMath; -import org.orekit.gnss.CombinedObservationData; +import org.orekit.files.rinex.observation.ObservationData; +import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.gnss.Frequency; import org.orekit.gnss.MeasurementType; -import org.orekit.gnss.ObservationData; -import org.orekit.gnss.ObservationDataSet; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; @@ -46,7 +45,6 @@ * one has access to the begin and end of availability, and a sorted set which contains all the date at which * cycle-slip have been detected *

    - *

    * @author David Soulard * @since 10.2 */ @@ -79,8 +77,8 @@ public PhaseMinusCodeCycleSlipDetector(final double dt, final double threshold, protected void manageData(final ObservationDataSet observation) { // Extract observation data - final SatelliteSystem system = observation.getSatelliteSystem(); - final int prn = observation.getPrnNumber(); + final SatelliteSystem system = observation.getSatellite().getSystem(); + final int prn = observation.getSatellite().getPRN(); final AbsoluteDate date = observation.getDate(); // Initialize list of measurements @@ -115,7 +113,7 @@ protected void manageData(final ObservationDataSet observation) { // Phase minus Code combination final PhaseMinusCodeCombination phaseMinusCode = MeasurementCombinationFactory.getPhaseMinusCodeCombination(system); final CombinedObservationData cod = phaseMinusCode.combine(phaseInMeters, pseudoRange); - final String nameSat = setName(prn, observation.getSatelliteSystem()); + final String nameSat = setName(prn, observation.getSatellite().getSystem()); // Check for cycle-slip detection final boolean slip = cycleSlipDetection(nameSat, date, cod.getValue(), phase.getObservationType().getFrequency(system)); diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/SimpleRatioAmbiguityAcceptance.java b/src/main/java/org/orekit/estimation/measurements/gnss/SimpleRatioAmbiguityAcceptance.java index 12ef18e387..0ffad7a18e 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/SimpleRatioAmbiguityAcceptance.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/SimpleRatioAmbiguityAcceptance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/WideLaneCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/WideLaneCombination.java index 0c60e6c1b0..415795760e 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/WideLaneCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/WideLaneCombination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/WindUp.java b/src/main/java/org/orekit/estimation/measurements/gnss/WindUp.java index 969ed9648b..8c49d43f5c 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/WindUp.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/WindUp.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2023 Thales Alenia Space * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,30 +16,18 @@ */ package org.orekit.estimation.measurements.gnss; -import java.util.Collections; -import java.util.List; - import org.hipparchus.geometry.euclidean.threed.Rotation; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; -import org.orekit.estimation.measurements.EstimatedMeasurement; -import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.GroundStation; import org.orekit.frames.Frame; -import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeStampedPVCoordinates; /** Modifier for wind-up effect in GNSS {@link Phase phase measurements}. - * @see Carrier Phase Wind-up Effect * @see WindUpFactory * @author Luc Maisonobe * @since 10.1 */ -public class WindUp implements EstimationModifier { - - /** Cached angular value of wind-up. */ - private double angularWindUp; +public class WindUp extends AbstractWindUp { /** Simple constructor. *

    @@ -47,60 +35,28 @@ public class WindUp implements EstimationModifier { * and preserve phase continuity for successive measurements involving the same * satellite/receiver pair. *

    + * @param emitter emitter dipole */ - WindUp() { - angularWindUp = 0.0; - } - - /** {@inheritDoc} - *

    - * Wind-up effect has no parameters, the returned list is always empty. - *

    - */ - @Override - public List getParametersDrivers() { - return Collections.emptyList(); + WindUp(final Dipole emitter) { + super(emitter, Dipole.CANONICAL_I_J); } /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { - - // signal line of sight - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final Vector3D los = participants[1].getPosition().subtract(participants[0].getPosition()).normalize(); - - // get ground antenna dipole - final Frame inertial = estimated.getStates()[0].getFrame(); - final GroundStation station = estimated.getObservedMeasurement().getStation(); - final Rotation offsetToInert = station.getOffsetToInertial(inertial, estimated.getDate()).getRotation(); - final Vector3D iGround = offsetToInert.applyTo(Vector3D.PLUS_I); - final Vector3D jGround = offsetToInert.applyTo(Vector3D.PLUS_J); - final Vector3D dGround = new Vector3D(1.0, iGround, -Vector3D.dotProduct(iGround, los), los). - add(Vector3D.crossProduct(los, jGround)); - - // get satellite dipole + protected Rotation emitterToInert(final EstimatedMeasurementBase estimated) { // we don't use the basic yaw steering attitude model from ESA navipedia page // but rely on the attitude that was computed by the propagator, which takes // into account the proper noon and midnight turns for each satellite model - final Rotation satToInert = estimated.getStates()[0].toTransform().getRotation().revert(); - final Vector3D iSat = satToInert.applyTo(Vector3D.PLUS_I); - final Vector3D jSat = satToInert.applyTo(Vector3D.PLUS_J); - final Vector3D dSat = new Vector3D(1.0, iSat, -Vector3D.dotProduct(iSat, los), los). - subtract(Vector3D.crossProduct(los, jSat)); - - // raw correction - final double correction = FastMath.copySign(Vector3D.angle(dSat, dGround), - Vector3D.dotProduct(los, Vector3D.crossProduct(dSat, dGround))); - - // ensure continuity accross measurements - // we assume the various measurements are close enough in time - // (less the one satellite half-turn) so the angles remain close - angularWindUp = MathUtils.normalizeAngle(correction, angularWindUp); - - // update estimate - estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + angularWindUp / MathUtils.TWO_PI); + return estimated.getStates()[0].toStaticTransform().getRotation().revert(); + } + /** {@inheritDoc} */ + @Override + protected Rotation receiverToInert(final EstimatedMeasurementBase estimated) { + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + final Frame inertial = estimated.getStates()[0].getFrame(); + final GroundStation station = estimated.getObservedMeasurement().getStation(); + return station.getOffsetToInertial(inertial, participants[1].getDate(), false).getRotation(); } } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java b/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java index ac26c4f8c7..1057f16da7 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,11 +44,12 @@ public WindUpFactory() { /** Get a modifier for a satellite/receiver pair. * @param system system the satellite belongs to * @param prnNumber PRN number + * @param emitterDipole emitter dipole * @param receiverName name of the receiver * @return modifier for the satellite/receiver pair */ - public WindUp getWindUp(final SatelliteSystem system, final int prnNumber, final String receiverName) { - + public WindUp getWindUp(final SatelliteSystem system, final int prnNumber, + final Dipole emitterDipole, final String receiverName) { // select satellite system Map> systemModifiers = modifiers.get(system); if (systemModifiers == null) { @@ -69,7 +70,7 @@ public WindUp getWindUp(final SatelliteSystem system, final int prnNumber, final WindUp receiverModifier = satelliteModifiers.get(receiverName); if (receiverModifier == null) { // build a new wind-up modifier - receiverModifier = new WindUp(); + receiverModifier = new WindUp(emitterDipole); satelliteModifiers.put(receiverName, receiverModifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/package-info.java b/src/main/java/org/orekit/estimation/measurements/gnss/package-info.java index d54ec7cdf2..fa68d5a98b 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/package-info.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java new file mode 100644 index 0000000000..f5bc0ea098 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java @@ -0,0 +1,359 @@ +/* Copyright 2023 Mark Rutten + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Mark Rutten licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.modifiers; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.estimation.measurements.AngularRaDec; +import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + + +/** + * Class modifying theoretical angular measurement with (the inverse of) stellar aberration. + *

    + * This class implements equation 3.252-3 from Seidelmann, "Explanatory Supplement to the Astronmical Almanac", 1992. + * + * @author Mark Rutten + */ +public class AberrationModifier implements EstimationModifier { + + /** Empty constructor. + *

    + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

    + * @since 12.0 + */ + public AberrationModifier() { + // nothing to do + } + + /** + * Natural to proper correction for aberration of light. + * + * @param naturalRaDec the "natural" direction (in barycentric coordinates) + * @param station the observer ground station + * @param date the date of the measurement + * @param frame the frame of the measurement + * @return the "proper" direction (station-relative coordinates) + */ + @DefaultDataContext + public static double[] naturalToProper(final double[] naturalRaDec, final GroundStation station, + final AbsoluteDate date, final Frame frame) { + + // Check measurement frame is inertial + if (!frame.isPseudoInertial()) { + throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frame.getName()); + } + + // Velocity of station relative to barycentre (units of c) + final PVCoordinates baryPV = CelestialBodyFactory.getSolarSystemBarycenter() + .getPVCoordinates(date, frame); + final PVCoordinates stationPV = station.getBaseFrame().getPVCoordinates(date, frame); + final Vector3D stationBaryVel = stationPV.getVelocity() + .subtract(baryPV.getVelocity()) + .scalarMultiply(1.0 / Constants.SPEED_OF_LIGHT); + + // Delegate to private method + return lorentzVelocitySum(naturalRaDec, stationBaryVel); + } + + /** + * Proper to natural correction for aberration of light. + * + * @param properRaDec the "proper" direction (station-relative coordinates) + * @param station the observer ground station + * @param date the date of the measurement + * @param frame the frame of the measurement + * @return the "natural" direction (in barycentric coordinates) + */ + @DefaultDataContext + public static double[] properToNatural(final double[] properRaDec, final GroundStation station, + final AbsoluteDate date, final Frame frame) { + + // Check measurement frame is inertial + if (!frame.isPseudoInertial()) { + throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frame.getName()); + } + + // Velocity of barycentre relative to station (units of c) + final PVCoordinates baryPV = CelestialBodyFactory.getSolarSystemBarycenter() + .getPVCoordinates(date, frame); + final PVCoordinates stationPV = station.getBaseFrame().getPVCoordinates(date, frame); + final Vector3D baryStationVel = baryPV.getVelocity() + .subtract(stationPV.getVelocity()) + .scalarMultiply(1.0 / Constants.SPEED_OF_LIGHT); + + // Delegate to private method + return lorentzVelocitySum(properRaDec, baryStationVel); + } + + /** + * Relativistic sum of velocities. + * This is based on equation 3.252-3 from Seidelmann, "Explanatory Supplement to the Astronmical Almanac", 1992. + * + * @param raDec the direction to transform + * @param velocity the velocity (units of c) + * @return the transformed direction + */ + private static double[] lorentzVelocitySum(final double[] raDec, final Vector3D velocity) { + + // Measurement as unit vector + final Vector3D direction = new Vector3D(raDec[0], raDec[1]); + + // Coefficients for calculations + final double inverseBeta = FastMath.sqrt(1.0 - velocity.getNormSq()); + final double velocityScale = 1.0 + direction.dotProduct(velocity) / (1.0 + inverseBeta); + + // From Seidelmann, equation 3.252-3 (unnormalised) + final Vector3D transformDirection = (direction.scalarMultiply(inverseBeta)) + .add(velocity.scalarMultiply(velocityScale)); + return new double[] {transformDirection.getAlpha(), transformDirection.getDelta()}; + } + + /** + * Natural to proper correction for aberration of light. + * + * @param naturalRaDec the "natural" direction (in barycentric coordinates) + * @param stationToInertial the transform from station to inertial coordinates + * @param frame the frame of the measurement + * @return the "proper" direction (station-relative coordinates) + */ + @DefaultDataContext + public static Gradient[] fieldNaturalToProper(final Gradient[] naturalRaDec, + final FieldTransform stationToInertial, + final Frame frame) { + + // Check measurement frame is inertial + if (!frame.isPseudoInertial()) { + throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frame.getName()); + } + + // Set up field + final Field field = naturalRaDec[0].getField(); + final FieldVector3D zero = FieldVector3D.getZero(field); + final FieldAbsoluteDate date = stationToInertial.getFieldDate(); + + // Barycentre in inertial coordinates + final PVCoordinates baryPV = CelestialBodyFactory.getSolarSystemBarycenter() + .getPVCoordinates(date.toAbsoluteDate(), frame); + + // Station in inertial coordinates + final TimeStampedFieldPVCoordinates stationPV = + stationToInertial.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(date, zero, zero, zero)); + + // Velocity of station relative to barycentre (units of c) + final FieldVector3D stationBaryVel = stationPV.getVelocity() + .subtract(baryPV.getVelocity()) + .scalarMultiply(1.0 / Constants.SPEED_OF_LIGHT); + + return fieldLorentzVelocitySum(naturalRaDec, stationBaryVel); + } + + + /** + * Proper to natural correction for aberration of light. + * + * @param properRaDec the "proper" direction (station-relative coordinates) + * @param stationToInertial the transform from station to inertial coordinates + * @param frame the frame of the measurement + * @return the "natural" direction (in barycentric coordinates) + */ + @DefaultDataContext + public static Gradient[] fieldProperToNatural(final Gradient[] properRaDec, + final FieldTransform stationToInertial, + final Frame frame) { + + // Check measurement frame is inertial + if (!frame.isPseudoInertial()) { + throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frame.getName()); + } + + // Set up field + final Field field = properRaDec[0].getField(); + final FieldVector3D zero = FieldVector3D.getZero(field); + final FieldAbsoluteDate date = stationToInertial.getFieldDate(); + + // Barycentre in inertial coordinates + final PVCoordinates baryPV = CelestialBodyFactory.getSolarSystemBarycenter() + .getPVCoordinates(date.toAbsoluteDate(), frame); + + // Station in inertial coordinates + final TimeStampedFieldPVCoordinates stationPV = + stationToInertial.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(date, zero, zero, zero)); + + // Velocity of barycentre relative to station (units of c) + final FieldVector3D stationBaryVel = stationPV.getVelocity() + .negate() + .add(baryPV.getVelocity()) + .scalarMultiply(1.0 / Constants.SPEED_OF_LIGHT); + + return fieldLorentzVelocitySum(properRaDec, stationBaryVel); + } + + /** + * Relativistic sum of velocities. + * This is based on equation 3.252-3 from Seidelmann, "Explanatory Supplement to the Astronmical Almanac", 1992. + * + * @param raDec the direction to transform + * @param velocity the velocity (units of c) + * @return the transformed direction + */ + private static Gradient[] fieldLorentzVelocitySum(final Gradient[] raDec, + final FieldVector3D velocity) { + + // Measurement as unit vector + final FieldVector3D direction = new FieldVector3D<>(raDec[0], raDec[1]); + + // Coefficients for calculations + final Gradient inverseBeta = (velocity.getNormSq().negate().add(1.0)).sqrt(); + final Gradient velocityScale = (direction.dotProduct(velocity)).divide(inverseBeta.add(1.0)).add(1.0); + + // From Seidelmann, equation 3.252-3 (unnormalised) + final FieldVector3D transformDirection = (direction.scalarMultiply(inverseBeta)) + .add(velocity.scalarMultiply(velocityScale)); + return new Gradient[] {transformDirection.getAlpha(), transformDirection.getDelta()}; + } + + + /** + * {@inheritDoc} + */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + + @Override + @DefaultDataContext + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + // Observation date + final AbsoluteDate date = estimated.getDate(); + + // Observation station + final GroundStation station = estimated.getObservedMeasurement().getStation(); + + // Observation frame + final Frame frame = estimated.getObservedMeasurement().getReferenceFrame(); + + // Convert measurement to natural direction + final double[] estimatedRaDec = estimated.getEstimatedValue(); + final double[] naturalRaDec = properToNatural(estimatedRaDec, station, date, frame); + + // Normalise RA + final double[] observed = estimated.getObservedValue(); + final double baseRightAscension = naturalRaDec[0]; + final double twoPiWrap = MathUtils.normalizeAngle(baseRightAscension, observed[0]) - baseRightAscension; + final double rightAscension = baseRightAscension + twoPiWrap; + + // New estimated values + estimated.setEstimatedValue(rightAscension, naturalRaDec[1]); + + } + + @Override + @DefaultDataContext + public void modify(final EstimatedMeasurement estimated) { + + // Observation date + final AbsoluteDate date = estimated.getDate(); + + // Observation frame + final Frame frame = estimated.getObservedMeasurement().getReferenceFrame(); + + // Extract RA/Dec parameters (no state derivatives) + int nbParams = 6; + final Map indices = new HashMap<>(); + for (ParameterDriver driver : estimated.getObservedMeasurement().getParametersDrivers()) { + if (driver.isSelected()) { + for (TimeSpanMap.Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (!indices.containsKey(span.getData())) { + indices.put(span.getData(), nbParams++); + } + } + } + } + final Field field = GradientField.getField(nbParams); + + // Observation location + final FieldTransform stationToInertial = + estimated.getObservedMeasurement().getStation().getOffsetToInertial(frame, date, nbParams, indices); + + // Convert measurement to natural direction + final double[] estimatedRaDec = estimated.getEstimatedValue(); + final Gradient[] estimatedRaDecDS = new Gradient[] { + field.getZero().add(estimatedRaDec[0]), + field.getZero().add(estimatedRaDec[1]) + }; + final Gradient[] naturalRaDec = fieldProperToNatural(estimatedRaDecDS, stationToInertial, frame); + + // Normalise RA + final double[] observed = estimated.getObservedValue(); + final Gradient baseRightAscension = naturalRaDec[0]; + final double twoPiWrap = MathUtils.normalizeAngle(baseRightAscension.getReal(), + observed[0]) - baseRightAscension.getReal(); + final Gradient rightAscension = baseRightAscension.add(twoPiWrap); + + // New estimated values + estimated.setEstimatedValue(rightAscension.getValue(), naturalRaDec[1].getValue()); + + // Derivatives (only parameter, no state) + final double[] raDerivatives = rightAscension.getGradient(); + final double[] decDerivatives = naturalRaDec[1].getGradient(); + + for (final ParameterDriver driver : estimated.getObservedMeasurement().getParametersDrivers()) { + for (TimeSpanMap.Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = indices.get(span.getData()); + if (index != null) { + final double[] parameterDerivative = estimated.getParameterDerivatives(driver); + parameterDerivative[0] += raDerivatives[index]; + parameterDerivative[1] += decDerivatives[index]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative[0], parameterDerivative[1]); + } + } + } + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java index 0b191ec6d3..09e3a580a0 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,9 @@ import org.hipparchus.util.FastMath; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; /** * Base class for phase ambiguity modifier. @@ -61,15 +63,31 @@ protected List getDrivers() { /** Modify measurement. * @param estimated measurement to modify */ - protected void doModify(final EstimatedMeasurement estimated) { + protected void doModifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Apply the ambiguity to the measurement value - final double[] value = estimated.getEstimatedValue(); - value[0] += ambiguity.getValue(); - if (ambiguity.isSelected()) { + for (Span span = ambiguity.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final double[] value = estimated.getEstimatedValue(); + value[0] += ambiguity.getValue(span.getStart()); + estimated.setEstimatedValue(value); + } + } + + /** Modify measurement. + * @param estimated measurement to modify + */ + protected void doModify(final EstimatedMeasurement estimated) { + + // apply the ambiguity to the measurement derivatives + for (Span span = ambiguity.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (ambiguity.isSelected()) { // add the partial derivatives - estimated.setParameterDerivatives(ambiguity, 1.0); + estimated.setParameterDerivatives(ambiguity, span.getStart(), 1.0); + } } - estimated.setEstimatedValue(value); + + // apply the ambiguity to the measurement value + doModifyWithoutDerivatives(estimated); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockModifier.java index 2750b91fb8..840bfcdfd7 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,7 @@ package org.orekit.estimation.measurements.modifiers; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.utils.Constants; import org.orekit.utils.TimeStampedPVCoordinates; @@ -46,7 +46,7 @@ public AbstractRelativisticClockModifier() { * @param estimated estimated measurement * @return the relativistic clock correction in seconds */ - protected double relativisticCorrection(final EstimatedMeasurement estimated) { + protected double relativisticCorrection(final EstimatedMeasurementBase estimated) { // Participants final TimeStampedPVCoordinates[] pv = estimated.getParticipants(); // Relativistic clock correction taking into account two-ways measurements diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticJ2ClockModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticJ2ClockModifier.java index 655bd3cf2c..afc277d5c7 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticJ2ClockModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticJ2ClockModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,14 +17,13 @@ package org.orekit.estimation.measurements.modifiers; -import org.orekit.utils.Constants; -import org.orekit.utils.TimeStampedPVCoordinates; - import org.hipparchus.util.FastMath; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.frames.Frame; import org.orekit.orbits.KeplerianOrbit; import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.Constants; +import org.orekit.utils.TimeStampedPVCoordinates; /** * Class modifying theoretical measurements with relativistic J2 clock correction. @@ -73,7 +72,7 @@ public AbstractRelativisticJ2ClockModifier(final double gm, * @param estimated EstimatedMeasurements on which to calculate the correction * @return dt_relJ2clk Time delay due to the relativistic J2 clock effect in seconds */ - protected double relativisticJ2Correction(final EstimatedMeasurement estimated) { + protected double relativisticJ2Correction(final EstimatedMeasurementBase estimated) { // Extracting the state of the receiver to determine the frame and mu /** diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java index 56daef4e9c..a93573190d 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.utils.Constants; import org.orekit.utils.TimeStampedPVCoordinates; @@ -45,7 +45,7 @@ public AbstractShapiroBaseModifier(final double gm) { /** Modify measurement. * @param estimated measurement to modify */ - protected void doModify(final EstimatedMeasurement estimated) { + protected void doModify(final EstimatedMeasurementBase estimated) { // compute correction, for one way or two way measurements final TimeStampedPVCoordinates[] pv = estimated.getParticipants(); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java index 1291479d86..1b2dab9d9d 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.MathUtils; import org.orekit.estimation.measurements.AngularAzEl; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.frames.Frame; @@ -31,6 +31,7 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** Class modifying theoretical angular measurement with ionospheric delay. * The effect of ionospheric correction on the angular measurement is computed @@ -76,7 +77,7 @@ private double angularErrorIonosphericModel(final GroundStation station, // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // delay in meters - final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters()); + final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); return delay; } @@ -87,7 +88,7 @@ public List getParametersDrivers() { } @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { final AngularAzEl measure = estimated.getObservedMeasurement(); final GroundStation station = measure.getStation(); final SpacecraftState state = estimated.getStates()[0]; @@ -101,17 +102,17 @@ public void modify(final EstimatedMeasurement estimated) { // Update estimated value taking into account the ionospheric delay. final AbsoluteDate date = transitState.getDate(); - final Vector3D position = transitState.getPVCoordinates().getPosition(); + final Vector3D position = transitState.getPosition(); final Frame inertial = transitState.getFrame(); // Elevation and azimuth in radians - final double elevation = station.getBaseFrame().getElevation(position, inertial, date); - final double baseAzimuth = station.getBaseFrame().getAzimuth(position, inertial, date); - final double twoPiWrap = MathUtils.normalizeAngle(baseAzimuth, measure.getObservedValue()[0]) - baseAzimuth; - final double azimuth = baseAzimuth + twoPiWrap; + final TrackingCoordinates tc = station.getBaseFrame().getTrackingCoordinates(position, inertial, date); + final double twoPiWrap = MathUtils.normalizeAngle(tc.getAzimuth(), measure.getObservedValue()[0]) - tc.getAzimuth(); + final double azimuth = tc.getAzimuth() + twoPiWrap; // Update estimated value taking into account the ionospheric delay. // Azimuth - elevation values - estimated.setEstimatedValue(azimuth, elevation); + estimated.setEstimatedValue(azimuth, tc.getElevation()); } + } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java index 31d1e1d3dd..ae3ae50611 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.estimation.measurements.AngularAzEl; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.models.AtmosphericRefractionModel; @@ -61,12 +61,12 @@ public AngularRadioRefractionModifier(final AtmosphericRefractionModel model) { private double angularErrorRadioRefractionModel(final GroundStation station, final SpacecraftState state) { - final Vector3D position = state.getPVCoordinates().getPosition(); + final Vector3D position = state.getPosition(); // elevation in radians - final double elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final double elevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // angle correction (rad) return atmosModel.getRefraction(elevation); @@ -79,7 +79,7 @@ public List getParametersDrivers() { } @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { final AngularAzEl measure = estimated.getObservedMeasurement(); final GroundStation station = measure.getStation(); final SpacecraftState state = estimated.getStates()[0]; @@ -94,4 +94,5 @@ public void modify(final EstimatedMeasurement estimated) { newValue[1] = newValue[1] + correction; estimated.setEstimatedValue(newValue[0], newValue[1]); } + } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java index 1367953711..28dda835ad 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.MathUtils; import org.orekit.estimation.measurements.AngularAzEl; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.frames.Frame; @@ -30,6 +30,7 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** Class modifying theoretical angular measurement with tropospheric delay. * The effect of tropospheric correction on the angular is computed @@ -64,17 +65,17 @@ public AngularTroposphericDelayModifier(final DiscreteTroposphericModel model) { private double angularErrorTroposphericModel(final GroundStation station, final SpacecraftState state) { // - final Vector3D position = state.getPVCoordinates().getPosition(); + final Vector3D position = state.getPosition(); // elevation - final double elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final double elevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation > 0.0) { // delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state.getDate()); + final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); // one-way measurement. return delay; @@ -90,7 +91,7 @@ public List getParametersDrivers() { } @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { final AngularAzEl measure = estimated.getObservedMeasurement(); final GroundStation station = measure.getStation(); final SpacecraftState state = estimated.getStates()[0]; @@ -104,17 +105,17 @@ public void modify(final EstimatedMeasurement estimated) { // Update measurement value taking into account the ionospheric delay. final AbsoluteDate date = transitState.getDate(); - final Vector3D position = transitState.getPVCoordinates().getPosition(); + final Vector3D position = transitState.getPosition(); final Frame inertial = transitState.getFrame(); // Elevation and azimuth in radians - final double elevation = station.getBaseFrame().getElevation(position, inertial, date); - final double baseAzimuth = station.getBaseFrame().getAzimuth(position, inertial, date); - final double twoPiWrap = MathUtils.normalizeAngle(baseAzimuth, measure.getObservedValue()[0]) - baseAzimuth; - final double azimuth = baseAzimuth + twoPiWrap; + final TrackingCoordinates tc = station.getBaseFrame().getTrackingCoordinates(position, inertial, date); + final double twoPiWrap = MathUtils.normalizeAngle(tc.getAzimuth(), measure.getObservedValue()[0]) - tc.getAzimuth(); + final double azimuth = tc.getAzimuth() + twoPiWrap; // Update estimated value taking into account the tropospheric delay. // Azimuth - elevation values - estimated.setEstimatedValue(azimuth, elevation); + estimated.setEstimatedValue(azimuth, tc.getElevation()); } + } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeIonosphericDelayModifier.java index bde1111acf..4e33b3faca 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateIonosphericDelayModifier.java index be56c4ac8d..f5e7600d94 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -75,11 +75,11 @@ protected double rangeRateErrorIonosphericModel(final GroundStation station, fin // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // delay in meters - final double delay1 = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters()); + final double delay1 = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); // propagate spacecraft state forward by dt final SpacecraftState state2 = state.shiftedBy(dt); // ionospheric delay dt after in meters - final double delay2 = ionoModel.pathDelay(state2, baseFrame, frequency, ionoModel.getParameters()); + final double delay2 = ionoModel.pathDelay(state2, baseFrame, frequency, ionoModel.getParameters(state.getDate())); // delay in meters return (delay2 - delay1) / dt; } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java index 8f9221d225..a2bd297af3 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -72,31 +72,31 @@ public double rangeRateErrorTroposphericModel(final GroundStation station, final double dt = 10; // s // spacecraft position and elevation as seen from the ground station - final Vector3D position = state.getPVCoordinates().getPosition(); + final Vector3D position = state.getPosition(); // elevation - final double elevation1 = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final double elevation1 = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation1 > 0) { // tropospheric delay in meters - final double d1 = tropoModel.pathDelay(elevation1, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state.getDate()); + final double d1 = tropoModel.pathDelay(elevation1, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); // propagate spacecraft state forward by dt final SpacecraftState state2 = state.shiftedBy(dt); // spacecraft position and elevation as seen from the ground station - final Vector3D position2 = state2.getPVCoordinates().getPosition(); + final Vector3D position2 = state2.getPosition(); // elevation - final double elevation2 = station.getBaseFrame().getElevation(position2, - state2.getFrame(), - state2.getDate()); + final double elevation2 = + station.getBaseFrame().getTrackingCoordinates(position2, state2.getFrame(), state2.getDate()). + getElevation(); // tropospheric delay dt after - final double d2 = tropoModel.pathDelay(elevation2, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state2.getDate()); + final double d2 = tropoModel.pathDelay(elevation2, station.getBaseFrame().getPoint(), tropoModel.getParameters(state2.getDate()), state2.getDate()); return (d2 - d1) / dt; } @@ -125,10 +125,10 @@ public > T rangeRateErrorTroposphericModel(fin final double dt = 10; // s // spacecraft position and elevation as seen from the ground station - final FieldVector3D position = state.getPVCoordinates().getPosition(); - final T elevation1 = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final FieldVector3D position = state.getPosition(); + final T elevation1 = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation1.getReal() > 0) { @@ -139,12 +139,12 @@ public > T rangeRateErrorTroposphericModel(fin final FieldSpacecraftState state2 = state.shiftedBy(dt); // spacecraft position and elevation as seen from the ground station - final FieldVector3D position2 = state2.getPVCoordinates().getPosition(); + final FieldVector3D position2 = state2.getPosition(); // elevation - final T elevation2 = station.getBaseFrame().getElevation(position2, - state2.getFrame(), - state2.getDate()); + final T elevation2 = + station.getBaseFrame().getTrackingCoordinates(position2, state2.getFrame(), state2.getDate()). + getElevation(); // tropospheric delay dt after diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java index a221bc9f1e..17b30bbf72 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -67,10 +67,10 @@ public double rangeErrorTroposphericModel(final GroundStation station, final SpacecraftState state) { // spacecraft position and elevation as seen from the ground station - final Vector3D position = state.getPVCoordinates().getPosition(); - final double elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final Vector3D position = state.getPosition(); + final double elevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation > 0) { @@ -100,10 +100,10 @@ public > T rangeErrorTroposphericModel(final G final T zero = field.getZero(); // spacecraft position and elevation as seen from the ground station - final FieldVector3D position = state.getPVCoordinates().getPosition(); - final T elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final FieldVector3D position = state.getPosition(); + final T elevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation .getReal() > 0) { diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java b/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java index a75bf83a4d..293a79854c 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,11 @@ import java.util.List; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; /** Class modeling a measurement bias. * @param the type of the measurement @@ -76,20 +78,38 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // apply the bias to the measurement value final double[] value = estimated.getEstimatedValue(); + int nb = 0; for (int i = 0; i < drivers.size(); ++i) { final ParameterDriver driver = drivers.get(i); - value[i] += driver.getValue(); - if (driver.isSelected()) { - // add the partial derivatives - estimated.setParameterDerivatives(driver, derivatives[i]); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + value[nb++] += driver.getValue(span.getStart()); } } estimated.setEstimatedValue(value); + } + + /** {@inheritDoc} */ + @Override + public void modify(final EstimatedMeasurement estimated) { + + // apply the bias to the measurement value + int nb = 0; + for (int i = 0; i < drivers.size(); ++i) { + final ParameterDriver driver = drivers.get(i); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (driver.isSelected()) { + // add the partial derivatives + estimated.setParameterDerivatives(driver, span.getStart(), derivatives[nb++]); + } + } + } + + modifyWithoutDerivatives(estimated); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java index 5f4396e585..bcffee1fb0 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -27,7 +28,8 @@ import org.orekit.propagation.integration.AbstractGradientConverter; import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TimeSpanMap.Span; /** Utility class for bistatic measurements. * @author Pascal Parraud @@ -40,6 +42,27 @@ private BistaticModifierUtil() { // not used } + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param emitter emitter station + * @param receiver receiver station + * @param modelEffect model effect + */ + public static > void modify(final EstimatedMeasurementBase estimated, + final GroundStation emitter, final GroundStation receiver, + final ParametricModelEffect modelEffect) { + + // update estimated value taking into account the model effect. + // The model effect delay is directly added to the measurement. + final SpacecraftState state = estimated.getStates()[0]; + final double[] newValue = estimated.getEstimatedValue().clone(); + newValue[0] += modelEffect.evaluate(emitter, state); + newValue[0] += modelEffect.evaluate(receiver, state); + estimated.setEstimatedValue(newValue); + + } + /** Apply a modifier to an estimated measurement. * @param type of the measurement * @param estimated estimated measurement to modify @@ -51,14 +74,13 @@ private BistaticModifierUtil() { * @param modelEffectGradient model effect gradient */ public static > void modify(final EstimatedMeasurement estimated, - final ParametersDriversProvider parametricModel, + final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation emitter, final GroundStation receiver, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { final SpacecraftState state = estimated.getStates()[0]; - final double[] oldValue = estimated.getEstimatedValue(); // update estimated derivatives with Jacobian of the measure wrt state final FieldSpacecraftState gState = converter.getState(parametricModel); @@ -81,12 +103,15 @@ public static > void modify(final EstimatedMeas int index = 0; for (final ParameterDriver driver : parametricModel.getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt model parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += derivativesUp[index + converter.getFreeStateParameters()]; - parameterDerivative += derivativesDown[index + converter.getFreeStateParameters()]; - estimated.setParameterDerivatives(driver, parameterDerivative); - index++; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt model parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += derivativesUp[index + converter.getFreeStateParameters()]; + parameterDerivative += derivativesDown[index + converter.getFreeStateParameters()]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + index++; + } } } @@ -95,11 +120,14 @@ public static > void modify(final EstimatedMeas emitter.getNorthOffsetDriver(), emitter.getZenithOffsetDriver())) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt station parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += Differentiation.differentiate(d -> modelEffect.evaluate(emitter, state), - 3, 10.0 * driver.getScale()).value(driver); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt station parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += Differentiation.differentiate((d, t) -> modelEffect.evaluate(emitter, state), + 3, 10.0 * driver.getScale()).value(driver, state.getDate()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } @@ -108,20 +136,19 @@ public static > void modify(final EstimatedMeas receiver.getNorthOffsetDriver(), receiver.getZenithOffsetDriver())) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt station parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += Differentiation.differentiate(d -> modelEffect.evaluate(receiver, state), - 3, 10.0 * driver.getScale()).value(driver); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt station parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += Differentiation.differentiate((d, t) -> modelEffect.evaluate(receiver, state), + 3, 10.0 * driver.getScale()).value(driver, state.getDate()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } - // update estimated value taking into account the model effect. - // The model effect delay is directly added to the measurement. - final double[] newValue = oldValue.clone(); - newValue[0] += delayUp.getValue(); - newValue[0] += delayDown.getValue(); - estimated.setEstimatedValue(newValue); + // modify the value + modify(estimated, emitter, receiver, modelEffect); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java index 802c2da04e..621e10ed83 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Mark Rutten +/* Copyright 2002-2023 Mark Rutten * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,10 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.BistaticRange; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.models.earth.ionosphere.IonosphericModel; @@ -54,16 +55,29 @@ public BistaticRangeIonosphericDelayModifier(final IonosphericModel model, super(model, freq); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final BistaticRange measurement = estimated.getObservedMeasurement(); + final GroundStation emitter = measurement.getEmitterStation(); + final GroundStation receiver = measurement.getReceiverStation(); + + BistaticModifierUtil.modify(estimated, emitter, receiver, this::rangeErrorIonosphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { + final BistaticRange measurement = estimated.getObservedMeasurement(); final GroundStation emitter = measurement.getEmitterStation(); final GroundStation receiver = measurement.getReceiverStation(); final SpacecraftState state = estimated.getStates()[0]; BistaticModifierUtil.modify(estimated, getIonoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeErrorIonosphericModel, this::rangeErrorIonosphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java index 8d4824e0a4..ad03320052 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,10 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.BistaticRangeRate; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.models.earth.ionosphere.IonosphericModel; @@ -45,6 +46,18 @@ public BistaticRangeRateIonosphericDelayModifier(final IonosphericModel model, f super(model, freq); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final BistaticRangeRate measurement = estimated.getObservedMeasurement(); + final GroundStation emitter = measurement.getEmitterStation(); + final GroundStation receiver = measurement.getReceiverStation(); + + BistaticModifierUtil.modify(estimated, emitter, receiver, this::rangeRateErrorIonosphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -55,7 +68,7 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; BistaticModifierUtil.modify(estimated, getIonoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeRateErrorIonosphericModel, this::rangeRateErrorIonosphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java index 8841939a25..8c499de798 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,10 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.BistaticRangeRate; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; @@ -44,6 +45,18 @@ public BistaticRangeRateTroposphericDelayModifier(final DiscreteTroposphericMode super(model); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final BistaticRangeRate measurement = estimated.getObservedMeasurement(); + final GroundStation emitter = measurement.getEmitterStation(); + final GroundStation receiver = measurement.getReceiverStation(); + + BistaticModifierUtil.modify(estimated, emitter, receiver, this::rangeRateErrorTroposphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -54,10 +67,11 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; BistaticModifierUtil.modify(estimated, getTropoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeRateErrorTroposphericModel, this::rangeRateErrorTroposphericModel); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java index 8f4477ae64..868fee4237 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Mark Rutten +/* Copyright 2002-2023 Mark Rutten * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,10 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.BistaticRange; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; @@ -46,6 +47,17 @@ public BistaticRangeTroposphericDelayModifier(final DiscreteTroposphericModel mo super(model); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + final BistaticRange measurement = estimated.getObservedMeasurement(); + final GroundStation emitter = measurement.getEmitterStation(); + final GroundStation receiver = measurement.getReceiverStation(); + + BistaticModifierUtil.modify(estimated, emitter, receiver, this::rangeErrorTroposphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -55,10 +67,11 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; BistaticModifierUtil.modify(estimated, getTropoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeErrorTroposphericModel, this::rangeErrorTroposphericModel); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/DynamicOutlierFilter.java b/src/main/java/org/orekit/estimation/measurements/modifiers/DynamicOutlierFilter.java index 5feb8a6b5a..3420a0e9bc 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/DynamicOutlierFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/DynamicOutlierFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java index 448d16a959..b40cc444e2 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.util.List; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.utils.ParameterDriver; @@ -50,6 +51,12 @@ public List getParametersDrivers() { return getDrivers(); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + doModifyWithoutDerivatives(estimated); + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/IonosphericGradientConverter.java b/src/main/java/org/orekit/estimation/measurements/modifiers/IonosphericGradientConverter.java deleted file mode 100644 index 37e9bd6666..0000000000 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/IonosphericGradientConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.measurements.modifiers; - -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.propagation.SpacecraftState; - -/** - * Converter for states and parameters arrays. - * @author Bryan Cazabonne - * @since 10.2 - * @deprecated as of 11.2, replaced by {@link ModifierGradientConverter} - */ -@Deprecated -public class IonosphericGradientConverter extends ModifierGradientConverter { - - /** - * Simple constructor. - * @param state regular state - * @param freeStateParameters number of free parameters, either 3 (position) or 6 (position-velocity) - * @param provider provider to use if attitude needs to be recomputed - */ - public IonosphericGradientConverter(final SpacecraftState state, final int freeStateParameters, - final AttitudeProvider provider) { - super(state, freeStateParameters, provider); - } - -} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ModifierGradientConverter.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ModifierGradientConverter.java index f7ea92c050..f34a886270 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ModifierGradientConverter.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ModifierGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,19 +16,9 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.FieldAttitude; -import org.orekit.orbits.FieldCartesianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AbstractGradientConverter; -import org.orekit.utils.TimeStampedFieldPVCoordinates; /** * Converter for states and parameters arrays. @@ -47,53 +37,8 @@ public ModifierGradientConverter(final SpacecraftState state, final int freeStat super(freeStateParameters); - // Derivative field - final Field field = GradientField.getField(freeStateParameters); - - // position always has derivatives - final Vector3D pos = state.getPVCoordinates().getPosition(); - final FieldVector3D posG = new FieldVector3D<>(Gradient.variable(freeStateParameters, 0, pos.getX()), - Gradient.variable(freeStateParameters, 1, pos.getY()), - Gradient.variable(freeStateParameters, 2, pos.getZ())); - - // velocity may have derivatives or not - final Vector3D vel = state.getPVCoordinates().getVelocity(); - final FieldVector3D velG; - if (freeStateParameters > 3) { - velG = new FieldVector3D<>(Gradient.variable(freeStateParameters, 3, vel.getX()), - Gradient.variable(freeStateParameters, 4, vel.getY()), - Gradient.variable(freeStateParameters, 5, vel.getZ())); - } else { - velG = new FieldVector3D<>(Gradient.constant(freeStateParameters, vel.getX()), - Gradient.constant(freeStateParameters, vel.getY()), - Gradient.constant(freeStateParameters, vel.getZ())); - } - - // acceleration never has derivatives - final Vector3D acc = state.getPVCoordinates().getAcceleration(); - final FieldVector3D accG = new FieldVector3D<>(Gradient.constant(freeStateParameters, acc.getX()), - Gradient.constant(freeStateParameters, acc.getY()), - Gradient.constant(freeStateParameters, acc.getZ())); - - // mass never has derivatives - final Gradient dsM = Gradient.constant(freeStateParameters, state.getMass()); - - final FieldOrbit gOrbit = - new FieldCartesianOrbit<>(new TimeStampedFieldPVCoordinates<>(state.getDate(), posG, velG, accG), - state.getFrame(), - field.getZero().add(state.getMu())); - - final FieldAttitude gAttitude; - if (freeStateParameters > 3) { - // compute attitude partial derivatives with respect to position/velocity - gAttitude = provider.getAttitude(gOrbit, gOrbit.getDate(), gOrbit.getFrame()); - } else { - // force model does not depend on attitude, don't bother recomputing it - gAttitude = new FieldAttitude<>(field, state.getAttitude()); - } - // initialize the list with the state having 0 force model parameters - initStates(new FieldSpacecraftState<>(gOrbit, gAttitude, dsM)); + initStates(buildBasicGradientSpacecraftState(state, freeStateParameters, provider)); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java index e85427a20d..10c8df8af7 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,10 +20,10 @@ import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; -import org.orekit.frames.Transform; +import org.orekit.frames.StaticTransform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -61,7 +61,7 @@ public List getParametersDrivers() { } @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // The participants are satellite 2 at emission, satellite 1 at reception final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); @@ -71,10 +71,10 @@ public void modify(final EstimatedMeasurement estimated) { // transforms from spacecraft to inertial frame at emission/reception dates final SpacecraftState localState = estimated.getStates()[0]; final SpacecraftState receptionState = localState.shiftedBy(receptionDate.durationFrom(localState.getDate())); - final Transform receptionSpacecraftToInert = receptionState.toTransform().getInverse(); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); final SpacecraftState remoteState = estimated.getStates()[1]; final SpacecraftState emissionState = remoteState.shiftedBy(emissionDate.durationFrom(remoteState.getDate())); - final Transform emissionSpacecraftToInert = emissionState.toTransform().getInverse(); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); // Compute the geometrical value of the inter-satellites range directly from participants positions. final Vector3D pSpacecraftReception = receptionSpacecraftToInert.transformPosition(Vector3D.ZERO); @@ -97,5 +97,4 @@ public void modify(final EstimatedMeasurement estimated) { } - } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java index 69986786cc..fd6b7c44a7 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,10 +20,10 @@ import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.InterSatellitesRange; -import org.orekit.frames.Transform; +import org.orekit.frames.StaticTransform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -62,7 +62,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { if (estimated.getParticipants().length < 3) { modifyOneWay(estimated); } else { @@ -73,7 +73,7 @@ public void modify(final EstimatedMeasurement estimated) { /** Apply a modifier to an estimated measurement in the one-way case. * @param estimated estimated measurement to modify */ - private void modifyOneWay(final EstimatedMeasurement estimated) { + private void modifyOneWay(final EstimatedMeasurementBase estimated) { // the participants are satellite 2 at emission, satellite 1 at reception final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); @@ -83,10 +83,10 @@ private void modifyOneWay(final EstimatedMeasurement estim // transforms from spacecraft to inertial frame at emission/reception dates final SpacecraftState refState1 = estimated.getStates()[0]; final SpacecraftState receptionState = refState1.shiftedBy(receptionDate.durationFrom(refState1.getDate())); - final Transform receptionSpacecraftToInert = receptionState.toTransform().getInverse(); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); final SpacecraftState refState2 = estimated.getStates()[1]; final SpacecraftState emissionState = refState2.shiftedBy(emissionDate.durationFrom(refState2.getDate())); - final Transform emissionSpacecraftToInert = emissionState.toTransform().getInverse(); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); // compute the geometrical value of the inter-satellites range directly from participants positions. // Note that this may be different from the value returned by estimated.getEstimatedValue(), @@ -115,7 +115,7 @@ private void modifyOneWay(final EstimatedMeasurement estim /** Apply a modifier to an estimated measurement in the two-way case. * @param estimated estimated measurement to modify */ - private void modifyTwoWay(final EstimatedMeasurement estimated) { + private void modifyTwoWay(final EstimatedMeasurementBase estimated) { // the participants are satellite 1 at emission, satellite 2 at transit, satellite 1 at reception final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); @@ -126,12 +126,12 @@ private void modifyTwoWay(final EstimatedMeasurement estim // transforms from spacecraft to inertial frame at emission/reception dates final SpacecraftState refState1 = estimated.getStates()[0]; final SpacecraftState receptionState = refState1.shiftedBy(receptionDate.durationFrom(refState1.getDate())); - final Transform receptionSpacecraftToInert = receptionState.toTransform().getInverse(); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); final SpacecraftState refState2 = estimated.getStates()[1]; final SpacecraftState transitState = refState2.shiftedBy(transitDate.durationFrom(refState2.getDate())); - final Transform transitSpacecraftToInert = transitState.toTransform().getInverse(); + final StaticTransform transitSpacecraftToInert = transitState.toStaticTransform().getInverse(); final SpacecraftState emissionState = refState1.shiftedBy(emissionDate.durationFrom(refState1.getDate())); - final Transform emissionSpacecraftToInert = emissionState.toTransform().getInverse(); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); // compute the geometrical value of the inter-satellites range directly from participants positions. // Note that this may be different from the value returned by estimated.getEstimatedValue(), diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java index 52b8bd0374..0a62425f22 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,10 +21,10 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; -import org.orekit.frames.Transform; +import org.orekit.frames.StaticTransform; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.propagation.SpacecraftState; @@ -70,7 +70,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // The participants are remote satellite at emission, local satellite at reception final TimeStampedPVCoordinates[] phaseParticipants = estimated.getParticipants(); @@ -80,7 +80,7 @@ public void modify(final EstimatedMeasurement estimated) { // Transforms from spacecraft to inertial frame at reception date final SpacecraftState refStateLocal = estimated.getStates()[0]; final SpacecraftState receptionState = refStateLocal.shiftedBy(phaseReceptionDate.durationFrom(refStateLocal.getDate())); - final Transform receptionSpacecraftToInert = receptionState.toTransform().getInverse(); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); // Orbit of the remote satellite final Orbit orbitRemote = new CartesianOrbit(phaseParticipants[0], refStateLocal.getFrame(), receptionState.getMu()); @@ -91,7 +91,7 @@ public void modify(final EstimatedMeasurement estimated) { orbitRemote.getDate(), orbitRemote.getFrame())); final SpacecraftState emissionState = refStateRemote.shiftedBy(phaseEmissionDate.durationFrom(refStateRemote.getDate())); - final Transform emissionSpacecraftToInert = emissionState.toTransform().getInverse(); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); // Compute the geometrical value of the one-way GNSS phase directly from participants positions. // Note that this may be different from the value returned by estimated.getEstimatedValue(), diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java index b0b298b505..8a1617803a 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,10 +21,10 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSRange; -import org.orekit.frames.Transform; +import org.orekit.frames.StaticTransform; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.propagation.SpacecraftState; @@ -70,7 +70,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // The participants are remote satellite at emission, local satellite at reception final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); @@ -80,7 +80,7 @@ public void modify(final EstimatedMeasurement estimated) { // Transforms from spacecraft to inertial frame at reception date final SpacecraftState refStateLocal = estimated.getStates()[0]; final SpacecraftState receptionState = refStateLocal.shiftedBy(receptionDate.durationFrom(refStateLocal.getDate())); - final Transform receptionSpacecraftToInert = receptionState.toTransform().getInverse(); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); // Orbit of the remote satellite final Orbit orbitRemote = new CartesianOrbit(participants[0], refStateLocal.getFrame(), receptionState.getMu()); @@ -91,7 +91,7 @@ public void modify(final EstimatedMeasurement estimated) { orbitRemote.getDate(), orbitRemote.getFrame())); final SpacecraftState emissionState = refStateRemote.shiftedBy(emissionDate.durationFrom(refStateRemote.getDate())); - final Transform emissionSpacecraftToInert = emissionState.toTransform().getInverse(); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); // Compute the geometrical value of the one-way GNSS range directly from participants positions. // Note that this may be different from the value returned by estimated.getEstimatedValue(), diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaPhaseModifier.java deleted file mode 100644 index 827e94a820..0000000000 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaPhaseModifier.java +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.measurements.modifiers; - -import java.util.Collections; -import java.util.List; - -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.estimation.measurements.EstimatedMeasurement; -import org.orekit.estimation.measurements.EstimationModifier; -import org.orekit.estimation.measurements.gnss.Phase; -import org.orekit.frames.Transform; -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedPVCoordinates; - -/** On-board antenna offset effect on phase measurements. - * @author David Soulard - * @since 10.2 - */ -public class OnBoardAntennaPhaseModifier implements EstimationModifier { - - /** Position of the Antenna Phase Center in satellite frame. */ - private final Vector3D antennaPhaseCenter; - - /** Simple constructor. - * @param antennaPhaseCenter position of the Antenna Phase Center in satellite frame - */ - public OnBoardAntennaPhaseModifier(final Vector3D antennaPhaseCenter) { - this.antennaPhaseCenter = antennaPhaseCenter; - } - - /** {@inheritDoc} */ - @Override - public List getParametersDrivers() { - return Collections.emptyList(); - } - - /** {@inheritDoc} */ - @Override - public void modify(final EstimatedMeasurement estimated) { - // the participants are spacecraft at emission, ground station at reception - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final AbsoluteDate emissionDate = participants[0].getDate(); - final Vector3D pReception = participants[1].getPosition(); - - // transform from spacecraft to inertial frame at emission date - final SpacecraftState refState = estimated.getStates()[0]; - final SpacecraftState emissionState = refState.shiftedBy(emissionDate.durationFrom(refState.getDate())); - final Transform spacecraftToInert = emissionState.toTransform().getInverse(); - - // compute the geometrical value of the range directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraft = spacecraftToInert.transformPosition(Vector3D.ZERO); - final double rangeUsingSpacecraftCenter = Vector3D.distance(pSpacecraft, pReception); - - // compute the geometrical value of the range replacing - // the spacecraft position with antenna phase center position - final Vector3D pAPC = spacecraftToInert.transformPosition(antennaPhaseCenter); - final double rangeUsingAntennaPhaseCenter = Vector3D.distance(pAPC, pReception); - - // get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // modify the value - value[0] += (rangeUsingAntennaPhaseCenter - rangeUsingSpacecraftCenter) / estimated.getObservedMeasurement().getWavelength(); - estimated.setEstimatedValue(value); - } - -} - diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaRangeModifier.java deleted file mode 100644 index 934d590a99..0000000000 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaRangeModifier.java +++ /dev/null @@ -1,138 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.measurements.modifiers; - -import java.util.Collections; -import java.util.List; - -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.estimation.measurements.EstimatedMeasurement; -import org.orekit.estimation.measurements.EstimationModifier; -import org.orekit.estimation.measurements.Range; -import org.orekit.frames.Transform; -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedPVCoordinates; - -/** On-board antenna offset effect on range measurements. - * @author Luc Maisonobe - * @since 9.0 - */ -public class OnBoardAntennaRangeModifier implements EstimationModifier { - - /** Position of the Antenna Phase Center in satellite frame. */ - private final Vector3D antennaPhaseCenter; - - /** Simple constructor. - * @param antennaPhaseCenter position of the Antenna Phase Center in satellite frame - */ - public OnBoardAntennaRangeModifier(final Vector3D antennaPhaseCenter) { - this.antennaPhaseCenter = antennaPhaseCenter; - } - - /** {@inheritDoc} */ - @Override - public List getParametersDrivers() { - return Collections.emptyList(); - } - - /** {@inheritDoc} */ - @Override - public void modify(final EstimatedMeasurement estimated) { - if (estimated.getObservedMeasurement().isTwoWay()) { - modifyTwoWay(estimated); - } else { - modifyOneWay(estimated); - } - } - - /** Apply a modifier to a one-way range measurement. - * @param estimated estimated measurement to modify - */ - private void modifyOneWay(final EstimatedMeasurement estimated) { - - // the participants are spacecraft at emission, ground station at reception - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final AbsoluteDate emissionDate = participants[0].getDate(); - final Vector3D pReception = participants[1].getPosition(); - - // transform from spacecraft to inertial frame at emission date - final SpacecraftState refState = estimated.getStates()[0]; - final SpacecraftState emissionState = refState.shiftedBy(emissionDate.durationFrom(refState.getDate())); - final Transform spacecraftToInert = emissionState.toTransform().getInverse(); - - // compute the geometrical value of the range directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraft = spacecraftToInert.transformPosition(Vector3D.ZERO); - final double rangeUsingSpacecraftCenter = Vector3D.distance(pSpacecraft, pReception); - - // compute the geometrical value of the range replacing - // the spacecraft position with antenna phase center position - final Vector3D pAPC = spacecraftToInert.transformPosition(antennaPhaseCenter); - final double rangeUsingAntennaPhaseCenter = Vector3D.distance(pAPC, pReception); - - // get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // modify the value - value[0] += rangeUsingAntennaPhaseCenter - rangeUsingSpacecraftCenter; - estimated.setEstimatedValue(value); - - } - - /** Apply a modifier to a two-way range measurement. - * @param estimated estimated measurement to modify - */ - private void modifyTwoWay(final EstimatedMeasurement estimated) { - - // the participants are ground station at emission, spacecraft, ground station at reception - // or spacecraft, ground station at reception if oneWay - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final Vector3D pEmission = participants[0].getPosition(); - final AbsoluteDate transitDate = participants[1].getDate(); - final Vector3D pReception = participants[2].getPosition(); - - // transform from spacecraft to inertial frame at transit date - final SpacecraftState refState = estimated.getStates()[0]; - final SpacecraftState transitState = refState.shiftedBy(transitDate.durationFrom(refState.getDate())); - final Transform spacecraftToInert = transitState.toTransform().getInverse(); - - // compute the geometrical value of the range directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraft = spacecraftToInert.transformPosition(Vector3D.ZERO); - final double rangeUsingSpacecraftCenter = - 0.5 * (Vector3D.distance(pEmission, pSpacecraft) + Vector3D.distance(pSpacecraft, pReception)); - - // compute the geometrical value of the range replacing - // the spacecraft position with antenna phase center position - final Vector3D pAPC = spacecraftToInert.transformPosition(antennaPhaseCenter); - final double rangeUsingAntennaPhaseCenter = - 0.5 * (Vector3D.distance(pEmission, pAPC) + Vector3D.distance(pAPC, pReception)); - - // get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // modify the value - value[0] += rangeUsingAntennaPhaseCenter - rangeUsingSpacecraftCenter; - estimated.setEstimatedValue(value); - - } - -} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java index e017e94111..c75bf5522a 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,10 +20,10 @@ import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.TurnAroundRange; -import org.orekit.frames.Transform; +import org.orekit.frames.StaticTransform; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -53,7 +53,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // the participants are primary station at emission, spacecraft during leg 1, // secondary station at rebound, spacecraft during leg 2, primary station at reception @@ -67,9 +67,9 @@ public void modify(final EstimatedMeasurement estimated) { // transforms from spacecraft to inertial frame at transit dates final SpacecraftState refState = estimated.getStates()[0]; final SpacecraftState transitStateLeg1 = refState.shiftedBy(transitDateLeg1.durationFrom(refState.getDate())); - final Transform spacecraftToInertLeg1 = transitStateLeg1.toTransform().getInverse(); + final StaticTransform spacecraftToInertLeg1 = transitStateLeg1.toStaticTransform().getInverse(); final SpacecraftState transitStateLeg2 = refState.shiftedBy(transitDateLeg2.durationFrom(refState.getDate())); - final Transform spacecraftToInertLeg2 = transitStateLeg2.toTransform().getInverse(); + final StaticTransform spacecraftToInertLeg2 = transitStateLeg2.toStaticTransform().getInverse(); // compute the geometrical value of the turn-around range directly from participants positions. // Note that this may be different from the value returned by estimated.getEstimatedValue(), diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifier.java index b3513a9411..4b88b109a9 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.util.List; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.utils.ParameterDriver; @@ -50,6 +51,12 @@ public List getParametersDrivers() { return getDrivers(); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + doModifyWithoutDerivatives(estimated); + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OutlierFilter.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OutlierFilter.java index d133ba8f15..1d5f4e3f73 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OutlierFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OutlierFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,7 @@ import org.hipparchus.util.FastMath; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.utils.ParameterDriver; @@ -68,7 +69,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { if (estimated.getIteration() > warmup) { diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java index 2a53247da4..ed06d614b7 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java index e0759a10fe..cd865a5851 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java index 1f1fd0d658..6e6ded125e 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.util.List; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.Phase; import org.orekit.utils.ParameterDriver; @@ -51,6 +52,11 @@ public List getParametersDrivers() { return getDrivers(); } + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + doModifyWithoutDerivatives(estimated); + } + @Override public void modify(final EstimatedMeasurement estimated) { doModify(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersGroundReceiverBaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersGroundReceiverBaseModifier.java new file mode 100644 index 0000000000..9402bd9b1a --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersGroundReceiverBaseModifier.java @@ -0,0 +1,115 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.modifiers; + +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.GroundReceiverMeasurement; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.Frame; +import org.orekit.frames.Transform; +import org.orekit.gnss.antenna.FrequencyPattern; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Ground and on-board antennas offsets effect on range measurements. + * @param the type of the measurement + * @author Luc Maisonobe + * @since 12.0 + */ +public class PhaseCentersGroundReceiverBaseModifier> { + + /** Uplink offset model. */ + private final PhaseCentersOffsetComputer uplink; + + /** Downlink offset model. */ + private final PhaseCentersOffsetComputer downlink; + + /** Simple constructor. + * @param stationPattern station pattern + * @param satellitePattern satellite pattern + */ + public PhaseCentersGroundReceiverBaseModifier(final FrequencyPattern stationPattern, + final FrequencyPattern satellitePattern) { + this.uplink = new PhaseCentersOffsetComputer(stationPattern, satellitePattern); + this.downlink = new PhaseCentersOffsetComputer(satellitePattern, stationPattern); + } + + /** Compute distance modification for one way measurement. + * @param estimated estimated measurement to modify + * @return distance modification to add to raw measurement + */ + public double oneWayDistanceModification(final EstimatedMeasurementBase estimated) { + + // get all participants + // note that clock offset is compensated in participants, + // so the dates included there are more accurate than the measurement date + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + + // station at reception date + final Frame inertial = estimated.getStates()[0].getFrame(); + final GroundStation station = estimated.getObservedMeasurement().getStation(); + final AbsoluteDate receptionDate = participants[1].getDate(); + final Transform stationToInert = station.getOffsetToInertial(inertial, receptionDate, false); + + // spacecraft at emission date + final AbsoluteDate emissionDate = participants[0].getDate(); + final SpacecraftState refState = estimated.getStates()[0]; + final SpacecraftState emissionState = refState.shiftedBy(emissionDate.durationFrom(refState.getDate())); + final Transform spacecraftToInert = emissionState.toTransform().getInverse(); + + // compute offset due to phase centers + return downlink.offset(spacecraftToInert, stationToInert); + + } + + /** Apply a modifier to a two-way range measurement. + * @param estimated estimated measurement to modify + * @return distance modification to add to raw measurement + */ + public double twoWayDistanceModification(final EstimatedMeasurementBase estimated) { + + // get all participants + // note that clock offset is compensated in participants, + // so the dates included there are more accurate than the measurement date + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + + // station at reception date + final Frame inertial = estimated.getStates()[0].getFrame(); + final GroundStation station = estimated.getObservedMeasurement().getStation(); + final AbsoluteDate receptionDate = participants[2].getDate(); + final Transform stationToInertReception = station.getOffsetToInertial(inertial, receptionDate, false); + + // transform from spacecraft to inertial frame at transit date + final AbsoluteDate transitDate = participants[1].getDate(); + final SpacecraftState refState = estimated.getStates()[0]; + final SpacecraftState transitState = refState.shiftedBy(transitDate.durationFrom(refState.getDate())); + final Transform spacecraftToInert = transitState.toTransform().getInverse(); + + // station at emission date + final AbsoluteDate emissionDate = participants[0].getDate(); + final Transform stationToInertEmission = station.getOffsetToInertial(inertial, emissionDate, true); + + // compute offsets due to phase centers + final double uplinkOffset = uplink.offset(stationToInertEmission, spacecraftToInert); + final double downlinkOffset = downlink.offset(spacecraftToInert, stationToInertReception); + + return 0.5 * (uplinkOffset + downlinkOffset); + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersOffsetComputer.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersOffsetComputer.java new file mode 100644 index 0000000000..58bdb94f10 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersOffsetComputer.java @@ -0,0 +1,75 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.modifiers; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.frames.StaticTransform; +import org.orekit.gnss.antenna.FrequencyPattern; + +/** Compute phase centers offset on an emitter-receiver link. + * @author Luc Maisonobe + * @since 12.0 + */ +public class PhaseCentersOffsetComputer { + + /** Emitter pattern. */ + private final FrequencyPattern emitterPattern; + + /** Receiver pattern. */ + private final FrequencyPattern receiverPattern; + + /** Simple constructor. + * @param emitterPattern emitter pattern + * @param receiverPattern receiver pattern + */ + public PhaseCentersOffsetComputer(final FrequencyPattern emitterPattern, + final FrequencyPattern receiverPattern) { + this.emitterPattern = emitterPattern; + this.receiverPattern = receiverPattern; + } + + /** Compute distance offset to be added to the distance between antennas reference points. + * @param emitterToInert transform from emitter to inertial frame at emission date + * @param receiverToInert transform from receiver to inertial frame at reception date + * @return offset to be added to distance between origins, in order to get distance between phase centers + */ + public double offset(final StaticTransform emitterToInert, final StaticTransform receiverToInert) { + + // compute the relative positions of frames origins + final Vector3D emitterOrigin = emitterToInert.transformPosition(Vector3D.ZERO); + final Vector3D receiverOrigin = receiverToInert.transformPosition(Vector3D.ZERO); + final Vector3D deltaOrigins = receiverOrigin.subtract(emitterOrigin); + + // compute the relative positions of mean phase centers + final Vector3D emitterMean = emitterToInert.transformPosition(emitterPattern.getEccentricities()); + final Vector3D receiverMean = receiverToInert.transformPosition(receiverPattern.getEccentricities()); + final Vector3D deltaMeans = receiverMean.subtract(emitterMean); + + // compute the phase variation at emission + final Vector3D emitterLos = emitterToInert.getRotation().applyInverseTo(deltaMeans); + final double emitterPCV = emitterPattern.getPhaseCenterVariation(emitterLos); + + // compute the phase variation at reception + final Vector3D receiverLos = receiverToInert.getRotation().applyInverseTo(deltaMeans.negate()); + final double receiverPCV = receiverPattern.getPhaseCenterVariation(receiverLos); + + // compute the total offset resulting from both antennas phase centers + return deltaMeans.getNorm() - deltaOrigins.getNorm() + emitterPCV + receiverPCV; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java new file mode 100644 index 0000000000..bc59947482 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java @@ -0,0 +1,60 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.modifiers; + +import java.util.Collections; +import java.util.List; + +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.gnss.Phase; +import org.orekit.gnss.antenna.FrequencyPattern; +import org.orekit.utils.ParameterDriver; + +/** Ground and on-board antennas offsets effect on phase measurements. + * @author Luc Maisonobe + * @since 12.0 + */ +public class PhaseCentersPhaseModifier implements EstimationModifier { + + /** Raw modifier. */ + private final PhaseCentersGroundReceiverBaseModifier modifier; + + /** Simple constructor. + * @param stationPattern station pattern + * @param satellitePattern satellite pattern + */ + public PhaseCentersPhaseModifier(final FrequencyPattern stationPattern, + final FrequencyPattern satellitePattern) { + this.modifier = new PhaseCentersGroundReceiverBaseModifier<>(stationPattern, satellitePattern); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + + modifier.oneWayDistanceModification(estimated) / + estimated.getObservedMeasurement().getWavelength()); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java new file mode 100644 index 0000000000..0e55015b90 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java @@ -0,0 +1,78 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.estimation.measurements.modifiers; + +import java.util.Collections; +import java.util.List; + +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.Range; +import org.orekit.gnss.antenna.FrequencyPattern; +import org.orekit.utils.ParameterDriver; + +/** Ground and on-board antennas offsets effect on range measurements. + * @author Luc Maisonobe + * @since 12.0 + */ +public class PhaseCentersRangeModifier implements EstimationModifier { + + /** Raw modifier. */ + private final PhaseCentersGroundReceiverBaseModifier modifier; + + /** Simple constructor. + * @param stationPattern station pattern + * @param satellitePattern satellite pattern + */ + public PhaseCentersRangeModifier(final FrequencyPattern stationPattern, + final FrequencyPattern satellitePattern) { + this.modifier = new PhaseCentersGroundReceiverBaseModifier<>(stationPattern, satellitePattern); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + if (estimated.getObservedMeasurement().isTwoWay()) { + modifyTwoWay(estimated); + } else { + modifyOneWay(estimated); + } + } + + /** Apply a modifier to a one-way range measurement. + * @param estimated estimated measurement to modify + */ + private void modifyOneWay(final EstimatedMeasurementBase estimated) { + estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + + modifier.oneWayDistanceModification(estimated)); + } + + /** Apply a modifier to a two-way range measurement. + * @param estimated estimated measurement to modify + */ + private void modifyTwoWay(final EstimatedMeasurementBase estimated) { + estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + + modifier.twoWayDistanceModification(estimated)); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java index db5e2e78cb..42c0321429 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,8 +21,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.gnss.Phase; @@ -34,6 +35,7 @@ import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; +import org.orekit.utils.TimeSpanMap.Span; /** * Class modifying theoretical phase measurement with ionospheric delay. @@ -74,7 +76,7 @@ private double phaseErrorIonosphericModel(final GroundStation station, final TopocentricFrame baseFrame = station.getBaseFrame(); final double wavelength = Constants.SPEED_OF_LIGHT / frequency; // delay in meters - final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters()); + final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); return delay / wavelength; } @@ -82,12 +84,12 @@ private double phaseErrorIonosphericModel(final GroundStation station, * @param type of the element * @param station station * @param state spacecraft state - * @param parameters ionospheric model parameters + * @param parameters ionospheric model parameters at state date * @return the measurement error due to ionosphere */ private > T phaseErrorIonosphericModel(final GroundStation station, - final FieldSpacecraftState state, - final T[] parameters) { + final FieldSpacecraftState state, + final T[] parameters) { // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); @@ -124,10 +126,10 @@ private double[][] phaseErrorJacobianState(final double[] derivatives, final int private double phaseErrorParameterDerivative(final GroundStation station, final ParameterDriver driver, final SpacecraftState state) { - final ParameterFunction phaseError = parameterDriver -> phaseErrorIonosphericModel(station, state); + final ParameterFunction phaseError = (parameterDriver, date) -> phaseErrorIonosphericModel(station, state); final ParameterFunction phaseErrorDerivative = Differentiation.differentiate(phaseError, 3, 10.0 * driver.getScale()); - return phaseErrorDerivative.value(driver); + return phaseErrorDerivative.value(driver, state.getDate()); } @@ -158,19 +160,33 @@ public List getParametersDrivers() { } @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + final Phase measurement = estimated.getObservedMeasurement(); final GroundStation station = measurement.getStation(); final SpacecraftState state = estimated.getStates()[0]; - // Old phase value - final double[] oldValue = estimated.getEstimatedValue(); + // Update estimated value taking into account the ionospheric delay. + // The ionospheric delay is directly subtracted to the phase. + final double[] newValue = estimated.getEstimatedValue(); + final double delay = phaseErrorIonosphericModel(station, state); + newValue[0] = newValue[0] - delay; + estimated.setEstimatedValue(newValue); + + } + + @Override + public void modify(final EstimatedMeasurement estimated) { + + final Phase measurement = estimated.getObservedMeasurement(); + final GroundStation station = measurement.getStation(); + final SpacecraftState state = estimated.getStates()[0]; // Compute ionospheric delay (the division by the wavelength is performed) final ModifierGradientConverter converter = - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())); + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())); final FieldSpacecraftState gState = converter.getState(ionoModel); - final Gradient[] gParameters = converter.getParameters(gState, ionoModel); + final Gradient[] gParameters = converter.getParametersAtStateDate(gState, ionoModel); final Gradient gDelay = phaseErrorIonosphericModel(station, gState, gParameters); final double[] derivatives = gDelay.getGradient(); @@ -188,12 +204,14 @@ public void modify(final EstimatedMeasurement estimated) { int index = 0; for (final ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt ionospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - final double[] dDelaydP = phaseErrorParameterDerivative(derivatives, converter.getFreeStateParameters()); - parameterDerivative -= dDelaydP[index]; - estimated.setParameterDerivatives(driver, parameterDerivative); - index = index + 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // update estimated derivatives with derivative of the modification wrt ionospheric parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + final double[] dDelaydP = phaseErrorParameterDerivative(derivatives, converter.getFreeStateParameters()); + parameterDerivative -= dDelaydP[index]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + index = index + 1; + } } } @@ -204,18 +222,18 @@ public void modify(final EstimatedMeasurement estimated) { station.getNorthOffsetDriver(), station.getZenithOffsetDriver())) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt station parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative -= phaseErrorParameterDerivative(station, driver, state); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // update estimated derivatives with derivative of the modification wrt station parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative -= phaseErrorParameterDerivative(station, driver, state); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } // Update estimated value taking into account the ionospheric delay. - // The ionospheric delay is directly subtracted to the phase. - final double[] newValue = oldValue.clone(); - newValue[0] = newValue[0] - gDelay.getValue(); - estimated.setEstimatedValue(newValue); + modifyWithoutDerivatives(estimated); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java index a07e19365d..5a0d9c9a5a 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,10 +22,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.gnss.Phase; @@ -35,6 +34,7 @@ import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; +import org.orekit.utils.TimeSpanMap.Span; /** * Class modifying theoretical phase measurement with tropospheric delay. @@ -64,18 +64,16 @@ public PhaseTroposphericDelayModifier(final DiscreteTroposphericModel model) { * @return the measurement error due to Troposphere */ private double phaseErrorTroposphericModel(final GroundStation station, final SpacecraftState state, final double wavelength) { - // satellite position - final Vector3D position = state.getPVCoordinates().getPosition(); // elevation - final double elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final double elevation = + station.getBaseFrame().getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation > 0) { // delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state.getDate()); + final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); return delay / wavelength; } @@ -100,10 +98,9 @@ private > T phaseErrorTroposphericModel(final final T zero = field.getZero(); // satellite elevation - final FieldVector3D position = state.getPVCoordinates().getPosition(); - final T elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final T elevation = + station.getBaseFrame().getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon @@ -142,10 +139,10 @@ private double phaseErrorParameterDerivative(final GroundStation station, final ParameterDriver driver, final SpacecraftState state, final double wavelength) { - final ParameterFunction rangeError = parameterDriver -> phaseErrorTroposphericModel(station, state, wavelength); + final ParameterFunction rangeError = (parameterDriver, date) -> phaseErrorTroposphericModel(station, state, wavelength); final ParameterFunction phaseErrorDerivative = Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale()); - return phaseErrorDerivative.value(driver); + return phaseErrorDerivative.value(driver, state.getDate()); } @@ -177,18 +174,32 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + final Phase measurement = estimated.getObservedMeasurement(); final GroundStation station = measurement.getStation(); final SpacecraftState state = estimated.getStates()[0]; - // Old range value - final double[] oldValue = estimated.getEstimatedValue(); + // Update estimated value taking into account the tropospheric delay. + // The tropospheric delay is directly added to the phase. + final double[] newValue = estimated.getEstimatedValue(); + final double delay = phaseErrorTroposphericModel(station, state, measurement.getWavelength()); + newValue[0] = newValue[0] + delay; + estimated.setEstimatedValue(newValue); + + } + + /** {@inheritDoc} */ + @Override + public void modify(final EstimatedMeasurement estimated) { + final Phase measurement = estimated.getObservedMeasurement(); + final GroundStation station = measurement.getStation(); + final SpacecraftState state = estimated.getStates()[0]; // update estimated derivatives with Jacobian of the measure wrt state - final ModifierGradientConverter converter = new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())); + final ModifierGradientConverter converter = new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())); final FieldSpacecraftState gState = converter.getState(tropoModel); - final Gradient[] gParameters = converter.getParameters(gState, tropoModel); + final Gradient[] gParameters = converter.getParametersAtStateDate(gState, tropoModel); final Gradient gDelay = phaseErrorTroposphericModel(station, gState, gParameters, measurement.getWavelength()); final double[] derivatives = gDelay.getGradient(); @@ -207,12 +218,15 @@ public void modify(final EstimatedMeasurement estimated) { int index = 0; for (final ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt tropospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - final double[] dDelaydP = phaseErrorParameterDerivative(derivatives, converter.getFreeStateParameters()); - parameterDerivative += dDelaydP[index]; - estimated.setParameterDerivatives(driver, parameterDerivative); - index = index + 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt tropospheric parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + final double[] dDelaydP = phaseErrorParameterDerivative(derivatives, converter.getFreeStateParameters()); + parameterDerivative += dDelaydP[index]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + index = index + 1; + } } } @@ -222,18 +236,18 @@ public void modify(final EstimatedMeasurement estimated) { station.getNorthOffsetDriver(), station.getZenithOffsetDriver())) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt station parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += phaseErrorParameterDerivative(station, driver, state, measurement.getWavelength()); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // update estimated derivatives with derivative of the modification wrt station parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += phaseErrorParameterDerivative(station, driver, state, measurement.getWavelength()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } // Update estimated value taking into account the tropospheric delay. - // The tropospheric delay is directly added to the phase. - final double[] newValue = oldValue.clone(); - newValue[0] = newValue[0] + gDelay.getReal(); - estimated.setEstimatedValue(newValue); + modifyWithoutDerivatives(estimated); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java index dce6c1382f..7cf5729a18 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,9 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.Range; @@ -50,6 +51,17 @@ public RangeIonosphericDelayModifier(final IonosphericModel model, super(model, freq); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final Range measurement = estimated.getObservedMeasurement(); + final GroundStation station = measurement.getStation(); + + RangeModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeErrorIonosphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -59,7 +71,7 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; RangeModifierUtil.modify(estimated, getIonoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeErrorIonosphericModel, this::rangeErrorIonosphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java index 2f24bccb90..8a3bfe5eaf 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -27,7 +28,8 @@ import org.orekit.propagation.integration.AbstractGradientConverter; import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TimeSpanMap.Span; /** Utility class modifying theoretical range measurement. * @author Maxime Journot @@ -41,6 +43,29 @@ private RangeModifierUtil() { // not used } + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param station ground station + * @param modelEffect model effect + * @since 12.0 + */ + public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final GroundStation station, + final ParametricModelEffect modelEffect) { + + final SpacecraftState state = estimated.getStates()[0]; + final double[] oldValue = estimated.getEstimatedValue(); + + // update estimated value taking into account the ionospheric delay. + // The ionospheric delay is directly added to the range. + final double[] newValue = oldValue.clone(); + final double delay = modelEffect.evaluate(station, state); + newValue[0] = newValue[0] + delay; + estimated.setEstimatedValue(newValue); + + } + /** Apply a modifier to an estimated measurement. * @param type of the measurement * @param estimated estimated measurement to modify @@ -51,14 +76,13 @@ private RangeModifierUtil() { * @param modelEffectGradient model effect gradient */ public static > void modify(final EstimatedMeasurement estimated, - final ParametersDriversProvider parametricModel, + final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation station, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { - final SpacecraftState state = estimated.getStates()[0]; - final double[] oldValue = estimated.getEstimatedValue(); + final SpacecraftState state = estimated.getStates()[0]; // update estimated derivatives with Jacobian of the measure wrt state final FieldSpacecraftState gState = converter.getState(parametricModel); @@ -76,10 +100,12 @@ public static > void modify(final EstimatedMeas for (final ParameterDriver driver : parametricModel.getParametersDrivers()) { if (driver.isSelected()) { // update estimated derivatives with derivative of the modification wrt ionospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += derivatives[index + converter.getFreeStateParameters()]; - estimated.setParameterDerivatives(driver, parameterDerivative); - index = index + 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += derivatives[index + converter.getFreeStateParameters()]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + index = index + 1; + } } } @@ -90,18 +116,17 @@ public static > void modify(final EstimatedMeas station.getZenithOffsetDriver())) { if (driver.isSelected()) { // update estimated derivatives with derivative of the modification wrt station parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += Differentiation.differentiate(d -> modelEffect.evaluate(station, state), - 3, 10.0 * driver.getScale()).value(driver); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += Differentiation.differentiate((d, t) -> modelEffect.evaluate(station, state), + 3, 10.0 * driver.getScale()).value(driver, state.getDate()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } // update estimated value taking into account the ionospheric delay. - // The ionospheric delay is directly added to the range. - final double[] newValue = oldValue.clone(); - newValue[0] = newValue[0] + gDelay.getValue(); - estimated.setEstimatedValue(newValue); + modifyWithoutDerivatives(estimated, station, modelEffect); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java index 1e6163fcc2..53ad515371 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,9 @@ package org.orekit.estimation.measurements.modifiers; import org.hipparchus.CalculusFieldElement; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.RangeRate; @@ -76,6 +77,17 @@ protected > T rangeRateErrorIonosphericModel(f return super.rangeRateErrorIonosphericModel(station, state, parameters).multiply(fTwoWay); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final RangeRate measurement = estimated.getObservedMeasurement(); + final GroundStation station = measurement.getStation(); + + RangeModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeRateErrorIonosphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -85,7 +97,7 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; RangeModifierUtil.modify(estimated, getIonoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeRateErrorIonosphericModel, this::rangeRateErrorIonosphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java index 19fdf92e02..113484bf27 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -27,7 +28,8 @@ import org.orekit.propagation.integration.AbstractGradientConverter; import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TimeSpanMap.Span; /** Utility class modifying theoretical range-rate measurement. * @author Joris Olympio @@ -40,6 +42,27 @@ private RangeRateModifierUtil() { // not used } + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param station ground station + * @param modelEffect model effect + */ + public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final GroundStation station, + final ParametricModelEffect modelEffect) { + + final SpacecraftState state = estimated.getStates()[0]; + + // update estimated value taking into account the ionospheric delay. + // The ionospheric delay is directly added to the range. + final double[] newValue = estimated.getEstimatedValue(); + final double delay = modelEffect.evaluate(station, state); + newValue[0] = newValue[0] + delay; + estimated.setEstimatedValue(newValue); + + } + /** Apply a modifier to an estimated measurement. * @param type of the measurement * @param estimated estimated measurement to modify @@ -50,14 +73,13 @@ private RangeRateModifierUtil() { * @param modelEffectGradient model effect gradient */ public static > void modify(final EstimatedMeasurement estimated, - final ParametersDriversProvider parametricModel, + final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation station, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { - final SpacecraftState state = estimated.getStates()[0]; - final double[] oldValue = estimated.getEstimatedValue(); + final SpacecraftState state = estimated.getStates()[0]; // update estimated derivatives with Jacobian of the measure wrt state final FieldSpacecraftState gState = converter.getState(parametricModel); @@ -75,11 +97,14 @@ public static > void modify(final EstimatedMeas int index = 0; for (final ParameterDriver driver : parametricModel.getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt ionospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += derivatives[index + converter.getFreeStateParameters()]; - estimated.setParameterDerivatives(driver, parameterDerivative); - index = index + 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt ionospheric parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += derivatives[index + converter.getFreeStateParameters()]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + index = index + 1; + } } } @@ -89,19 +114,19 @@ public static > void modify(final EstimatedMeas station.getNorthOffsetDriver(), station.getZenithOffsetDriver())) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt station parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += Differentiation.differentiate(d -> modelEffect.evaluate(station, state), - 3, 10.0 * driver.getScale()).value(driver); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt station parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += Differentiation.differentiate((d, t) -> modelEffect.evaluate(station, state), + 3, 10.0 * driver.getScale()).value(driver, state.getDate()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } // update estimated value taking into account the ionospheric delay. - // The ionospheric delay is directly added to the range. - final double[] newValue = oldValue.clone(); - newValue[0] = newValue[0] + gDelay.getValue(); - estimated.setEstimatedValue(newValue); + modifyWithoutDerivatives(estimated, station, modelEffect); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java index 458bf834cc..39bc3ffeed 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,9 @@ package org.orekit.estimation.measurements.modifiers; import org.hipparchus.CalculusFieldElement; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.RangeRate; @@ -82,6 +83,18 @@ public > T rangeRateErrorTroposphericModel(fin return super.rangeRateErrorTroposphericModel(station, state, parameters).multiply(fTwoWay); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final RangeRate measurement = estimated.getObservedMeasurement(); + final GroundStation station = measurement.getStation(); + + RangeRateModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeRateErrorTroposphericModel); + + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -91,7 +104,7 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; RangeRateModifierUtil.modify(estimated, getTropoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeRateErrorTroposphericModel, this::rangeRateErrorTroposphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java index 7da7dbb9fa..55ac510e2e 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,9 @@ */ package org.orekit.estimation.measurements.modifiers; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.Range; @@ -45,6 +46,18 @@ public RangeTroposphericDelayModifier(final DiscreteTroposphericModel model) { super(model); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final Range measurement = estimated.getObservedMeasurement(); + final GroundStation station = measurement.getStation(); + + RangeModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeErrorTroposphericModel); + + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -54,7 +67,7 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; RangeModifierUtil.modify(estimated, getTropoModel(), - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeErrorTroposphericModel, this::rangeErrorTroposphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java index 4809e02f3f..01cacff134 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.utils.Constants; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtRel = relativisticCorrection(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifier.java index 7fad9b8981..ccb302504b 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.InterSatellitesRange; import org.orekit.utils.Constants; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtRel = relativisticCorrection(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifier.java index 560e9e0fba..fec186bb1c 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.utils.Constants; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtRel = relativisticCorrection(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifier.java index f2350daee2..a6568da2d5 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSRange; import org.orekit.utils.Constants; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtRel = relativisticCorrection(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java index a111219a8a..df50a4a7e3 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.Phase; import org.orekit.utils.Constants; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtRel = relativisticCorrection(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java index 83e3f83136..9bcd2c3f34 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.Range; import org.orekit.utils.Constants; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic effect final double dtRel = relativisticCorrection(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java index ac3c226f1a..f2006a6139 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.RangeRate; import org.orekit.propagation.SpacecraftState; @@ -58,14 +58,14 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Spacecraft state final SpacecraftState state = estimated.getStates()[0]; // Relativistic frequency deviation final double factor = -gm * getScaleFactor(); final double dfRel = factor * - (reciprocal(state.getA()) - reciprocal(state.getPVCoordinates().getPosition().getNorm())); + (reciprocal(state.getA()) - reciprocal(state.getPosition().getNorm())); // Update estimated value taking into account the relativistic effect. final double[] newValue = estimated.getEstimatedValue().clone(); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java index 83ed1b3bd2..d956bea01c 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.utils.Constants; @@ -66,7 +66,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtJ2 = relativisticJ2Correction(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifier.java index b0b858fa2f..16f61a5de6 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.InterSatellitesRange; import org.orekit.utils.Constants; @@ -66,7 +66,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic effect final double dtJ2 = relativisticJ2Correction(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifier.java index 66e802e2b3..19d3fef9d5 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.utils.Constants; @@ -66,7 +66,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtJ2 = relativisticJ2Correction(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifier.java index f2a46b63ea..fb6720af89 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSRange; import org.orekit.utils.Constants; @@ -66,7 +66,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic effect final double dtJ2 = relativisticJ2Correction(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockPhaseModifier.java index 4cea8869e1..46e08c5d29 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.Phase; import org.orekit.utils.Constants; @@ -64,7 +64,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic clock correction final double dtJ2 = relativisticJ2Correction(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java index a760a84561..96413d9646 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.Range; import org.orekit.utils.Constants; @@ -65,7 +65,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Relativistic effect final double dtJ2 = relativisticJ2Correction(estimated); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java index 55d3336344..c5011e3e94 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.utils.ParameterDriver; @@ -49,7 +49,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Compute correction final TimeStampedPVCoordinates[] pv = estimated.getParticipants(); final double correction = shapiroCorrection(pv[0], pv[1]); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifier.java index 34044228b2..b5ccde695d 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.InterSatellitesRange; import org.orekit.utils.ParameterDriver; @@ -49,7 +49,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { doModify(estimated); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java index 8123bcf1a8..62aa39ebe0 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.utils.ParameterDriver; @@ -49,7 +49,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // Compute correction final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); final double phaseCorrection = shapiroCorrection(participants[0], participants[1]); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifier.java index f82cf5b960..e9f8fe520e 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSRange; import org.orekit.utils.ParameterDriver; @@ -49,7 +49,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { doModify(estimated); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java index 14b34c0c41..c25f289118 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.Phase; import org.orekit.utils.ParameterDriver; @@ -51,7 +51,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // wavelength final double wavelength = estimated.getObservedMeasurement().getWavelength(); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java index 7d6df3e0a9..e15726dc45 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.Collections; import java.util.List; -import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.Range; import org.orekit.utils.ParameterDriver; @@ -49,7 +49,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { doModify(estimated); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java index db891a5963..bccf94a945 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,8 +19,9 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.TDOA; @@ -70,7 +71,7 @@ private double timeErrorIonosphericModel(final GroundStation station, // base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // delay in meters - final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters()); + final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); // return delay in seconds return delay / Constants.SPEED_OF_LIGHT; } @@ -99,15 +100,27 @@ public List getParametersDrivers() { return ionoModel.getParametersDrivers(); } + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final TDOA measurement = estimated.getObservedMeasurement(); + final GroundStation primeStation = measurement.getPrimeStation(); + final GroundStation secondStation = measurement.getSecondStation(); + + TDOAModifierUtil.modifyWithoutDerivatives(estimated, primeStation, secondStation, this::timeErrorIonosphericModel); + + } + @Override public void modify(final EstimatedMeasurement estimated) { + final TDOA measurement = estimated.getObservedMeasurement(); final GroundStation primeStation = measurement.getPrimeStation(); final GroundStation secondStation = measurement.getSecondStation(); final SpacecraftState state = estimated.getStates()[0]; TDOAModifierUtil.modify(estimated, ionoModel, - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), primeStation, secondStation, this::timeErrorIonosphericModel, this::timeErrorIonosphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java index ead549e62c..9b55183365 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -27,7 +28,8 @@ import org.orekit.propagation.integration.AbstractGradientConverter; import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TimeSpanMap.Span; /** Utility class for TDOA measurements. * @author Pascal Parraud @@ -40,6 +42,31 @@ private TDOAModifierUtil() { // not used } + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param primeStation prime station + * @param secondStation second station + * @param modelEffect model effect + */ + public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final GroundStation primeStation, + final GroundStation secondStation, + final ParametricModelEffect modelEffect) { + + final SpacecraftState state = estimated.getStates()[0]; + final double[] oldValue = estimated.getEstimatedValue(); + final double primeDelay = modelEffect.evaluate(primeStation, state); + final double secondDelay = modelEffect.evaluate(secondStation, state); + + // Update estimated value taking into account the ionospheric delay for each downlink. + // The ionospheric time delay is directly applied to the TDOA. + final double[] newValue = oldValue.clone(); + newValue[0] += primeDelay; + newValue[0] -= secondDelay; + estimated.setEstimatedValue(newValue); + } + /** Apply a modifier to an estimated measurement. * @param type of the measurement * @param estimated estimated measurement to modify @@ -51,7 +78,7 @@ private TDOAModifierUtil() { * @param modelEffectGradient model effect gradient */ public static > void modify(final EstimatedMeasurement estimated, - final ParametersDriversProvider parametricModel, + final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation primeStation, final GroundStation secondStation, final ParametricModelEffect modelEffect, @@ -78,12 +105,15 @@ public static > void modify(final EstimatedMeas int index = 0; for (final ParameterDriver driver : parametricModel.getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt ionospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += primeDerivatives[index + converter.getFreeStateParameters()]; - parameterDerivative -= secondDerivatives[index + converter.getFreeStateParameters()]; - estimated.setParameterDerivatives(driver, parameterDerivative); - index += 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // update estimated derivatives with derivative of the modification wrt ionospheric parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += primeDerivatives[index + converter.getFreeStateParameters()]; + parameterDerivative -= secondDerivatives[index + converter.getFreeStateParameters()]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + index += 1; + } } } @@ -94,10 +124,13 @@ public static > void modify(final EstimatedMeas primeStation.getNorthOffsetDriver(), primeStation.getZenithOffsetDriver())) { if (driver.isSelected()) { - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += Differentiation.differentiate(d -> modelEffect.evaluate(primeStation, state), - 3, 10.0 * driver.getScale()).value(driver); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += Differentiation.differentiate((d, t) -> modelEffect.evaluate(primeStation, state), + 3, 10.0 * driver.getScale()).value(driver, state.getDate()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } @@ -107,10 +140,13 @@ public static > void modify(final EstimatedMeas secondStation.getNorthOffsetDriver(), secondStation.getZenithOffsetDriver())) { if (driver.isSelected()) { - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative -= Differentiation.differentiate(d -> modelEffect.evaluate(secondStation, state), - 3, 10.0 * driver.getScale()).value(driver); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative -= Differentiation.differentiate((d, t) -> modelEffect.evaluate(secondStation, state), + 3, 10.0 * driver.getScale()).value(driver, state.getDate()); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java index c2de8174f6..87efd5b07c 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,8 +22,9 @@ import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.TDOA; @@ -62,18 +63,18 @@ public TDOATroposphericDelayModifier(final DiscreteTroposphericModel model) { * @return the measurement error due to Troposphere (s) */ private double timeErrorTroposphericModel(final GroundStation station, final SpacecraftState state) { - final Vector3D position = state.getPVCoordinates().getPosition(); + final Vector3D position = state.getPosition(); // elevation - final double elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final double elevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measurements above the horizon if (elevation > 0) { // Delay in meters final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), - tropoModel.getParameters(), state.getDate()); + tropoModel.getParameters(state.getDate()), state.getDate()); // return delay in seconds return delay / Constants.SPEED_OF_LIGHT; } @@ -96,10 +97,10 @@ private > T timeErrorTroposphericModel(final G final T zero = field.getZero(); // elevation - final FieldVector3D pos = state.getPVCoordinates().getPosition(); - final T elevation = station.getBaseFrame().getElevation(pos, - state.getFrame(), - state.getDate()); + final FieldVector3D pos = state.getPosition(); + final T elevation = + station.getBaseFrame().getTrackingCoordinates(pos, state.getFrame(), state.getDate()). + getElevation(); // only consider measurements above the horizon if (elevation.getReal() > 0) { @@ -119,6 +120,18 @@ public List getParametersDrivers() { return tropoModel.getParametersDrivers(); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final TDOA measurement = estimated.getObservedMeasurement(); + final GroundStation primeStation = measurement.getPrimeStation(); + final GroundStation secondStation = measurement.getSecondStation(); + + TDOAModifierUtil.modifyWithoutDerivatives(estimated, primeStation, secondStation, this::timeErrorTroposphericModel); + + } + /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -129,7 +142,7 @@ public void modify(final EstimatedMeasurement estimated) { final SpacecraftState state = estimated.getStates()[0]; TDOAModifierUtil.modify(estimated, tropoModel, - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())), + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), primeStation, secondStation, this::timeErrorTroposphericModel, this::timeErrorTroposphericModel); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TroposphericGradientConverter.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TroposphericGradientConverter.java deleted file mode 100644 index 090bd0570b..0000000000 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TroposphericGradientConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.measurements.modifiers; - -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.propagation.SpacecraftState; - -/** - * Converter for states and parameters arrays. - * @author Bryan Cazabonne - * @since 10.2 - * @deprecated as of 11.2, replaced by {@link ModifierGradientConverter} - */ -@Deprecated -public class TroposphericGradientConverter extends ModifierGradientConverter { - - /** Simple constructor. - * @param state regular state - * @param freeStateParameters number of free parameters, either 3 (position) or 6 (position-velocity) - * @param provider provider to use if attitude needs to be recomputed - */ - public TroposphericGradientConverter(final SpacecraftState state, final int freeStateParameters, - final AttitudeProvider provider) { - super(state, freeStateParameters, provider); - } - -} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java index 7890366be9..c3d57ee3be 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,8 +21,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.TurnAroundRange; @@ -30,9 +31,11 @@ import org.orekit.models.earth.ionosphere.IonosphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; +import org.orekit.utils.TimeSpanMap.Span; /** Class modifying theoretical TurnAroundRange measurement with ionospheric delay. * The effect of ionospheric correction on the TurnAroundRange is directly computed @@ -76,7 +79,7 @@ private double rangeErrorIonosphericModel(final GroundStation station, // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // Delay in meters - final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters()); + final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); return delay; } @@ -125,7 +128,7 @@ private double rangeErrorParameterDerivative(final GroundStation station, final ParameterFunction rangeError = new ParameterFunction() { /** {@inheritDoc} */ @Override - public double value(final ParameterDriver parameterDriver) { + public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return rangeErrorIonosphericModel(station, state); } }; @@ -133,7 +136,7 @@ public double value(final ParameterDriver parameterDriver) { final ParameterFunction rangeErrorDerivative = Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale()); - return rangeErrorDerivative.value(driver); + return rangeErrorDerivative.value(driver, state.getDate()); } @@ -164,19 +167,35 @@ public List getParametersDrivers() { } @Override - public void modify(final EstimatedMeasurement estimated) { + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + final TurnAroundRange measurement = estimated.getObservedMeasurement(); final GroundStation primaryStation = measurement.getPrimaryStation(); final GroundStation secondaryStation = measurement.getSecondaryStation(); final SpacecraftState state = estimated.getStates()[0]; - final double[] oldValue = estimated.getEstimatedValue(); + // Update estimated value taking into account the ionospheric delay. + // The ionospheric delay is directly added to the TurnAroundRange. + final double[] newValue = estimated.getEstimatedValue(); + final double primaryDelay = rangeErrorIonosphericModel(primaryStation, state); + final double secondaryDelay = rangeErrorIonosphericModel(secondaryStation, state); + newValue[0] = newValue[0] + primaryDelay + secondaryDelay; + estimated.setEstimatedValue(newValue); + + } + + @Override + public void modify(final EstimatedMeasurement estimated) { + final TurnAroundRange measurement = estimated.getObservedMeasurement(); + final GroundStation primaryStation = measurement.getPrimaryStation(); + final GroundStation secondaryStation = measurement.getSecondaryStation(); + final SpacecraftState state = estimated.getStates()[0]; // Update estimated derivatives with Jacobian of the measure wrt state final ModifierGradientConverter converter = - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())); + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())); final FieldSpacecraftState gState = converter.getState(ionoModel); - final Gradient[] gParameters = converter.getParameters(gState, ionoModel); + final Gradient[] gParameters = converter.getParametersAtStateDate(gState, ionoModel); final Gradient primaryGDelay = rangeErrorIonosphericModel(primaryStation, gState, gParameters); final Gradient secondaryGDelay = rangeErrorIonosphericModel(secondaryStation, gState, gParameters); final double[] primaryDerivatives = primaryGDelay.getGradient(); @@ -195,12 +214,14 @@ public void modify(final EstimatedMeasurement estimated) { int indexPrimary = 0; for (final ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt ionospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - final double[] derivatives = rangeErrorParameterDerivative(primaryDerivatives, converter.getFreeStateParameters()); - parameterDerivative += derivatives[indexPrimary]; - estimated.setParameterDerivatives(driver, parameterDerivative); - indexPrimary += 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // update estimated derivatives with derivative of the modification wrt ionospheric parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + final double[] derivatives = rangeErrorParameterDerivative(primaryDerivatives, converter.getFreeStateParameters()); + parameterDerivative += derivatives[indexPrimary]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + indexPrimary += 1; + } } } @@ -208,12 +229,14 @@ public void modify(final EstimatedMeasurement estimated) { int indexSecondary = 0; for (final ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { - // update estimated derivatives with derivative of the modification wrt ionospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - final double[] derivatives = rangeErrorParameterDerivative(secondaryDerivatives, converter.getFreeStateParameters()); - parameterDerivative += derivatives[indexSecondary]; - estimated.setParameterDerivatives(driver, parameterDerivative); - indexSecondary += 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // update estimated derivatives with derivative of the modification wrt ionospheric parameters + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + final double[] derivatives = rangeErrorParameterDerivative(secondaryDerivatives, converter.getFreeStateParameters()); + parameterDerivative += derivatives[indexSecondary]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + indexSecondary += 1; + } } } @@ -224,9 +247,12 @@ public void modify(final EstimatedMeasurement estimated) { primaryStation.getNorthOffsetDriver(), primaryStation.getZenithOffsetDriver())) { if (driver.isSelected()) { - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += rangeErrorParameterDerivative(primaryStation, driver, state); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += rangeErrorParameterDerivative(primaryStation, driver, state); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } @@ -235,15 +261,18 @@ public void modify(final EstimatedMeasurement estimated) { secondaryStation.getNorthOffsetDriver(), secondaryStation.getZenithOffsetDriver())) { if (driver.isSelected()) { - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += rangeErrorParameterDerivative(secondaryStation, driver, state); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += rangeErrorParameterDerivative(secondaryStation, driver, state); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } // Update estimated value taking into account the ionospheric delay. // The ionospheric delay is directly added to the TurnAroundRange. - final double[] newValue = oldValue.clone(); + final double[] newValue = estimated.getEstimatedValue(); newValue[0] = newValue[0] + primaryGDelay.getReal() + secondaryGDelay.getReal(); estimated.setEstimatedValue(newValue); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java index 674fb0fdbf..6e0706513e 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,22 +19,25 @@ import java.util.Arrays; import java.util.List; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.TurnAroundRange; import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; +import org.orekit.utils.TimeSpanMap.Span; /** Class modifying theoretical turn-around TurnAroundRange measurement with tropospheric delay. * The effect of tropospheric correction on the TurnAroundRange is directly computed @@ -66,17 +69,17 @@ public TurnAroundRangeTroposphericDelayModifier(final DiscreteTroposphericModel */ private double rangeErrorTroposphericModel(final GroundStation station, final SpacecraftState state) { // - final Vector3D position = state.getPVCoordinates().getPosition(); + final Vector3D position = state.getPosition(); // elevation - final double elevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final double elevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (elevation > 0) { // Delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state.getDate()); + final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); return delay; } @@ -99,10 +102,10 @@ private > T rangeErrorTroposphericModel(final final T zero = field.getZero(); // - final FieldVector3D position = state.getPVCoordinates().getPosition(); - final T dsElevation = station.getBaseFrame().getElevation(position, - state.getFrame(), - state.getDate()); + final FieldVector3D position = state.getPosition(); + final T dsElevation = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). + getElevation(); // only consider measures above the horizon if (dsElevation.getReal() > 0) { @@ -143,14 +146,14 @@ private double rangeErrorParameterDerivative(final GroundStation station, final ParameterFunction rangeError = new ParameterFunction() { /** {@inheritDoc} */ @Override - public double value(final ParameterDriver parameterDriver) { + public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return rangeErrorTroposphericModel(station, state); } }; final ParameterFunction rangeErrorDerivative = Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale()); - return rangeErrorDerivative.value(driver); + return rangeErrorDerivative.value(driver, state.getDate()); } @@ -180,6 +183,24 @@ public List getParametersDrivers() { return tropoModel.getParametersDrivers(); } + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + final TurnAroundRange measurement = estimated.getObservedMeasurement(); + final GroundStation primaryStation = measurement.getPrimaryStation(); + final GroundStation secondaryStation = measurement.getSecondaryStation(); + final SpacecraftState state = estimated.getStates()[0]; + + // Update estimated value taking into account the tropospheric delay. + // The tropospheric delay is directly added to the TurnAroundRange. + final double[] newValue = estimated.getEstimatedValue(); + final double primaryDelay = rangeErrorTroposphericModel(primaryStation, state); + final double secondaryDelay = rangeErrorTroposphericModel(secondaryStation, state); + newValue[0] = newValue[0] + primaryDelay + secondaryDelay; + estimated.setEstimatedValue(newValue); + + } /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { @@ -192,9 +213,9 @@ public void modify(final EstimatedMeasurement estimated) { // Update estimated derivatives with Jacobian of the measure wrt state final ModifierGradientConverter converter = - new ModifierGradientConverter(state, 6, new InertialProvider(state.getFrame())); + new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())); final FieldSpacecraftState gState = converter.getState(tropoModel); - final Gradient[] gParameters = converter.getParameters(gState, tropoModel); + final Gradient[] gParameters = converter.getParametersAtStateDate(gState, tropoModel); final Gradient primaryGDelay = rangeErrorTroposphericModel(primaryStation, gState, gParameters); final Gradient secondaryGDelay = rangeErrorTroposphericModel(secondaryStation, gState, gParameters); final double[] primaryDerivatives = primaryGDelay.getGradient(); @@ -214,11 +235,13 @@ public void modify(final EstimatedMeasurement estimated) { for (final ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { // update estimated derivatives with derivative of the modification wrt tropospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - final double[] derivatives = rangeErrorParameterDerivative(primaryDerivatives, converter.getFreeStateParameters()); - parameterDerivative += derivatives[indexPrimary]; - estimated.setParameterDerivatives(driver, parameterDerivative); - indexPrimary += 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + final double[] derivatives = rangeErrorParameterDerivative(primaryDerivatives, converter.getFreeStateParameters()); + parameterDerivative += derivatives[indexPrimary]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + indexPrimary += 1; + } } } @@ -227,11 +250,13 @@ public void modify(final EstimatedMeasurement estimated) { for (final ParameterDriver driver : getParametersDrivers()) { if (driver.isSelected()) { // update estimated derivatives with derivative of the modification wrt tropospheric parameters - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - final double[] derivatives = rangeErrorParameterDerivative(secondaryDerivatives, converter.getFreeStateParameters()); - parameterDerivative += derivatives[indexSecondary]; - estimated.setParameterDerivatives(driver, parameterDerivative); - indexSecondary += 1; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + final double[] derivatives = rangeErrorParameterDerivative(secondaryDerivatives, converter.getFreeStateParameters()); + parameterDerivative += derivatives[indexSecondary]; + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + indexSecondary += 1; + } } } @@ -242,9 +267,12 @@ public void modify(final EstimatedMeasurement estimated) { primaryStation.getNorthOffsetDriver(), primaryStation.getZenithOffsetDriver())) { if (driver.isSelected()) { - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += rangeErrorParameterDerivative(primaryStation, driver, state); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += rangeErrorParameterDerivative(primaryStation, driver, state); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } @@ -253,9 +281,12 @@ public void modify(final EstimatedMeasurement estimated) { secondaryStation.getNorthOffsetDriver(), secondaryStation.getZenithOffsetDriver())) { if (driver.isSelected()) { - double parameterDerivative = estimated.getParameterDerivatives(driver)[0]; - parameterDerivative += rangeErrorParameterDerivative(secondaryStation, driver, state); - estimated.setParameterDerivatives(driver, parameterDerivative); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0]; + parameterDerivative += rangeErrorParameterDerivative(secondaryStation, driver, state); + estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative); + } } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/package-info.java b/src/main/java/org/orekit/estimation/measurements/modifiers/package-info.java index 094da88e15..e8f99c7596 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/package-info.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/measurements/package-info.java b/src/main/java/org/orekit/estimation/measurements/package-info.java index 0d52ebcaf7..f2158d75dc 100644 --- a/src/main/java/org/orekit/estimation/measurements/package-info.java +++ b/src/main/java/org/orekit/estimation/measurements/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/package-info.java b/src/main/java/org/orekit/estimation/package-info.java index c7cdc8ad8d..b35819c0f2 100644 --- a/src/main/java/org/orekit/estimation/package-info.java +++ b/src/main/java/org/orekit/estimation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/AbstractCovarianceMatrixProvider.java b/src/main/java/org/orekit/estimation/sequential/AbstractCovarianceMatrixProvider.java index 6929b3b7c5..4697842289 100644 --- a/src/main/java/org/orekit/estimation/sequential/AbstractCovarianceMatrixProvider.java +++ b/src/main/java/org/orekit/estimation/sequential/AbstractCovarianceMatrixProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/AbstractKalmanEstimator.java b/src/main/java/org/orekit/estimation/sequential/AbstractKalmanEstimator.java index 13891826f9..07b300dfc3 100644 --- a/src/main/java/org/orekit/estimation/sequential/AbstractKalmanEstimator.java +++ b/src/main/java/org/orekit/estimation/sequential/AbstractKalmanEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/AbstractKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/AbstractKalmanModel.java deleted file mode 100644 index d69a84c5fb..0000000000 --- a/src/main/java/org/orekit/estimation/sequential/AbstractKalmanModel.java +++ /dev/null @@ -1,1079 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.sequential; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.hipparchus.filtering.kalman.ProcessEstimate; -import org.hipparchus.filtering.kalman.extended.NonLinearEvolution; -import org.hipparchus.filtering.kalman.extended.NonLinearProcess; -import org.hipparchus.linear.Array2DRowRealMatrix; -import org.hipparchus.linear.ArrayRealVector; -import org.hipparchus.linear.MatrixUtils; -import org.hipparchus.linear.RealMatrix; -import org.hipparchus.linear.RealVector; -import org.orekit.estimation.measurements.EstimatedMeasurement; -import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.orbits.Orbit; -import org.orekit.propagation.MatricesHarvester; -import org.orekit.propagation.PropagationType; -import org.orekit.propagation.Propagator; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.propagation.integration.AbstractJacobiansMapper; -import org.orekit.time.AbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; -import org.orekit.utils.ParameterDriversList.DelegatingDriver; - -/** Abstract class defining the process model dynamics to use with a {@link KalmanEstimator}. - * @author Romain Gerbaud - * @author Maxime Journot - * @author Bryan Cazabonne - * @author Thomas Paulet - * @since 11.0 - */ -public abstract class AbstractKalmanModel implements KalmanEstimation, NonLinearProcess { - - /** Builders for propagators. */ - private final List builders; - - /** Estimated orbital parameters. */ - private final ParameterDriversList allEstimatedOrbitalParameters; - - /** Estimated propagation drivers. */ - private final ParameterDriversList allEstimatedPropagationParameters; - - /** Per-builder estimated orbita parameters drivers. - * @since 11.1 - */ - private final ParameterDriversList[] estimatedOrbitalParameters; - - /** Per-builder estimated propagation drivers. */ - private final ParameterDriversList[] estimatedPropagationParameters; - - /** Estimated measurements parameters. */ - private final ParameterDriversList estimatedMeasurementsParameters; - - /** Start columns for each estimated orbit. */ - private final int[] orbitsStartColumns; - - /** End columns for each estimated orbit. */ - private final int[] orbitsEndColumns; - - /** Map for propagation parameters columns. */ - private final Map propagationParameterColumns; - - /** Map for measurements parameters columns. */ - private final Map measurementParameterColumns; - - /** Providers for covariance matrices. */ - private final List covarianceMatricesProviders; - - /** Process noise matrix provider for measurement parameters. */ - private final CovarianceMatrixProvider measurementProcessNoiseMatrix; - - /** Indirection arrays to extract the noise components for estimated parameters. */ - private final int[][] covarianceIndirection; - - /** Scaling factors. */ - private final double[] scale; - - /** Harvesters for extracting Jacobians from integrated states. */ - private MatricesHarvester[] harvesters; - - /** Propagators for the reference trajectories, up to current date. */ - private Propagator[] referenceTrajectories; - - /** Current corrected estimate. */ - private ProcessEstimate correctedEstimate; - - /** Current number of measurement. */ - private int currentMeasurementNumber; - - /** Reference date. */ - private AbsoluteDate referenceDate; - - /** Current date. */ - private AbsoluteDate currentDate; - - /** Predicted spacecraft states. */ - private SpacecraftState[] predictedSpacecraftStates; - - /** Corrected spacecraft states. */ - private SpacecraftState[] correctedSpacecraftStates; - - /** Predicted measurement. */ - private EstimatedMeasurement predictedMeasurement; - - /** Corrected measurement. */ - private EstimatedMeasurement correctedMeasurement; - - /** Type of the orbit used for the propagation.*/ - private PropagationType propagationType; - - /** Type of the elements used to define the orbital state.*/ - private PropagationType stateType; - - /** Kalman process model constructor (package private). - * This constructor is used whenever state type and propagation type do not matter. - * It is used for {@link KalmanModel} and {@link TLEKalmanModel}. - * @param propagatorBuilders propagators builders used to evaluate the orbits. - * @param covarianceMatricesProviders providers for covariance matrices - * @param estimatedMeasurementParameters measurement parameters to estimate - * @param measurementProcessNoiseMatrix provider for measurement process noise matrix - * @param harvesters harvesters for extracting Jacobians from integrated states - */ - protected AbstractKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix, - final MatricesHarvester[] harvesters) { - this(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, - measurementProcessNoiseMatrix, harvesters, PropagationType.MEAN, PropagationType.MEAN); - } - - /** Kalman process model constructor (package private). - * This constructor is used whenever propagation type and/or state type are to be specified. - * It is used for {@link DSSTKalmanModel}. - * @param propagatorBuilders propagators builders used to evaluate the orbits. - * @param covarianceMatricesProviders providers for covariance matrices - * @param estimatedMeasurementParameters measurement parameters to estimate - * @param measurementProcessNoiseMatrix provider for measurement process noise matrix - * @param harvesters harvesters for extracting Jacobians from integrated states - * @param propagationType type of the orbit used for the propagation (mean or osculating), applicable only for DSST - * @param stateType type of the elements used to define the orbital state (mean or osculating), applicable only for DSST - */ - protected AbstractKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix, - final MatricesHarvester[] harvesters, - final PropagationType propagationType, - final PropagationType stateType) { - - this.builders = propagatorBuilders; - this.estimatedMeasurementsParameters = estimatedMeasurementParameters; - this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size()); - this.currentMeasurementNumber = 0; - this.referenceDate = propagatorBuilders.get(0).getInitialOrbitDate(); - this.currentDate = referenceDate; - this.propagationType = propagationType; - this.stateType = stateType; - - final Map orbitalParameterColumns = new HashMap<>(6 * builders.size()); - orbitsStartColumns = new int[builders.size()]; - orbitsEndColumns = new int[builders.size()]; - int columns = 0; - allEstimatedOrbitalParameters = new ParameterDriversList(); - estimatedOrbitalParameters = new ParameterDriversList[builders.size()]; - for (int k = 0; k < builders.size(); ++k) { - estimatedOrbitalParameters[k] = new ParameterDriversList(); - orbitsStartColumns[k] = columns; - final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null; - for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(currentDate); - } - if (suffix != null && !driver.getName().endsWith(suffix)) { - // we add suffix only conditionally because the method may already have been called - // and suffixes may have already been appended - driver.setName(driver.getName() + suffix); - } - if (driver.isSelected()) { - allEstimatedOrbitalParameters.add(driver); - estimatedOrbitalParameters[k].add(driver); - orbitalParameterColumns.put(driver.getName(), columns++); - } - } - orbitsEndColumns[k] = columns; - } - - // Gather all the propagation drivers names in a list - allEstimatedPropagationParameters = new ParameterDriversList(); - estimatedPropagationParameters = new ParameterDriversList[builders.size()]; - final List estimatedPropagationParametersNames = new ArrayList<>(); - for (int k = 0; k < builders.size(); ++k) { - estimatedPropagationParameters[k] = new ParameterDriversList(); - for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(currentDate); - } - if (driver.isSelected()) { - allEstimatedPropagationParameters.add(driver); - estimatedPropagationParameters[k].add(driver); - final String driverName = driver.getName(); - // Add the driver name if it has not been added yet - if (!estimatedPropagationParametersNames.contains(driverName)) { - estimatedPropagationParametersNames.add(driverName); - } - } - } - } - estimatedPropagationParametersNames.sort(Comparator.naturalOrder()); - - // Populate the map of propagation drivers' columns and update the total number of columns - propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size()); - for (final String driverName : estimatedPropagationParametersNames) { - propagationParameterColumns.put(driverName, columns); - ++columns; - } - - // Populate the map of measurement drivers' columns and update the total number of columns - for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { - if (parameter.getReferenceDate() == null) { - parameter.setReferenceDate(currentDate); - } - measurementParameterColumns.put(parameter.getName(), columns); - ++columns; - } - - // Store providers for process noise matrices - this.covarianceMatricesProviders = covarianceMatricesProviders; - this.measurementProcessNoiseMatrix = measurementProcessNoiseMatrix; - this.covarianceIndirection = new int[builders.size()][columns]; - for (int k = 0; k < covarianceIndirection.length; ++k) { - final ParameterDriversList orbitDrivers = builders.get(k).getOrbitalParametersDrivers(); - final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers(); - Arrays.fill(covarianceIndirection[k], -1); - int i = 0; - for (final ParameterDriver driver : orbitDrivers.getDrivers()) { - final Integer c = orbitalParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - for (final ParameterDriver driver : parametersDrivers.getDrivers()) { - final Integer c = propagationParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) { - final Integer c = measurementParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - } - - // Compute the scale factors - this.scale = new double[columns]; - int index = 0; - for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { - scale[index++] = driver.getScale(); - } - for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { - scale[index++] = driver.getScale(); - } - for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - scale[index++] = driver.getScale(); - } - - // Build the reference propagators and add their partial derivatives equations implementation - this.harvesters = harvesters.clone(); - updateReferenceTrajectories(getEstimatedPropagators(), propagationType, stateType); - this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length]; - for (int i = 0; i < predictedSpacecraftStates.length; ++i) { - predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState(); - }; - this.correctedSpacecraftStates = predictedSpacecraftStates.clone(); - - // Initialize the estimated normalized state and fill its values - final RealVector correctedState = MatrixUtils.createRealVector(columns); - - int p = 0; - for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getNormalizedValue()); - } - for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getNormalizedValue()); - } - for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getNormalizedValue()); - } - - // Set up initial covariance - final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns); - for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { - - // Number of estimated measurement parameters - final int nbMeas = estimatedMeasurementParameters.getNbParams(); - - // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + - estimatedPropagationParameters[k].getNbParams(); - - // Covariance matrix - final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); - if (nbDyn > 0) { - final RealMatrix noiseP = covarianceMatricesProviders.get(k). - getInitialCovarianceMatrix(correctedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseP.getData(), 0, 0); - } - if (measurementProcessNoiseMatrix != null) { - final RealMatrix noiseM = measurementProcessNoiseMatrix. - getInitialCovarianceMatrix(correctedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); - } - - KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), - builders.get(k).getOrbitalParametersDrivers(), - builders.get(k).getPropagationParametersDrivers(), - estimatedMeasurementsParameters); - - final int[] indK = covarianceIndirection[k]; - for (int i = 0; i < indK.length; ++i) { - if (indK[i] >= 0) { - for (int j = 0; j < indK.length; ++j) { - if (indK[j] >= 0) { - physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); - } - } - } - } - - } - final RealMatrix correctedCovariance = normalizeCovarianceMatrix(physicalProcessNoise); - - correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance); - - } - - /** Update the reference trajectories using the propagators as input. - * @param propagators The new propagators to use - * @param pType propagationType type of the orbit used for the propagation (mean or osculating) - * @param sType type of the elements used to define the orbital state (mean or osculating) - */ - protected abstract void updateReferenceTrajectories(Propagator[] propagators, - PropagationType pType, - PropagationType sType); - - /** Not used anymore. - * @param mapper Jacobian mapper to calculate short period perturbations - * @param state state used to calculate short period perturbations - * @deprecated as of 11.1, not used anymore - */ - protected void analyticalDerivativeComputations(final AbstractJacobiansMapper mapper, final SpacecraftState state) { - // nothing by default - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalStateTransitionMatrix() { - // Un-normalize the state transition matrix (φ) from Hipparchus and return it. - // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas - // For each element [i,j] of normalized φ (φn), the corresponding physical value is: - // φ[i,j] = φn[i,j] * scale[i] / scale[j] - - // Normalized matrix - final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix(); - - if (normalizedSTM == null) { - return null; - } else { - // Initialize physical matrix - final int nbParams = normalizedSTM.getRowDimension(); - final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams); - - // Un-normalize the matrix - for (int i = 0; i < nbParams; ++i) { - for (int j = 0; j < nbParams; ++j) { - physicalSTM.setEntry(i, j, - normalizedSTM.getEntry(i, j) * scale[i] / scale[j]); - } - } - return physicalSTM; - } - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalMeasurementJacobian() { - // Un-normalize the measurement matrix (H) from Hipparchus and return it. - // H is an nxm matrix where: - // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters - // - n is the size of the measurement being processed by the filter - // For each element [i,j] of normalized H (Hn) the corresponding physical value is: - // H[i,j] = Hn[i,j] * σ[i] / scale[j] - - // Normalized matrix - final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian(); - - if (normalizedH == null) { - return null; - } else { - // Get current measurement sigmas - final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation(); - - // Initialize physical matrix - final int nbLine = normalizedH.getRowDimension(); - final int nbCol = normalizedH.getColumnDimension(); - final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol); - - // Un-normalize the matrix - for (int i = 0; i < nbLine; ++i) { - for (int j = 0; j < nbCol; ++j) { - physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]); - } - } - return physicalH; - } - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalInnovationCovarianceMatrix() { - // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it. - // S is an nxn matrix where n is the size of the measurement being processed by the filter - // For each element [i,j] of normalized S (Sn) the corresponding physical value is: - // S[i,j] = Sn[i,j] * σ[i] * σ[j] - - // Normalized matrix - final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance(); - - if (normalizedS == null) { - return null; - } else { - // Get current measurement sigmas - final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation(); - - // Initialize physical matrix - final int nbMeas = sigmas.length; - final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas); - - // Un-normalize the matrix - for (int i = 0; i < nbMeas; ++i) { - for (int j = 0; j < nbMeas; ++j) { - physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] * sigmas[j]); - } - } - return physicalS; - } - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalKalmanGain() { - // Un-normalize the Kalman gain (K) from Hipparchus and return it. - // K is an mxn matrix where: - // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters - // - n is the size of the measurement being processed by the filter - // For each element [i,j] of normalized K (Kn) the corresponding physical value is: - // K[i,j] = Kn[i,j] * scale[i] / σ[j] - - // Normalized matrix - final RealMatrix normalizedK = correctedEstimate.getKalmanGain(); - - if (normalizedK == null) { - return null; - } else { - // Get current measurement sigmas - final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation(); - - // Initialize physical matrix - final int nbLine = normalizedK.getRowDimension(); - final int nbCol = normalizedK.getColumnDimension(); - final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol); - - // Un-normalize the matrix - for (int i = 0; i < nbLine; ++i) { - for (int j = 0; j < nbCol; ++j) { - physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]); - } - } - return physicalK; - } - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState[] getPredictedSpacecraftStates() { - return predictedSpacecraftStates.clone(); - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState[] getCorrectedSpacecraftStates() { - return correctedSpacecraftStates.clone(); - } - - /** {@inheritDoc} */ - @Override - public int getCurrentMeasurementNumber() { - return currentMeasurementNumber; - } - - /** {@inheritDoc} */ - @Override - public AbsoluteDate getCurrentDate() { - return currentDate; - } - - /** {@inheritDoc} */ - @Override - public EstimatedMeasurement getPredictedMeasurement() { - return predictedMeasurement; - } - - /** {@inheritDoc} */ - @Override - public EstimatedMeasurement getCorrectedMeasurement() { - return correctedMeasurement; - } - - /** {@inheritDoc} */ - @Override - public RealVector getPhysicalEstimatedState() { - // Method {@link ParameterDriver#getValue()} is used to get - // the physical values of the state. - // The scales'array is used to get the size of the state vector - final RealVector physicalEstimatedState = new ArrayRealVector(scale.length); - int i = 0; - for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); - } - for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); - } - for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); - } - - return physicalEstimatedState; - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalEstimatedCovarianceMatrix() { - // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it. - // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas - // For each element [i,j] of P the corresponding normalized value is: - // Pn[i,j] = P[i,j] / (scale[i]*scale[j]) - // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j] - - // Normalized covariance matrix - final RealMatrix normalizedP = correctedEstimate.getCovariance(); - - // Initialize physical covariance matrix - final int nbParams = normalizedP.getRowDimension(); - final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams); - - // Un-normalize the covairance matrix - for (int i = 0; i < nbParams; ++i) { - for (int j = 0; j < nbParams; ++j) { - physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]); - } - } - return physicalP; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedOrbitalParameters() { - return allEstimatedOrbitalParameters; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedPropagationParameters() { - return allEstimatedPropagationParameters; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedMeasurementsParameters() { - return estimatedMeasurementsParameters; - } - - /** Get the current corrected estimate. - * @return current corrected estimate - */ - public ProcessEstimate getEstimate() { - return correctedEstimate; - } - - /** Get the normalized error state transition matrix (STM) from previous point to current point. - * The STM contains the partial derivatives of current state with respect to previous state. - * The STM is an mxm matrix where m is the size of the state vector. - * m = nbOrb + nbPropag + nbMeas - * @return the normalized error state transition matrix - */ - private RealMatrix getErrorStateTransitionMatrix() { - - /* The state transition matrix is obtained as follows, with: - * - Y : Current state vector - * - Y0 : Initial state vector - * - Pp : Current propagation parameter - * - Pp0: Initial propagation parameter - * - Mp : Current measurement parameter - * - Mp0: Initial measurement parameter - * - * | | | | | | | . | - * | dY/dY0 | dY/dPp | dY/dMp | | dY/dY0 | dY/dPp | ..0.. | - * | | | | | | | . | - * |--------|---------|---------| |--------|--------|--------| - * | | | | | . | 1 0 0..| . | - * STM = | dP/dY0 | dP/dPp0 | dP/dMp | = | ..0.. | 0 1 0..| ..0.. | - * | | | | | . | 0 0 1..| . | - * |--------|---------|---------| |--------|--------|--------| - * | | | | | . | . | 1 0 0..| - * | dM/dY0 | dM/dPp0 | dM/dMp0 | | ..0.. | ..0.. | 0 1 0..| - * | | | | | . | . | 0 0 1..| - */ - - // Initialize to the proper size identity matrix - final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension()); - - // loop over all orbits - for (int k = 0; k < predictedSpacecraftStates.length; ++k) { - - // Indexes - final int[] indK = covarianceIndirection[k]; - - // Derivatives of the state vector with respect to initial state vector - final int nbOrbParams = estimatedOrbitalParameters[k].getNbParams(); - if (nbOrbParams > 0) { - - // Reset reference (for example compute short periodic terms in DSST) - harvesters[k].setReferenceState(predictedSpacecraftStates[k]); - - final RealMatrix dYdY0 = harvesters[k].getStateTransitionMatrix(predictedSpacecraftStates[k]); - - // Fill upper left corner (dY/dY0) - for (int i = 0; i < dYdY0.getRowDimension(); ++i) { - for (int j = 0; j < nbOrbParams; ++j) { - stm.setEntry(indK[i], indK[j], dYdY0.getEntry(i, j)); - } - } - } - - // Derivatives of the state vector with respect to propagation parameters - final int nbParams = estimatedPropagationParameters[k].getNbParams(); - if (nbParams > 0) { - final RealMatrix dYdPp = harvesters[k].getParametersJacobian(predictedSpacecraftStates[k]); - - // Fill 1st row, 2nd column (dY/dPp) - for (int i = 0; i < dYdPp.getRowDimension(); ++i) { - for (int j = 0; j < nbParams; ++j) { - stm.setEntry(indK[i], indK[j + 6], dYdPp.getEntry(i, j)); - } - } - - } - - } - - // Normalization of the STM - // normalized(STM)ij = STMij*Sj/Si - for (int i = 0; i < scale.length; i++) { - for (int j = 0; j < scale.length; j++ ) { - stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]); - } - } - - // Return the error state transition matrix - return stm; - - } - - /** Get the normalized measurement matrix H. - * H contains the partial derivatives of the measurement with respect to the state. - * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector. - * @return the normalized measurement matrix H - */ - private RealMatrix getMeasurementMatrix() { - - // Observed measurement characteristics - final SpacecraftState[] evaluationStates = predictedMeasurement.getStates(); - final ObservedMeasurement observedMeasurement = predictedMeasurement.getObservedMeasurement(); - final double[] sigma = observedMeasurement.getTheoreticalStandardDeviation(); - - // Initialize measurement matrix H: nxm - // n: Number of measurements in current measurement - // m: State vector size - final RealMatrix measurementMatrix = MatrixUtils. - createRealMatrix(observedMeasurement.getDimension(), - correctedEstimate.getState().getDimension()); - - // loop over all orbits involved in the measurement - for (int k = 0; k < evaluationStates.length; ++k) { - final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex(); - - // Predicted orbit - final Orbit predictedOrbit = evaluationStates[k].getOrbit(); - - // Measurement matrix's columns related to orbital parameters - // ---------------------------------------------------------- - - // Partial derivatives of the current Cartesian coordinates with respect to current orbital state - final double[][] aCY = new double[6][6]; - predictedOrbit.getJacobianWrtParameters(builders.get(p).getPositionAngle(), aCY); //dC/dY - final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false); - - // Jacobian of the measurement with respect to current Cartesian coordinates - final RealMatrix dMdC = new Array2DRowRealMatrix(predictedMeasurement.getStateDerivatives(k), false); - - // Jacobian of the measurement with respect to current orbital state - final RealMatrix dMdY = dMdC.multiply(dCdY); - - // Fill the normalized measurement matrix's columns related to estimated orbital parameters - for (int i = 0; i < dMdY.getRowDimension(); ++i) { - int jOrb = orbitsStartColumns[p]; - for (int j = 0; j < dMdY.getColumnDimension(); ++j) { - final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j); - if (driver.isSelected()) { - measurementMatrix.setEntry(i, jOrb++, - dMdY.getEntry(i, j) / sigma[i] * driver.getScale()); - } - } - } - - // Normalized measurement matrix's columns related to propagation parameters - // -------------------------------------------------------------- - - // Jacobian of the measurement with respect to propagation parameters - final int nbParams = estimatedPropagationParameters[p].getNbParams(); - if (nbParams > 0) { - final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]); - final RealMatrix dMdPp = dMdY.multiply(dYdPp); - for (int i = 0; i < dMdPp.getRowDimension(); ++i) { - for (int j = 0; j < nbParams; ++j) { - final ParameterDriver delegating = estimatedPropagationParameters[p].getDrivers().get(j); - measurementMatrix.setEntry(i, propagationParameterColumns.get(delegating.getName()), - dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale()); - } - } - } - - // Normalized measurement matrix's columns related to measurement parameters - // -------------------------------------------------------------- - - // Jacobian of the measurement with respect to measurement parameters - // Gather the measurement parameters linked to current measurement - for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { - if (driver.isSelected()) { - // Derivatives of current measurement w/r to selected measurement parameter - final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver); - - // Check that the measurement parameter is managed by the filter - if (measurementParameterColumns.get(driver.getName()) != null) { - // Column of the driver in the measurement matrix - final int driverColumn = measurementParameterColumns.get(driver.getName()); - - // Fill the corresponding indexes of the measurement matrix - for (int i = 0; i < aMPm.length; ++i) { - measurementMatrix.setEntry(i, driverColumn, - aMPm[i] / sigma[i] * driver.getScale()); - } - } - } - } - } - - // Return the normalized measurement matrix - return measurementMatrix; - - } - - /** Normalize a covariance matrix. - * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas - * For each element [i,j] of P the corresponding normalized value is: - * Pn[i,j] = P[i,j] / (scale[i]*scale[j]) - * @param physicalCovarianceMatrix The "physical" covariance matrix in input - * @return the normalized covariance matrix - */ - private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) { - - // Initialize output matrix - final int nbParams = physicalCovarianceMatrix.getRowDimension(); - final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams); - - // Normalize the state matrix - for (int i = 0; i < nbParams; ++i) { - for (int j = 0; j < nbParams; ++j) { - normalizedCovarianceMatrix.setEntry(i, j, - physicalCovarianceMatrix.getEntry(i, j) / - (scale[i] * scale[j])); - } - } - return normalizedCovarianceMatrix; - } - - /** {@inheritDoc} */ - @Override - public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState, - final MeasurementDecorator measurement) { - - // Set a reference date for all measurements parameters that lack one (including the not estimated ones) - final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); - for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(builders.get(0).getInitialOrbitDate()); - } - } - - ++currentMeasurementNumber; - currentDate = measurement.getObservedMeasurement().getDate(); - - // Note: - // - n = size of the current measurement - // Example: - // * 1 for Range, RangeRate and TurnAroundRange - // * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination) - // * 6 for Position/Velocity - // - m = size of the state vector. n = nbOrb + nbPropag + nbMeas - - // Predict the state vector (mx1) - final RealVector predictedState = predictState(observedMeasurement.getDate()); - - // Get the error state transition matrix (mxm) - final RealMatrix stateTransitionMatrix = getErrorStateTransitionMatrix(); - - // Predict the measurement based on predicted spacecraft state - // Compute the innovations (i.e. residuals of the predicted measurement) - // ------------------------------------------------------------ - - // Predicted measurement - // Note: here the "iteration/evaluation" formalism from the batch LS method - // is twisted to fit the need of the Kalman filter. - // The number of "iterations" is actually the number of measurements processed by the filter - // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement. - predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(observedMeasurement, predictedSpacecraftStates)); - - // Normalized measurement matrix (nxm) - final RealMatrix measurementMatrix = getMeasurementMatrix(); - - // compute process noise matrix - final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(), - previousState.getDimension()); - for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { - - // Number of estimated measurement parameters - final int nbMeas = estimatedMeasurementsParameters.getNbParams(); - - // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + - estimatedPropagationParameters[k].getNbParams(); - - // Covariance matrix - final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); - if (nbDyn > 0) { - final RealMatrix noiseP = covarianceMatricesProviders.get(k). - getProcessNoiseMatrix(correctedSpacecraftStates[k], - predictedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseP.getData(), 0, 0); - } - if (measurementProcessNoiseMatrix != null) { - final RealMatrix noiseM = measurementProcessNoiseMatrix. - getProcessNoiseMatrix(correctedSpacecraftStates[k], - predictedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); - } - - KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), - builders.get(k).getOrbitalParametersDrivers(), - builders.get(k).getPropagationParametersDrivers(), - estimatedMeasurementsParameters); - - final int[] indK = covarianceIndirection[k]; - for (int i = 0; i < indK.length; ++i) { - if (indK[i] >= 0) { - for (int j = 0; j < indK.length; ++j) { - if (indK[j] >= 0) { - physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); - } - } - } - } - - } - final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(physicalProcessNoise); - - return new NonLinearEvolution(measurement.getTime(), predictedState, - stateTransitionMatrix, normalizedProcessNoise, measurementMatrix); - - } - - - /** {@inheritDoc} */ - @Override - public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution, - final RealMatrix innovationCovarianceMatrix) { - - // Apply the dynamic outlier filter, if it exists - KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix); - // Compute the innovation vector - return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement, predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); - } - - /** Finalize estimation. - * @param observedMeasurement measurement that has just been processed - * @param estimate corrected estimate - */ - public void finalizeEstimation(final ObservedMeasurement observedMeasurement, - final ProcessEstimate estimate) { - // Update the parameters with the estimated state - // The min/max values of the parameters are handled by the ParameterDriver implementation - correctedEstimate = estimate; - updateParameters(); - - // Get the estimated propagator (mirroring parameter update in the builder) - // and the estimated spacecraft state - final Propagator[] estimatedPropagators = getEstimatedPropagators(); - for (int k = 0; k < estimatedPropagators.length; ++k) { - correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState(); - } - - // Compute the estimated measurement using estimated spacecraft state - correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(observedMeasurement, correctedSpacecraftStates)); - // Update the trajectory - // --------------------- - updateReferenceTrajectories(estimatedPropagators, propagationType, stateType); - - } - - /** Set the predicted normalized state vector. - * The predicted/propagated orbit is used to update the state vector - * @param date prediction date - * @return predicted state - */ - private RealVector predictState(final AbsoluteDate date) { - - // Predicted state is initialized to previous estimated state - final RealVector predictedState = correctedEstimate.getState().copy(); - - // Orbital parameters counter - int jOrb = 0; - - for (int k = 0; k < predictedSpacecraftStates.length; ++k) { - - // Propagate the reference trajectory to measurement date - predictedSpacecraftStates[k] = referenceTrajectories[k].propagate(date); - - // Update the builder with the predicted orbit - // This updates the orbital drivers with the values of the predicted orbit - builders.get(k).resetOrbit(predictedSpacecraftStates[k].getOrbit()); - - // The orbital parameters in the state vector are replaced with their predicted values - // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation) - // As the propagator builder was previously updated with the predicted orbit, - // the selected orbital drivers are already up to date with the prediction - for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { - if (orbitalDriver.isSelected()) { - predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue()); - } - } - - } - - return predictedState; - - } - - /** Update the estimated parameters after the correction phase of the filter. - * The min/max allowed values are handled by the parameter themselves. - */ - private void updateParameters() { - final RealVector correctedState = correctedEstimate.getState(); - int i = 0; - for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { - // let the parameter handle min/max clipping - driver.setNormalizedValue(correctedState.getEntry(i)); - correctedState.setEntry(i++, driver.getNormalizedValue()); - } - for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { - // let the parameter handle min/max clipping - driver.setNormalizedValue(correctedState.getEntry(i)); - correctedState.setEntry(i++, driver.getNormalizedValue()); - } - for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { - // let the parameter handle min/max clipping - driver.setNormalizedValue(correctedState.getEntry(i)); - correctedState.setEntry(i++, driver.getNormalizedValue()); - } - } - - /** Getter for the propagators. - * @return the propagators - */ - public List getBuilders() { - return builders; - } - - /** Getter for the reference trajectories. - * @return the referencetrajectories - */ - public Propagator[] getReferenceTrajectories() { - return referenceTrajectories.clone(); - } - - /** Setter for the reference trajectories. - * @param referenceTrajectories the reference trajectories to be setted - */ - public void setReferenceTrajectories(final Propagator[] referenceTrajectories) { - this.referenceTrajectories = referenceTrajectories.clone(); - } - - /** Getter for the jacobian mappers. - * @return the jacobian mappers - * @deprecated as of 11.1, not used anymore - */ - @Deprecated - public AbstractJacobiansMapper[] getMappers() { - return null; - } - - /** Setter for the jacobian mappers. - * @param mappers the jacobian mappers to set - * @deprecated as of 11.1, replaced by {@link #setHarvesters(MatricesHarvester[])} - */ - @Deprecated - public void setMappers(final AbstractJacobiansMapper[] mappers) { - setHarvesters(mappers); - } - - /** Setter for the jacobian harvesters. - * @param harvesters the jacobian harvesters to set - * @since 11.1 - */ - public void setHarvesters(final MatricesHarvester[] harvesters) { - this.harvesters = harvesters.clone(); - } - - /** Get the propagators estimated with the values set in the propagators builders. - * @return propagators based on the current values in the builder - */ - public Propagator[] getEstimatedPropagators() { - // Return propagators built with current instantiation of the propagator builders - final Propagator[] propagators = new Propagator[getBuilders().size()]; - for (int k = 0; k < getBuilders().size(); ++k) { - propagators[k] = getBuilders().get(k).buildPropagator(getBuilders().get(k).getSelectedNormalizedParameters()); - } - return propagators; - } - -} diff --git a/src/main/java/org/orekit/estimation/sequential/ConstantProcessNoise.java b/src/main/java/org/orekit/estimation/sequential/ConstantProcessNoise.java index abaab75bad..f75af6143b 100644 --- a/src/main/java/org/orekit/estimation/sequential/ConstantProcessNoise.java +++ b/src/main/java/org/orekit/estimation/sequential/ConstantProcessNoise.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/CovarianceMatrixProvider.java b/src/main/java/org/orekit/estimation/sequential/CovarianceMatrixProvider.java index b0de233b33..c08b5acfad 100644 --- a/src/main/java/org/orekit/estimation/sequential/CovarianceMatrixProvider.java +++ b/src/main/java/org/orekit/estimation/sequential/CovarianceMatrixProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/DSSTKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/DSSTKalmanModel.java deleted file mode 100644 index 7a1b09f39f..0000000000 --- a/src/main/java/org/orekit/estimation/sequential/DSSTKalmanModel.java +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.sequential; - -import java.util.List; - -import org.orekit.propagation.MatricesHarvester; -import org.orekit.propagation.PropagationType; -import org.orekit.propagation.Propagator; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.propagation.semianalytical.dsst.DSSTJacobiansMapper; -import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; -import org.orekit.utils.ParameterDriversList; - -/** Class defining the process model dynamics to use with a {@link KalmanEstimator}. - *

    - * This class is an adaption of the {@link KalmanModel} class - * but for the {@link DSSTPropagator DSST propagator}. - *

    - * @author Romain Gerbaud - * @author Maxime Journot - * @author Bryan Cazabonne - * @since 10.0 - * @deprecated as of 11.1, replaced by {@link SemiAnalyticalKalmanModel} - */ -@Deprecated -public class DSSTKalmanModel extends AbstractKalmanModel { - - /** Kalman process model constructor. - * @param propagatorBuilders propagators builders used to evaluate the orbits. - * @param covarianceMatricesProviders providers for covariance matrices - * @param estimatedMeasurementParameters measurement parameters to estimate - * @param measurementProcessNoiseMatrix provider for measurement process noise matrix - * @param propagationType type of the orbit used for the propagation (mean or osculating) - * @param stateType type of the elements used to define the orbital state (mean or osculating) - */ - public DSSTKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix, - final PropagationType propagationType, - final PropagationType stateType) { - // call super constructor - super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, - measurementProcessNoiseMatrix, new DSSTJacobiansMapper[propagatorBuilders.size()], - propagationType, stateType); - } - - /** {@inheritDoc} */ - @Override - protected void updateReferenceTrajectories(final Propagator[] propagators, - final PropagationType pType, - final PropagationType sType) { - - // Update the reference trajectory propagator - setReferenceTrajectories(propagators); - - // Jacobian mappers - final MatricesHarvester[] harvesters = new MatricesHarvester[propagators.length]; - - for (int k = 0; k < propagators.length; ++k) { - // Link the partial derivatives to this new propagator - final String equationName = KalmanEstimator.class.getName() + "-derivatives-" + k; - harvesters[k] = ((DSSTPropagator) getReferenceTrajectories()[k]).setupMatricesComputation(equationName, null, null); - } - - // Update Jacobian harvesters - setHarvesters(harvesters); - - } - -} diff --git a/src/main/java/org/orekit/estimation/sequential/EskfMeasurementHandler.java b/src/main/java/org/orekit/estimation/sequential/EskfMeasurementHandler.java deleted file mode 100644 index c5dc0089d5..0000000000 --- a/src/main/java/org/orekit/estimation/sequential/EskfMeasurementHandler.java +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.sequential; - -import java.util.List; - -import org.hipparchus.exception.MathRuntimeException; -import org.hipparchus.filtering.kalman.ProcessEstimate; -import org.hipparchus.filtering.kalman.extended.ExtendedKalmanFilter; -import org.orekit.errors.OrekitException; -import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.sampling.OrekitStepHandler; -import org.orekit.propagation.sampling.OrekitStepInterpolator; -import org.orekit.time.AbsoluteDate; - -/** {@link org.orekit.propagation.sampling.OrekitStepHandler Step handler} picking up - * {@link ObservedMeasurement measurements} for the {@link SemiAnalyticalKalmanEstimator}. - * @author Julie Bayard - * @author Bryan Cazabonne - * @author Maxime Journot - * @since 11.1 - */ -@Deprecated -public class EskfMeasurementHandler implements OrekitStepHandler { - - /** ESKF model. */ - private final SemiAnalyticalKalmanModel model; - - /** Extended Kalman Filter. */ - private final ExtendedKalmanFilter filter; - - /** Underlying measurements. */ - private final List> observedMeasurements; - - /** Index of the next measurement component in the model. */ - private int index; - - /** Reference date. */ - private AbsoluteDate referenceDate; - - /** Observer to retrieve current estimation info. */ - private KalmanObserver observer; - - /** Simple constructor. - * @param model semi-analytical kalman model - * @param filter kalman filter instance - * @param observedMeasurements list of observed measurements - * @param referenceDate reference date - */ - public EskfMeasurementHandler(final SemiAnalyticalKalmanModel model, - final ExtendedKalmanFilter filter, - final List> observedMeasurements, - final AbsoluteDate referenceDate) { - this.model = model; - this.filter = filter; - this.observer = model.getObserver(); - this.observedMeasurements = observedMeasurements; - this.referenceDate = referenceDate; - } - - /** {@inheritDoc} */ - @Override - public void init(final SpacecraftState s0, final AbsoluteDate t) { - this.index = 0; - // Initialize short periodic terms. - model.initializeShortPeriodicTerms(s0); - model.updateShortPeriods(s0); - } - - /** {@inheritDoc} */ - @Override - public void handleStep(final OrekitStepInterpolator interpolator) { - - // Current date - final AbsoluteDate currentDate = interpolator.getCurrentState().getDate(); - - // Update the short period terms with the current MEAN state - model.updateShortPeriods(interpolator.getCurrentState()); - - // Process the measurements between previous step and current step - while (index < observedMeasurements.size() && observedMeasurements.get(index).getDate().compareTo(currentDate) < 0) { - - try { - - // Update the norminal state with the interpolated parameters - model.updateNominalSpacecraftState(interpolator.getInterpolatedState(observedMeasurements.get(index).getDate())); - - // Process the current observation - final ProcessEstimate estimate = filter.estimationStep(KalmanEstimatorUtil.decorate(observedMeasurements.get(index), referenceDate)); - - // Finalize the estimation - model.finalizeEstimation(observedMeasurements.get(index), estimate); - - // Call the observer if the user add one - if (observer != null) { - observer.evaluationPerformed(model); - } - - } catch (MathRuntimeException mrte) { - throw new OrekitException(mrte); - } - - // Increment the measurement index - index += 1; - - } - - // Reset the initial state of the propagator - model.finalizeOperationsObservationGrid(); - - } - -} diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanEstimation.java b/src/main/java/org/orekit/estimation/sequential/KalmanEstimation.java index c36a978eeb..8781a658bb 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanEstimation.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanEstimation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanEstimator.java b/src/main/java/org/orekit/estimation/sequential/KalmanEstimator.java index d643ed3d08..b79a36c937 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanEstimator.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,7 +25,12 @@ import org.orekit.errors.OrekitException; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.Propagator; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; +import org.orekit.propagation.analytical.BrouwerLyddanePropagator; +import org.orekit.propagation.analytical.EcksteinHechlerPropagator; +import org.orekit.propagation.analytical.Ephemeris; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; import org.orekit.time.AbsoluteDate; @@ -36,8 +41,14 @@ /** * Implementation of a Kalman filter to perform orbit determination. *

    - * The filter uses a {@link OrbitDeterminationPropagatorBuilder} to initialize its reference trajectory {@link NumericalPropagator} - * or {@link DSSTPropagator}. + * The filter uses a {@link PropagatorBuilder} to initialize its reference trajectory. + * The Kalman estimator can be used with a {@link NumericalPropagator}, {@link TLEPropagator}, + * {@link BrouwerLyddanePropagator}, {@link EcksteinHechlerPropagator}, {@link KeplerianPropagator}, + * or {@link Ephemeris}. + *

    + *

    + * Kalman estimation using a {@link DSSTPropagator semi-analytical orbit propagator} must be done using + * the {@link SemiAnalyticalKalmanEstimator}. *

    *

    * The estimated parameters are driven by {@link ParameterDriver} objects. They are of 3 different types:

      @@ -71,7 +82,7 @@ public class KalmanEstimator extends AbstractKalmanEstimator { private final AbsoluteDate referenceDate; /** Kalman filter process model. */ - private final AbstractKalmanModel processModel; + private final KalmanModel processModel; /** Filter. */ private final ExtendedKalmanFilter filter; @@ -88,7 +99,7 @@ public class KalmanEstimator extends AbstractKalmanEstimator { * @since 10.3 */ KalmanEstimator(final MatrixDecomposer decomposer, - final List propagatorBuilders, + final List propagatorBuilders, final List processNoiseMatricesProviders, final ParameterDriversList estimatedMeasurementParameters, final CovarianceMatrixProvider measurementProcessNoiseMatrix) { @@ -97,10 +108,10 @@ public class KalmanEstimator extends AbstractKalmanEstimator { this.observer = null; // Build the process model and measurement model - this.processModel = propagatorBuilders.get(0).buildKalmanModel(propagatorBuilders, - processNoiseMatricesProviders, - estimatedMeasurementParameters, - measurementProcessNoiseMatrix); + this.processModel = new KalmanModel(propagatorBuilders, + processNoiseMatricesProviders, + estimatedMeasurementParameters, + measurementProcessNoiseMatrix); this.filter = new ExtendedKalmanFilter<>(decomposer, processModel, processModel.getEstimate()); diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorBuilder.java b/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorBuilder.java index 546600dc3a..8ec6860abc 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorBuilder.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.propagation.conversion.EphemerisPropagatorBuilder; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.utils.ParameterDriversList; /** Builder for a Kalman filter estimator. @@ -38,7 +38,7 @@ public class KalmanEstimatorBuilder { private MatrixDecomposer decomposer; /** Builders for propagators. */ - private List propagatorBuilders; + private List propagatorBuilders; /** Estimated measurements parameters. */ private ParameterDriversList estimatedMeasurementsParameters; @@ -62,7 +62,7 @@ public KalmanEstimatorBuilder() { /** Construct a {@link KalmanEstimator} from the data in this builder. *

      - * Before this method is called, {@link #addPropagationConfiguration(OrbitDeterminationPropagatorBuilder, + * Before this method is called, {@link #addPropagationConfiguration(PropagatorBuilder, * CovarianceMatrixProvider) addPropagationConfiguration()} must have been called * at least once, otherwise configuration is incomplete and an exception will be raised. *

      @@ -112,7 +112,7 @@ public KalmanEstimatorBuilder decomposer(final MatrixDecomposer matrixDecomposer * @see CovarianceMatrixProvider#getProcessNoiseMatrix(org.orekit.propagation.SpacecraftState, * org.orekit.propagation.SpacecraftState) getProcessNoiseMatrix(previous, current) */ - public KalmanEstimatorBuilder addPropagationConfiguration(final OrbitDeterminationPropagatorBuilder builder, + public KalmanEstimatorBuilder addPropagationConfiguration(final PropagatorBuilder builder, final CovarianceMatrixProvider provider) { propagatorBuilders.add(builder); processNoiseMatricesProviders.add(provider); diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorUtil.java b/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorUtil.java index d129236d85..cfb339657f 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorUtil.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanEstimatorUtil.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -292,4 +292,138 @@ public static RealVector computeInnovationVector(final EstimatedMeasurement p } + /** + * Normalize a covariance matrix. + * @param physicalP "physical" covariance matrix in input + * @param parameterScales scale factor of estimated parameters + * @return the normalized covariance matrix + */ + public static RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalP, + final double[] parameterScales) { + + // Initialize output matrix + final int nbParams = physicalP.getRowDimension(); + final RealMatrix normalizedP = MatrixUtils.createRealMatrix(nbParams, nbParams); + + // Normalize the state matrix + for (int i = 0; i < nbParams; ++i) { + for (int j = 0; j < nbParams; ++j) { + normalizedP.setEntry(i, j, physicalP.getEntry(i, j) / (parameterScales[i] * parameterScales[j])); + } + } + return normalizedP; + } + + /** + * Un-nomalized the covariance matrix. + * @param normalizedP normalized covariance matrix + * @param parameterScales scale factor of estimated parameters + * @return the un-normalized covariance matrix + */ + public static RealMatrix unnormalizeCovarianceMatrix(final RealMatrix normalizedP, + final double[] parameterScales) { + // Initialize physical covariance matrix + final int nbParams = normalizedP.getRowDimension(); + final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams); + + // Un-normalize the covairance matrix + for (int i = 0; i < nbParams; ++i) { + for (int j = 0; j < nbParams; ++j) { + physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * parameterScales[i] * parameterScales[j]); + } + } + return physicalP; + } + + /** + * Un-nomalized the state transition matrix. + * @param normalizedSTM normalized state transition matrix + * @param parameterScales scale factor of estimated parameters + * @return the un-normalized state transition matrix + */ + public static RealMatrix unnormalizeStateTransitionMatrix(final RealMatrix normalizedSTM, + final double[] parameterScales) { + // Initialize physical matrix + final int nbParams = normalizedSTM.getRowDimension(); + final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams); + + // Un-normalize the matrix + for (int i = 0; i < nbParams; ++i) { + for (int j = 0; j < nbParams; ++j) { + physicalSTM.setEntry(i, j, + normalizedSTM.getEntry(i, j) * parameterScales[i] / parameterScales[j]); + } + } + return physicalSTM; + } + + /** + * Un-normalize the measurement matrix. + * @param normalizedH normalized measurement matrix + * @param parameterScales scale factor of estimated parameters + * @param sigmas measurement theoretical standard deviation + * @return the un-normalized measurement matrix + */ + public static RealMatrix unnormalizeMeasurementJacobian(final RealMatrix normalizedH, + final double[] parameterScales, + final double[] sigmas) { + // Initialize physical matrix + final int nbLine = normalizedH.getRowDimension(); + final int nbCol = normalizedH.getColumnDimension(); + final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol); + + // Un-normalize the matrix + for (int i = 0; i < nbLine; ++i) { + for (int j = 0; j < nbCol; ++j) { + physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / parameterScales[j]); + } + } + return physicalH; + } + + /** + * Un-normalize the innovation covariance matrix. + * @param normalizedS normalized innovation covariance matrix + * @param sigmas measurement theoretical standard deviation + * @return the un-normalized innovation covariance matrix + */ + public static RealMatrix unnormalizeInnovationCovarianceMatrix(final RealMatrix normalizedS, + final double[] sigmas) { + // Initialize physical matrix + final int nbMeas = sigmas.length; + final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas); + + // Un-normalize the matrix + for (int i = 0; i < nbMeas; ++i) { + for (int j = 0; j < nbMeas; ++j) { + physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] * sigmas[j]); + } + } + return physicalS; + } + + /** + * Un-normalize the Kalman gain matrix. + * @param normalizedK normalized Kalman gain matrix + * @param parameterScales scale factor of estimated parameters + * @param sigmas measurement theoretical standard deviation + * @return the un-normalized Kalman gain matrix + */ + public static RealMatrix unnormalizeKalmanGainMatrix(final RealMatrix normalizedK, + final double[] parameterScales, + final double[] sigmas) { + // Initialize physical matrix + final int nbLine = normalizedK.getRowDimension(); + final int nbCol = normalizedK.getColumnDimension(); + final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol); + + // Un-normalize the matrix + for (int i = 0; i < nbLine; ++i) { + for (int j = 0; j < nbCol; ++j) { + physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * parameterScales[i] / sigmas[j]); + } + } + return physicalK; + } + } diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanModel.java b/src/main/java/org/orekit/estimation/sequential/KalmanModel.java index 0f85e11401..0460794798 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,21 +16,113 @@ */ package org.orekit.estimation.sequential; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import org.hipparchus.filtering.kalman.ProcessEstimate; +import org.hipparchus.filtering.kalman.extended.NonLinearEvolution; +import org.hipparchus.filtering.kalman.extended.NonLinearProcess; +import org.hipparchus.linear.Array2DRowRealMatrix; +import org.hipparchus.linear.ArrayRealVector; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.linear.RealVector; +import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.orbits.Orbit; import org.orekit.propagation.MatricesHarvester; -import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.propagation.numerical.JacobiansMapper; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.PropagatorBuilder; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; +import org.orekit.utils.ParameterDriversList.DelegatingDriver; /** Class defining the process model dynamics to use with a {@link KalmanEstimator}. * @author Romain Gerbaud * @author Maxime Journot * @since 9.2 */ -public class KalmanModel extends AbstractKalmanModel { +public class KalmanModel implements KalmanEstimation, NonLinearProcess { + + /** Builders for propagators. */ + private final List builders; + + /** Estimated orbital parameters. */ + private final ParameterDriversList allEstimatedOrbitalParameters; + + /** Estimated propagation drivers. */ + private final ParameterDriversList allEstimatedPropagationParameters; + + /** Per-builder estimated orbita parameters drivers. + * @since 11.1 + */ + private final ParameterDriversList[] estimatedOrbitalParameters; + + /** Per-builder estimated propagation drivers. */ + private final ParameterDriversList[] estimatedPropagationParameters; + + /** Estimated measurements parameters. */ + private final ParameterDriversList estimatedMeasurementsParameters; + + /** Start columns for each estimated orbit. */ + private final int[] orbitsStartColumns; + + /** End columns for each estimated orbit. */ + private final int[] orbitsEndColumns; + + /** Map for propagation parameters columns. */ + private final Map propagationParameterColumns; + + /** Map for measurements parameters columns. */ + private final Map measurementParameterColumns; + + /** Providers for covariance matrices. */ + private final List covarianceMatricesProviders; + + /** Process noise matrix provider for measurement parameters. */ + private final CovarianceMatrixProvider measurementProcessNoiseMatrix; + + /** Indirection arrays to extract the noise components for estimated parameters. */ + private final int[][] covarianceIndirection; + + /** Scaling factors. */ + private final double[] scale; + + /** Harvesters for extracting Jacobians from integrated states. */ + private MatricesHarvester[] harvesters; + + /** Propagators for the reference trajectories, up to current date. */ + private Propagator[] referenceTrajectories; + + /** Current corrected estimate. */ + private ProcessEstimate correctedEstimate; + + /** Current number of measurement. */ + private int currentMeasurementNumber; + + /** Reference date. */ + private AbsoluteDate referenceDate; + + /** Current date. */ + private AbsoluteDate currentDate; + + /** Predicted spacecraft states. */ + private SpacecraftState[] predictedSpacecraftStates; + + /** Corrected spacecraft states. */ + private SpacecraftState[] correctedSpacecraftStates; + + /** Predicted measurement. */ + private EstimatedMeasurement predictedMeasurement; + + /** Corrected measurement. */ + private EstimatedMeasurement correctedMeasurement; /** Kalman process model constructor. * @param propagatorBuilders propagators builders used to evaluate the orbits. @@ -38,26 +130,206 @@ public class KalmanModel extends AbstractKalmanModel { * @param estimatedMeasurementParameters measurement parameters to estimate * @param measurementProcessNoiseMatrix provider for measurement process noise matrix */ - public KalmanModel(final List propagatorBuilders, + public KalmanModel(final List propagatorBuilders, final List covarianceMatricesProviders, final ParameterDriversList estimatedMeasurementParameters, final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - // call super constructor - super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, - measurementProcessNoiseMatrix, new JacobiansMapper[propagatorBuilders.size()]); + + this.builders = propagatorBuilders; + this.estimatedMeasurementsParameters = estimatedMeasurementParameters; + this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size()); + this.currentMeasurementNumber = 0; + this.referenceDate = propagatorBuilders.get(0).getInitialOrbitDate(); + this.currentDate = referenceDate; + + final Map orbitalParameterColumns = new HashMap<>(6 * builders.size()); + orbitsStartColumns = new int[builders.size()]; + orbitsEndColumns = new int[builders.size()]; + int columns = 0; + allEstimatedOrbitalParameters = new ParameterDriversList(); + estimatedOrbitalParameters = new ParameterDriversList[builders.size()]; + for (int k = 0; k < builders.size(); ++k) { + estimatedOrbitalParameters[k] = new ParameterDriversList(); + orbitsStartColumns[k] = columns; + final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null; + for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(currentDate); + } + if (suffix != null && !driver.getName().endsWith(suffix)) { + // we add suffix only conditionally because the method may already have been called + // and suffixes may have already been appended + driver.setName(driver.getName() + suffix); + } + if (driver.isSelected()) { + allEstimatedOrbitalParameters.add(driver); + estimatedOrbitalParameters[k].add(driver); + orbitalParameterColumns.put(driver.getName(), columns++); + } + } + orbitsEndColumns[k] = columns; + } + + // Gather all the propagation drivers names in a list + allEstimatedPropagationParameters = new ParameterDriversList(); + estimatedPropagationParameters = new ParameterDriversList[builders.size()]; + final List estimatedPropagationParametersNames = new ArrayList<>(); + for (int k = 0; k < builders.size(); ++k) { + estimatedPropagationParameters[k] = new ParameterDriversList(); + for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(currentDate); + } + if (driver.isSelected()) { + allEstimatedPropagationParameters.add(driver); + estimatedPropagationParameters[k].add(driver); + final String driverName = driver.getName(); + // Add the driver name if it has not been added yet + if (!estimatedPropagationParametersNames.contains(driverName)) { + estimatedPropagationParametersNames.add(driverName); + } + } + } + } + estimatedPropagationParametersNames.sort(Comparator.naturalOrder()); + + // Populate the map of propagation drivers' columns and update the total number of columns + propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size()); + for (final String driverName : estimatedPropagationParametersNames) { + propagationParameterColumns.put(driverName, columns); + ++columns; + } + + // Populate the map of measurement drivers' columns and update the total number of columns + for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { + if (parameter.getReferenceDate() == null) { + parameter.setReferenceDate(currentDate); + } + measurementParameterColumns.put(parameter.getName(), columns); + ++columns; + } + + // Store providers for process noise matrices + this.covarianceMatricesProviders = covarianceMatricesProviders; + this.measurementProcessNoiseMatrix = measurementProcessNoiseMatrix; + this.covarianceIndirection = new int[builders.size()][columns]; + for (int k = 0; k < covarianceIndirection.length; ++k) { + final ParameterDriversList orbitDrivers = builders.get(k).getOrbitalParametersDrivers(); + final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers(); + Arrays.fill(covarianceIndirection[k], -1); + int i = 0; + for (final ParameterDriver driver : orbitDrivers.getDrivers()) { + final Integer c = orbitalParameterColumns.get(driver.getName()); + if (c != null) { + covarianceIndirection[k][i++] = c.intValue(); + } + } + for (final ParameterDriver driver : parametersDrivers.getDrivers()) { + final Integer c = propagationParameterColumns.get(driver.getName()); + if (c != null) { + covarianceIndirection[k][i++] = c.intValue(); + } + } + for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) { + final Integer c = measurementParameterColumns.get(driver.getName()); + if (c != null) { + covarianceIndirection[k][i++] = c.intValue(); + } + } + } + + // Compute the scale factors + this.scale = new double[columns]; + int index = 0; + for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { + scale[index++] = driver.getScale(); + } + for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { + scale[index++] = driver.getScale(); + } + for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { + scale[index++] = driver.getScale(); + } + + // Build the reference propagators and add their partial derivatives equations implementation + updateReferenceTrajectories(getEstimatedPropagators()); + this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length]; + for (int i = 0; i < predictedSpacecraftStates.length; ++i) { + predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState(); + }; + this.correctedSpacecraftStates = predictedSpacecraftStates.clone(); + + // Initialize the estimated normalized state and fill its values + final RealVector correctedState = MatrixUtils.createRealVector(columns); + + int p = 0; + for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { + correctedState.setEntry(p++, driver.getNormalizedValue()); + } + for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { + correctedState.setEntry(p++, driver.getNormalizedValue()); + } + for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { + correctedState.setEntry(p++, driver.getNormalizedValue()); + } + + // Set up initial covariance + final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns); + for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { + + // Number of estimated measurement parameters + final int nbMeas = estimatedMeasurementParameters.getNbParams(); + + // Number of estimated dynamic parameters (orbital + propagation) + final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + + estimatedPropagationParameters[k].getNbParams(); + + // Covariance matrix + final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); + if (nbDyn > 0) { + final RealMatrix noiseP = covarianceMatricesProviders.get(k). + getInitialCovarianceMatrix(correctedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseP.getData(), 0, 0); + } + if (measurementProcessNoiseMatrix != null) { + final RealMatrix noiseM = measurementProcessNoiseMatrix. + getInitialCovarianceMatrix(correctedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); + } + + KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), + builders.get(k).getOrbitalParametersDrivers(), + builders.get(k).getPropagationParametersDrivers(), + estimatedMeasurementsParameters); + + final int[] indK = covarianceIndirection[k]; + for (int i = 0; i < indK.length; ++i) { + if (indK[i] >= 0) { + for (int j = 0; j < indK.length; ++j) { + if (indK[j] >= 0) { + physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); + } + } + } + } + + } + final RealMatrix correctedCovariance = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); + + correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance); + } - /** {@inheritDoc} */ - @Override - protected void updateReferenceTrajectories(final Propagator[] propagators, - final PropagationType pType, - final PropagationType sType) { + /** Update the reference trajectories using the propagators as input. + * @param propagators The new propagators to use + */ + protected void updateReferenceTrajectories(final Propagator[] propagators) { // Update the reference trajectory propagator setReferenceTrajectories(propagators); // Jacobian harvesters - final MatricesHarvester[] harvesters = new MatricesHarvester[propagators.length]; + harvesters = new MatricesHarvester[propagators.length]; for (int k = 0; k < propagators.length; ++k) { // Link the partial derivatives to this new propagator @@ -65,9 +337,567 @@ protected void updateReferenceTrajectories(final Propagator[] propagators, harvesters[k] = getReferenceTrajectories()[k].setupMatricesComputation(equationName, null, null); } - // Update Jacobian harvesters - setHarvesters(harvesters); + } + + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalStateTransitionMatrix() { + // Un-normalize the state transition matrix (φ) from Hipparchus and return it. + // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas + // For each element [i,j] of normalized φ (φn), the corresponding physical value is: + // φ[i,j] = φn[i,j] * scale[i] / scale[j] + return correctedEstimate.getStateTransitionMatrix() == null ? + null : KalmanEstimatorUtil.unnormalizeStateTransitionMatrix(correctedEstimate.getStateTransitionMatrix(), scale); + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalMeasurementJacobian() { + // Un-normalize the measurement matrix (H) from Hipparchus and return it. + // H is an nxm matrix where: + // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters + // - n is the size of the measurement being processed by the filter + // For each element [i,j] of normalized H (Hn) the corresponding physical value is: + // H[i,j] = Hn[i,j] * σ[i] / scale[j] + return correctedEstimate.getMeasurementJacobian() == null ? + null : KalmanEstimatorUtil.unnormalizeMeasurementJacobian(correctedEstimate.getMeasurementJacobian(), + scale, + correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalInnovationCovarianceMatrix() { + // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it. + // S is an nxn matrix where n is the size of the measurement being processed by the filter + // For each element [i,j] of normalized S (Sn) the corresponding physical value is: + // S[i,j] = Sn[i,j] * σ[i] * σ[j] + return correctedEstimate.getInnovationCovariance() == null ? + null : KalmanEstimatorUtil.unnormalizeInnovationCovarianceMatrix(correctedEstimate.getInnovationCovariance(), + predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalKalmanGain() { + // Un-normalize the Kalman gain (K) from Hipparchus and return it. + // K is an mxn matrix where: + // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters + // - n is the size of the measurement being processed by the filter + // For each element [i,j] of normalized K (Kn) the corresponding physical value is: + // K[i,j] = Kn[i,j] * scale[i] / σ[j] + return correctedEstimate.getKalmanGain() == null ? + null : KalmanEstimatorUtil.unnormalizeKalmanGainMatrix(correctedEstimate.getKalmanGain(), + scale, + correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState[] getPredictedSpacecraftStates() { + return predictedSpacecraftStates.clone(); + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState[] getCorrectedSpacecraftStates() { + return correctedSpacecraftStates.clone(); + } + + /** {@inheritDoc} */ + @Override + public int getCurrentMeasurementNumber() { + return currentMeasurementNumber; + } + /** {@inheritDoc} */ + @Override + public AbsoluteDate getCurrentDate() { + return currentDate; + } + + /** {@inheritDoc} */ + @Override + public EstimatedMeasurement getPredictedMeasurement() { + return predictedMeasurement; + } + + /** {@inheritDoc} */ + @Override + public EstimatedMeasurement getCorrectedMeasurement() { + return correctedMeasurement; + } + + /** {@inheritDoc} */ + @Override + public RealVector getPhysicalEstimatedState() { + // Method {@link ParameterDriver#getValue()} is used to get + // the physical values of the state. + // The scales'array is used to get the size of the state vector + final RealVector physicalEstimatedState = new ArrayRealVector(scale.length); + int i = 0; + for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { + physicalEstimatedState.setEntry(i++, driver.getValue()); + } + for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { + physicalEstimatedState.setEntry(i++, driver.getValue()); + } + for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { + physicalEstimatedState.setEntry(i++, driver.getValue()); + } + + return physicalEstimatedState; + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalEstimatedCovarianceMatrix() { + // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it. + // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas + // For each element [i,j] of P the corresponding normalized value is: + // Pn[i,j] = P[i,j] / (scale[i]*scale[j]) + // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j] + return KalmanEstimatorUtil.unnormalizeCovarianceMatrix(correctedEstimate.getCovariance(), scale); + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getEstimatedOrbitalParameters() { + return allEstimatedOrbitalParameters; + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getEstimatedPropagationParameters() { + return allEstimatedPropagationParameters; + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getEstimatedMeasurementsParameters() { + return estimatedMeasurementsParameters; + } + + /** Get the current corrected estimate. + * @return current corrected estimate + */ + public ProcessEstimate getEstimate() { + return correctedEstimate; + } + + /** Get the normalized error state transition matrix (STM) from previous point to current point. + * The STM contains the partial derivatives of current state with respect to previous state. + * The STM is an mxm matrix where m is the size of the state vector. + * m = nbOrb + nbPropag + nbMeas + * @return the normalized error state transition matrix + */ + private RealMatrix getErrorStateTransitionMatrix() { + + /* The state transition matrix is obtained as follows, with: + * - Y : Current state vector + * - Y0 : Initial state vector + * - Pp : Current propagation parameter + * - Pp0: Initial propagation parameter + * - Mp : Current measurement parameter + * - Mp0: Initial measurement parameter + * + * | | | | | | | . | + * | dY/dY0 | dY/dPp | dY/dMp | | dY/dY0 | dY/dPp | ..0.. | + * | | | | | | | . | + * |--------|---------|---------| |--------|--------|--------| + * | | | | | . | 1 0 0..| . | + * STM = | dP/dY0 | dP/dPp0 | dP/dMp | = | ..0.. | 0 1 0..| ..0.. | + * | | | | | . | 0 0 1..| . | + * |--------|---------|---------| |--------|--------|--------| + * | | | | | . | . | 1 0 0..| + * | dM/dY0 | dM/dPp0 | dM/dMp0 | | ..0.. | ..0.. | 0 1 0..| + * | | | | | . | . | 0 0 1..| + */ + + // Initialize to the proper size identity matrix + final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension()); + + // loop over all orbits + for (int k = 0; k < predictedSpacecraftStates.length; ++k) { + + // Indexes + final int[] indK = covarianceIndirection[k]; + + // Derivatives of the state vector with respect to initial state vector + final int nbOrbParams = estimatedOrbitalParameters[k].getNbParams(); + if (nbOrbParams > 0) { + + // Reset reference (for example compute short periodic terms in DSST) + harvesters[k].setReferenceState(predictedSpacecraftStates[k]); + + final RealMatrix dYdY0 = harvesters[k].getStateTransitionMatrix(predictedSpacecraftStates[k]); + + // Fill upper left corner (dY/dY0) + for (int i = 0; i < dYdY0.getRowDimension(); ++i) { + for (int j = 0; j < nbOrbParams; ++j) { + stm.setEntry(indK[i], indK[j], dYdY0.getEntry(i, j)); + } + } + } + + // Derivatives of the state vector with respect to propagation parameters + final int nbParams = estimatedPropagationParameters[k].getNbParams(); + if (nbParams > 0) { + final RealMatrix dYdPp = harvesters[k].getParametersJacobian(predictedSpacecraftStates[k]); + + // Fill 1st row, 2nd column (dY/dPp) + for (int i = 0; i < dYdPp.getRowDimension(); ++i) { + for (int j = 0; j < nbParams; ++j) { + stm.setEntry(indK[i], indK[j + 6], dYdPp.getEntry(i, j)); + } + } + + } + + } + + // Normalization of the STM + // normalized(STM)ij = STMij*Sj/Si + for (int i = 0; i < scale.length; i++) { + for (int j = 0; j < scale.length; j++ ) { + stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]); + } + } + + // Return the error state transition matrix + return stm; + + } + + /** Get the normalized measurement matrix H. + * H contains the partial derivatives of the measurement with respect to the state. + * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector. + * @return the normalized measurement matrix H + */ + private RealMatrix getMeasurementMatrix() { + + // Observed measurement characteristics + final SpacecraftState[] evaluationStates = predictedMeasurement.getStates(); + final ObservedMeasurement observedMeasurement = predictedMeasurement.getObservedMeasurement(); + final double[] sigma = observedMeasurement.getTheoreticalStandardDeviation(); + + // Initialize measurement matrix H: nxm + // n: Number of measurements in current measurement + // m: State vector size + final RealMatrix measurementMatrix = MatrixUtils. + createRealMatrix(observedMeasurement.getDimension(), + correctedEstimate.getState().getDimension()); + + // loop over all orbits involved in the measurement + for (int k = 0; k < evaluationStates.length; ++k) { + final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex(); + + // Predicted orbit + final Orbit predictedOrbit = evaluationStates[k].getOrbit(); + + // Measurement matrix's columns related to orbital parameters + // ---------------------------------------------------------- + + // Partial derivatives of the current Cartesian coordinates with respect to current orbital state + final double[][] aCY = new double[6][6]; + predictedOrbit.getJacobianWrtParameters(builders.get(p).getPositionAngleType(), aCY); //dC/dY + final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false); + + // Jacobian of the measurement with respect to current Cartesian coordinates + final RealMatrix dMdC = new Array2DRowRealMatrix(predictedMeasurement.getStateDerivatives(k), false); + + // Jacobian of the measurement with respect to current orbital state + final RealMatrix dMdY = dMdC.multiply(dCdY); + + // Fill the normalized measurement matrix's columns related to estimated orbital parameters + for (int i = 0; i < dMdY.getRowDimension(); ++i) { + int jOrb = orbitsStartColumns[p]; + for (int j = 0; j < dMdY.getColumnDimension(); ++j) { + final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j); + if (driver.isSelected()) { + measurementMatrix.setEntry(i, jOrb++, + dMdY.getEntry(i, j) / sigma[i] * driver.getScale()); + } + } + } + + // Normalized measurement matrix's columns related to propagation parameters + // -------------------------------------------------------------- + + // Jacobian of the measurement with respect to propagation parameters + final int nbParams = estimatedPropagationParameters[p].getNbParams(); + if (nbParams > 0) { + final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]); + final RealMatrix dMdPp = dMdY.multiply(dYdPp); + for (int i = 0; i < dMdPp.getRowDimension(); ++i) { + for (int j = 0; j < nbParams; ++j) { + final ParameterDriver delegating = estimatedPropagationParameters[p].getDrivers().get(j); + measurementMatrix.setEntry(i, propagationParameterColumns.get(delegating.getName()), + dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale()); + } + } + } + + // Normalized measurement matrix's columns related to measurement parameters + // -------------------------------------------------------------- + + // Jacobian of the measurement with respect to measurement parameters + // Gather the measurement parameters linked to current measurement + for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { + if (driver.isSelected()) { + // Derivatives of current measurement w/r to selected measurement parameter + final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver); + + // Check that the measurement parameter is managed by the filter + if (measurementParameterColumns.get(driver.getName()) != null) { + // Column of the driver in the measurement matrix + final int driverColumn = measurementParameterColumns.get(driver.getName()); + + // Fill the corresponding indexes of the measurement matrix + for (int i = 0; i < aMPm.length; ++i) { + measurementMatrix.setEntry(i, driverColumn, + aMPm[i] / sigma[i] * driver.getScale()); + } + } + } + } + } + + // Return the normalized measurement matrix + return measurementMatrix; + + } + + /** {@inheritDoc} */ + @Override + public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState, + final MeasurementDecorator measurement) { + + // Set a reference date for all measurements parameters that lack one (including the not estimated ones) + final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); + for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(builders.get(0).getInitialOrbitDate()); + } + } + + ++currentMeasurementNumber; + currentDate = measurement.getObservedMeasurement().getDate(); + + // Note: + // - n = size of the current measurement + // Example: + // * 1 for Range, RangeRate and TurnAroundRange + // * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination) + // * 6 for Position/Velocity + // - m = size of the state vector. n = nbOrb + nbPropag + nbMeas + + // Predict the state vector (mx1) + final RealVector predictedState = predictState(observedMeasurement.getDate()); + + // Get the error state transition matrix (mxm) + final RealMatrix stateTransitionMatrix = getErrorStateTransitionMatrix(); + + // Predict the measurement based on predicted spacecraft state + // Compute the innovations (i.e. residuals of the predicted measurement) + // ------------------------------------------------------------ + + // Predicted measurement + // Note: here the "iteration/evaluation" formalism from the batch LS method + // is twisted to fit the need of the Kalman filter. + // The number of "iterations" is actually the number of measurements processed by the filter + // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement. + predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, + currentMeasurementNumber, + KalmanEstimatorUtil.filterRelevant(observedMeasurement, predictedSpacecraftStates)); + + // Normalized measurement matrix (nxm) + final RealMatrix measurementMatrix = getMeasurementMatrix(); + + // compute process noise matrix + final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(), + previousState.getDimension()); + for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { + + // Number of estimated measurement parameters + final int nbMeas = estimatedMeasurementsParameters.getNbParams(); + + // Number of estimated dynamic parameters (orbital + propagation) + final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + + estimatedPropagationParameters[k].getNbParams(); + + // Covariance matrix + final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); + if (nbDyn > 0) { + final RealMatrix noiseP = covarianceMatricesProviders.get(k). + getProcessNoiseMatrix(correctedSpacecraftStates[k], + predictedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseP.getData(), 0, 0); + } + if (measurementProcessNoiseMatrix != null) { + final RealMatrix noiseM = measurementProcessNoiseMatrix. + getProcessNoiseMatrix(correctedSpacecraftStates[k], + predictedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); + } + + KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), + builders.get(k).getOrbitalParametersDrivers(), + builders.get(k).getPropagationParametersDrivers(), + estimatedMeasurementsParameters); + + final int[] indK = covarianceIndirection[k]; + for (int i = 0; i < indK.length; ++i) { + if (indK[i] >= 0) { + for (int j = 0; j < indK.length; ++j) { + if (indK[j] >= 0) { + physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); + } + } + } + } + + } + final RealMatrix normalizedProcessNoise = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); + + return new NonLinearEvolution(measurement.getTime(), predictedState, + stateTransitionMatrix, normalizedProcessNoise, measurementMatrix); + + } + + + /** {@inheritDoc} */ + @Override + public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution, + final RealMatrix innovationCovarianceMatrix) { + + // Apply the dynamic outlier filter, if it exists + KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix); + // Compute the innovation vector + return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement, predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** Finalize estimation. + * @param observedMeasurement measurement that has just been processed + * @param estimate corrected estimate + */ + public void finalizeEstimation(final ObservedMeasurement observedMeasurement, + final ProcessEstimate estimate) { + // Update the parameters with the estimated state + // The min/max values of the parameters are handled by the ParameterDriver implementation + correctedEstimate = estimate; + updateParameters(); + + // Get the estimated propagator (mirroring parameter update in the builder) + // and the estimated spacecraft state + final Propagator[] estimatedPropagators = getEstimatedPropagators(); + for (int k = 0; k < estimatedPropagators.length; ++k) { + correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState(); + } + + // Compute the estimated measurement using estimated spacecraft state + correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, + currentMeasurementNumber, + KalmanEstimatorUtil.filterRelevant(observedMeasurement, correctedSpacecraftStates)); + // Update the trajectory + // --------------------- + updateReferenceTrajectories(estimatedPropagators); + + } + + /** Set the predicted normalized state vector. + * The predicted/propagated orbit is used to update the state vector + * @param date prediction date + * @return predicted state + */ + private RealVector predictState(final AbsoluteDate date) { + + // Predicted state is initialized to previous estimated state + final RealVector predictedState = correctedEstimate.getState().copy(); + + // Orbital parameters counter + int jOrb = 0; + + for (int k = 0; k < predictedSpacecraftStates.length; ++k) { + + // Propagate the reference trajectory to measurement date + predictedSpacecraftStates[k] = referenceTrajectories[k].propagate(date); + + // Update the builder with the predicted orbit + // This updates the orbital drivers with the values of the predicted orbit + builders.get(k).resetOrbit(predictedSpacecraftStates[k].getOrbit()); + + // The orbital parameters in the state vector are replaced with their predicted values + // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation) + // As the propagator builder was previously updated with the predicted orbit, + // the selected orbital drivers are already up to date with the prediction + for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { + if (orbitalDriver.isSelected()) { + predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue()); + } + } + + } + + return predictedState; + + } + + /** Update the estimated parameters after the correction phase of the filter. + * The min/max allowed values are handled by the parameter themselves. + */ + private void updateParameters() { + final RealVector correctedState = correctedEstimate.getState(); + int i = 0; + for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { + // let the parameter handle min/max clipping + driver.setNormalizedValue(correctedState.getEntry(i)); + correctedState.setEntry(i++, driver.getNormalizedValue()); + } + for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { + // let the parameter handle min/max clipping + driver.setNormalizedValue(correctedState.getEntry(i)); + correctedState.setEntry(i++, driver.getNormalizedValue()); + } + for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { + // let the parameter handle min/max clipping + driver.setNormalizedValue(correctedState.getEntry(i)); + correctedState.setEntry(i++, driver.getNormalizedValue()); + } + } + + /** Getter for the propagators. + * @return the propagators + */ + public List getBuilders() { + return builders; + } + + /** Getter for the reference trajectories. + * @return the referencetrajectories + */ + public Propagator[] getReferenceTrajectories() { + return referenceTrajectories.clone(); + } + + /** Setter for the reference trajectories. + * @param referenceTrajectories the reference trajectories to be setted + */ + public void setReferenceTrajectories(final Propagator[] referenceTrajectories) { + this.referenceTrajectories = referenceTrajectories.clone(); + } + + /** Get the propagators estimated with the values set in the propagators builders. + * @return propagators based on the current values in the builder + */ + public Propagator[] getEstimatedPropagators() { + // Return propagators built with current instantiation of the propagator builders + final Propagator[] propagators = new Propagator[getBuilders().size()]; + for (int k = 0; k < getBuilders().size(); ++k) { + propagators[k] = getBuilders().get(k).buildPropagator(getBuilders().get(k).getSelectedNormalizedParameters()); + } + return propagators; } } diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanObserver.java b/src/main/java/org/orekit/estimation/sequential/KalmanObserver.java index 28dbaff423..6c832ea0fa 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanObserver.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanObserver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/MeasurementDecorator.java b/src/main/java/org/orekit/estimation/sequential/MeasurementDecorator.java index ed8f7a62bc..f8dc151cdc 100644 --- a/src/main/java/org/orekit/estimation/sequential/MeasurementDecorator.java +++ b/src/main/java/org/orekit/estimation/sequential/MeasurementDecorator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimator.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimator.java index 026b610676..f9f5e583f5 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimator.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorBuilder.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorBuilder.java index daa19c9eab..ab4a96c0a1 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorBuilder.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java index 1fbb034033..25bbab50fd 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -52,6 +52,7 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import org.orekit.utils.TimeSpanMap.Span; /** Process model to use with a {@link SemiAnalyticalKalmanEstimator}. * @@ -191,10 +192,12 @@ protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilde // Verify if the driver is selected if (driver.isSelected()) { estimatedPropagationParameters.add(driver); - final String driverName = driver.getName(); // Add the driver name if it has not been added yet - if (!estimatedPropagationParametersNames.contains(driverName)) { - estimatedPropagationParametersNames.add(driverName); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + if (!estimatedPropagationParametersNames.contains(span.getData())) { + estimatedPropagationParametersNames.add(span.getData()); + } } } @@ -213,8 +216,10 @@ protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilde if (parameter.getReferenceDate() == null) { parameter.setReferenceDate(currentDate); } - measurementParameterColumns.put(parameter.getName(), columns); - ++columns; + for (Span span = parameter.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + measurementParameterColumns.put(span.getData(), columns); + ++columns; + } } // Compute the scale factors @@ -224,10 +229,14 @@ protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilde scale[index++] = driver.getScale(); } for (final ParameterDriver driver : estimatedPropagationParameters.getDrivers()) { - scale[index++] = driver.getScale(); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + scale[index++] = driver.getScale(); + } } for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - scale[index++] = driver.getScale(); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + scale[index++] = driver.getScale(); + } } // Build the reference propagator and add its partial derivatives equations implementation @@ -245,18 +254,18 @@ protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilde // Initialize propagation parameters part of the state transition matrix (See Ref [1], Eq. 3.2c) this.psiS = null; if (estimatedPropagationParameters.getNbParams() != 0) { - this.psiS = MatrixUtils.createRealMatrix(getNumberSelectedOrbitalDrivers(), - getNumberSelectedPropagationDrivers()); + this.psiS = MatrixUtils.createRealMatrix(getNumberSelectedOrbitalDriversValuesToEstimate(), + getNumberSelectedPropagationDriversValuesToEstimate()); } // Initialize inverse of the orbital part of the state transition matrix (See Ref [1], Eq. 3.2d) - this.phiS = MatrixUtils.createRealIdentityMatrix(getNumberSelectedOrbitalDrivers()); + this.phiS = MatrixUtils.createRealIdentityMatrix(getNumberSelectedOrbitalDriversValuesToEstimate()); // Number of estimated measurement parameters - final int nbMeas = getNumberSelectedMeasurementDrivers(); + final int nbMeas = getNumberSelectedMeasurementDriversValuesToEstimate(); // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers(); + final int nbDyn = getNumberSelectedOrbitalDriversValuesToEstimate() + getNumberSelectedPropagationDriversValuesToEstimate(); // Covariance matrix final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); @@ -273,7 +282,7 @@ protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilde builder.getPropagationParametersDrivers(), estimatedMeasurementsParameters); - final RealMatrix correctedCovariance = normalizeCovarianceMatrix(noiseK); + final RealMatrix correctedCovariance = KalmanEstimatorUtil.normalizeCovarianceMatrix(noiseK, scale); // Initialize corrected estimate this.correctedEstimate = new ProcessEstimate(0.0, correctedFilterCorrection, correctedCovariance); @@ -370,7 +379,7 @@ public NonLinearEvolution getEvolution(final double previousTime, final RealVect // Calculate the predicted osculating elements final double[] osculating = computeOsculatingElements(predictedFilterCorrection); - final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngle(), + final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngleType(), currentDate, nominalMeanSpacecraftState.getMu(), nominalMeanSpacecraftState.getFrame()); @@ -389,10 +398,10 @@ public NonLinearEvolution getEvolution(final double previousTime, final RealVect final RealMatrix measurementMatrix = getMeasurementMatrix(); // Number of estimated measurement parameters - final int nbMeas = getNumberSelectedMeasurementDrivers(); + final int nbMeas = getNumberSelectedMeasurementDriversValuesToEstimate(); // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers(); + final int nbDyn = getNumberSelectedOrbitalDriversValuesToEstimate() + getNumberSelectedPropagationDriversValuesToEstimate(); // Covariance matrix final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); @@ -409,7 +418,7 @@ public NonLinearEvolution getEvolution(final double previousTime, final RealVect builder.getPropagationParametersDrivers(), estimatedMeasurementsParameters); - final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(noiseK); + final RealMatrix normalizedProcessNoise = KalmanEstimatorUtil.normalizeCovarianceMatrix(noiseK, scale); // Return return new NonLinearEvolution(measurement.getTime(), predictedFilterCorrection, stm, @@ -439,7 +448,7 @@ public void finalizeEstimation(final ObservedMeasurement observedMeasurement, previousNominalMeanSpacecraftState = nominalMeanSpacecraftState; // Calculate the corrected osculating elements final double[] osculating = computeOsculatingElements(correctedFilterCorrection); - final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngle(), + final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngleType(), currentDate, nominalMeanSpacecraftState.getMu(), nominalMeanSpacecraftState.getFrame()); @@ -505,13 +514,19 @@ public RealVector getPhysicalEstimatedState() { final RealVector physicalEstimatedState = new ArrayRealVector(scale.length); int i = 0; for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + physicalEstimatedState.setEntry(i++, span.getData()); + } } for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + physicalEstimatedState.setEntry(i++, span.getData()); + } } for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + physicalEstimatedState.setEntry(i++, span.getData()); + } } return physicalEstimatedState; @@ -525,21 +540,7 @@ public RealMatrix getPhysicalEstimatedCovarianceMatrix() { // For each element [i,j] of P the corresponding normalized value is: // Pn[i,j] = P[i,j] / (scale[i]*scale[j]) // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j] - - // Normalized covariance matrix - final RealMatrix normalizedP = correctedEstimate.getCovariance(); - - // Initialize physical covariance matrix - final int nbParams = normalizedP.getRowDimension(); - final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams); - - // Un-normalize the covairance matrix - for (int i = 0; i < nbParams; ++i) { - for (int j = 0; j < nbParams; ++j) { - physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]); - } - } - return physicalP; + return KalmanEstimatorUtil.unnormalizeCovarianceMatrix(correctedEstimate.getCovariance(), scale); } /** {@inheritDoc} */ @@ -549,26 +550,8 @@ public RealMatrix getPhysicalStateTransitionMatrix() { // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas // For each element [i,j] of normalized φ (φn), the corresponding physical value is: // φ[i,j] = φn[i,j] * scale[i] / scale[j] - - // Normalized matrix - final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix(); - - if (normalizedSTM == null) { - return null; - } else { - // Initialize physical matrix - final int nbParams = normalizedSTM.getRowDimension(); - final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams); - - // Un-normalize the matrix - for (int i = 0; i < nbParams; ++i) { - for (int j = 0; j < nbParams; ++j) { - physicalSTM.setEntry(i, j, - normalizedSTM.getEntry(i, j) * scale[i] / scale[j]); - } - } - return physicalSTM; - } + return correctedEstimate.getStateTransitionMatrix() == null ? + null : KalmanEstimatorUtil.unnormalizeStateTransitionMatrix(correctedEstimate.getStateTransitionMatrix(), scale); } /** {@inheritDoc} */ @@ -580,29 +563,10 @@ public RealMatrix getPhysicalMeasurementJacobian() { // - n is the size of the measurement being processed by the filter // For each element [i,j] of normalized H (Hn) the corresponding physical value is: // H[i,j] = Hn[i,j] * σ[i] / scale[j] - - // Normalized matrix - final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian(); - - if (normalizedH == null) { - return null; - } else { - // Get current measurement sigmas - final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation(); - - // Initialize physical matrix - final int nbLine = normalizedH.getRowDimension(); - final int nbCol = normalizedH.getColumnDimension(); - final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol); - - // Un-normalize the matrix - for (int i = 0; i < nbLine; ++i) { - for (int j = 0; j < nbCol; ++j) { - physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]); - } - } - return physicalH; - } + return correctedEstimate.getMeasurementJacobian() == null ? + null : KalmanEstimatorUtil.unnormalizeMeasurementJacobian(correctedEstimate.getMeasurementJacobian(), + scale, + correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); } /** {@inheritDoc} */ @@ -612,28 +576,9 @@ public RealMatrix getPhysicalInnovationCovarianceMatrix() { // S is an nxn matrix where n is the size of the measurement being processed by the filter // For each element [i,j] of normalized S (Sn) the corresponding physical value is: // S[i,j] = Sn[i,j] * σ[i] * σ[j] - - // Normalized matrix - final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance(); - - if (normalizedS == null) { - return null; - } else { - // Get current measurement sigmas - final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation(); - - // Initialize physical matrix - final int nbMeas = sigmas.length; - final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas); - - // Un-normalize the matrix - for (int i = 0; i < nbMeas; ++i) { - for (int j = 0; j < nbMeas; ++j) { - physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] * sigmas[j]); - } - } - return physicalS; - } + return correctedEstimate.getInnovationCovariance() == null ? + null : KalmanEstimatorUtil.unnormalizeInnovationCovarianceMatrix(correctedEstimate.getInnovationCovariance(), + predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); } /** {@inheritDoc} */ @@ -645,29 +590,10 @@ public RealMatrix getPhysicalKalmanGain() { // - n is the size of the measurement being processed by the filter // For each element [i,j] of normalized K (Kn) the corresponding physical value is: // K[i,j] = Kn[i,j] * scale[i] / σ[j] - - // Normalized matrix - final RealMatrix normalizedK = correctedEstimate.getKalmanGain(); - - if (normalizedK == null) { - return null; - } else { - // Get current measurement sigmas - final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation(); - - // Initialize physical matrix - final int nbLine = normalizedK.getRowDimension(); - final int nbCol = normalizedK.getColumnDimension(); - final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol); - - // Un-normalize the matrix - for (int i = 0; i < nbLine; ++i) { - for (int j = 0; j < nbCol; ++j) { - physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]); - } - } - return physicalK; - } + return correctedEstimate.getKalmanGain() == null ? + null : KalmanEstimatorUtil.unnormalizeKalmanGainMatrix(correctedEstimate.getKalmanGain(), + scale, + correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); } /** {@inheritDoc} */ @@ -728,7 +654,7 @@ public void updateReferenceTrajectory(final DSSTPropagator propagator) { public void updateShortPeriods(final SpacecraftState state) { // Loop on DSST force models for (final DSSTForceModel model : builder.getAllForceModels()) { - model.updateShortPeriodTerms(model.getParameters(), state); + model.updateShortPeriodTerms(model.getParametersAllValues(), state); } harvester.updateFieldShortPeriodTerms(state); } @@ -738,7 +664,7 @@ public void updateShortPeriods(final SpacecraftState state) { public void initializeShortPeriodicTerms(final SpacecraftState meanState) { final List shortPeriodTerms = new ArrayList(); for (final DSSTForceModel force : builder.getAllForceModels()) { - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(new AuxiliaryElements(meanState.getOrbit(), 1), PropagationType.OSCULATING, force.getParameters())); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(new AuxiliaryElements(meanState.getOrbit(), 1), PropagationType.OSCULATING, force.getParameters(meanState.getDate()))); } dsstPropagator.setShortPeriodTerms(shortPeriodTerms); } @@ -772,7 +698,7 @@ private RealMatrix getErrorStateTransitionMatrix() { final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension()); // Derivatives of the state vector with respect to initial state vector - final int nbOrb = getNumberSelectedOrbitalDrivers(); + final int nbOrb = getNumberSelectedOrbitalDriversValuesToEstimate(); final RealMatrix dYdY0 = harvester.getB2(nominalMeanSpacecraftState); // Calculate transitional orbital matrix (See Ref [1], Eq. 3.4a) @@ -797,7 +723,7 @@ private RealMatrix getErrorStateTransitionMatrix() { // Derivatives of the state vector with respect to propagation parameters if (psiS != null) { - final int nbProp = getNumberSelectedPropagationDrivers(); + final int nbProp = getNumberSelectedPropagationDriversValuesToEstimate(); final RealMatrix dYdPp = harvester.getB3(nominalMeanSpacecraftState); // Calculate transitional parameters matrix (See Ref [1], Eq. 3.4b) @@ -857,7 +783,7 @@ private RealMatrix getMeasurementMatrix() { final int nbOrb = getNumberSelectedOrbitalDrivers(); final int nbProp = getNumberSelectedPropagationDrivers(); final double[][] aCY = new double[nbOrb][nbOrb]; - predictedOrbit.getJacobianWrtParameters(builder.getPositionAngle(), aCY); + predictedOrbit.getJacobianWrtParameters(builder.getPositionAngleType(), aCY); final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false); // Jacobian of the measurement with respect to current Cartesian coordinates @@ -891,10 +817,17 @@ private RealMatrix getMeasurementMatrix() { final double driverScale = builder.getOrbitalParametersDrivers().getDrivers().get(j).getScale(); measurementMatrix.setEntry(i, j, dMdY.getEntry(i, j) / sigma[i] * driverScale); } + + int col = 0; for (int j = 0; j < nbProp; j++) { final double driverScale = estimatedPropagationParameters.getDrivers().get(j).getScale(); - measurementMatrix.setEntry(i, j + nbOrb, - dMdY.getEntry(i, j + nbOrb) / sigma[i] * driverScale); + for (Span span = estimatedPropagationParameters.getDrivers().get(j).getValueSpanMap().getFirstSpan(); + span != null; span = span.next()) { + + measurementMatrix.setEntry(i, col + nbOrb, + dMdY.getEntry(i, col + nbOrb) / sigma[i] * driverScale); + col++; + } } } @@ -905,17 +838,19 @@ private RealMatrix getMeasurementMatrix() { // Gather the measurement parameters linked to current measurement for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { if (driver.isSelected()) { - // Derivatives of current measurement w/r to selected measurement parameter - final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver); - - // Check that the measurement parameter is managed by the filter - if (measurementParameterColumns.get(driver.getName()) != null) { - // Column of the driver in the measurement matrix - final int driverColumn = measurementParameterColumns.get(driver.getName()); - - // Fill the corresponding indexes of the measurement matrix - for (int i = 0; i < aMPm.length; ++i) { - measurementMatrix.setEntry(i, driverColumn, aMPm[i] / sigma[i] * driver.getScale()); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // Derivatives of current measurement w/r to selected measurement parameter + final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver, span.getStart()); + + // Check that the measurement parameter is managed by the filter + if (measurementParameterColumns.get(span.getData()) != null) { + // Column of the driver in the measurement matrix + final int driverColumn = measurementParameterColumns.get(span.getData()); + + // Fill the corresponding indexes of the measurement matrix + for (int i = 0; i < aMPm.length; ++i) { + measurementMatrix.setEntry(i, driverColumn, aMPm[i] / sigma[i] * driver.getScale()); + } } } } @@ -959,7 +894,7 @@ private double[] computeOsculatingElements(final RealVector filterCorrection) { // Nominal mean elements final double[] nominalMeanElements = new double[nbOrb]; - OrbitType.EQUINOCTIAL.mapOrbitToArray(nominalMeanSpacecraftState.getOrbit(), builder.getPositionAngle(), nominalMeanElements, null); + OrbitType.EQUINOCTIAL.mapOrbitToArray(nominalMeanSpacecraftState.getOrbit(), builder.getPositionAngleType(), nominalMeanElements, null); // Ref [1] Eq. 3.6 final double[] osculatingElements = new double[nbOrb]; @@ -983,30 +918,6 @@ private void analyticalDerivativeComputations(final SpacecraftState state) { harvester.setReferenceState(state); } - /** Normalize a covariance matrix. - * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas - * For each element [i,j] of P the corresponding normalized value is: - * Pn[i,j] = P[i,j] / (scale[i]*scale[j]) - * @param physicalCovarianceMatrix The "physical" covariance matrix in input - * @return the normalized covariance matrix - */ - private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) { - - // Initialize output matrix - final int nbParams = physicalCovarianceMatrix.getRowDimension(); - final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams); - - // Normalize the state matrix - for (int i = 0; i < nbParams; ++i) { - for (int j = 0; j < nbParams; ++j) { - normalizedCovarianceMatrix.setEntry(i, j, - physicalCovarianceMatrix.getEntry(i, j) / - (scale[i] * scale[j])); - } - } - return normalizedCovarianceMatrix; - } - /** Get the number of estimated orbital parameters. * @return the number of estimated orbital parameters */ @@ -1021,11 +932,43 @@ private int getNumberSelectedPropagationDrivers() { return estimatedPropagationParameters.getNbParams(); } - /** Get the number of estimated measurement parameters. - * @return the number of estimated measurement parameters + /** Get the number of estimated orbital parameters values (some parameter + * driver may have several values to estimate for different time range + * {@link ParameterDriver}. + * @return the number of estimated values for , orbital parameters */ - private int getNumberSelectedMeasurementDrivers() { - return estimatedMeasurementsParameters.getNbParams(); + private int getNumberSelectedOrbitalDriversValuesToEstimate() { + int nbOrbitalValuesToEstimate = 0; + for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) { + nbOrbitalValuesToEstimate += driver.getNbOfValues(); + } + return nbOrbitalValuesToEstimate; + } + + /** Get the number of estimated propagation parameters values (some parameter + * driver may have several values to estimate for different time range + * {@link ParameterDriver}. + * @return the number of estimated values for propagation parameters + */ + private int getNumberSelectedPropagationDriversValuesToEstimate() { + int nbPropagationValuesToEstimate = 0; + for (final ParameterDriver driver : estimatedPropagationParameters.getDrivers()) { + nbPropagationValuesToEstimate += driver.getNbOfValues(); + } + return nbPropagationValuesToEstimate; + } + + /** Get the number of estimated measurement parameters values (some parameter + * driver may have several values to estimate for different time range + * {@link ParameterDriver}. + * @return the number of estimated values for measurement parameters + */ + private int getNumberSelectedMeasurementDriversValuesToEstimate() { + int nbMeasurementValuesToEstimate = 0; + for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { + nbMeasurementValuesToEstimate += driver.getNbOfValues(); + } + return nbMeasurementValuesToEstimate; } /** Update the estimated parameters after the correction phase of the filter. @@ -1034,17 +977,29 @@ private int getNumberSelectedMeasurementDrivers() { private void updateParameters() { final RealVector correctedState = correctedEstimate.getState(); int i = 0; + // Orbital parameters for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { // let the parameter handle min/max clipping - driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++)); + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + driver.setNormalizedValue(driver.getNormalizedValue(span.getStart()) + correctedState.getEntry(i++), span.getStart()); + } } + + // Propagation parameters for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { // let the parameter handle min/max clipping - driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++)); + // If the parameter driver contains only 1 value to estimate over the all time range + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + driver.setNormalizedValue(driver.getNormalizedValue(span.getStart()) + correctedState.getEntry(i++), span.getStart()); + } } + + // Measurements parameters for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { // let the parameter handle min/max clipping - driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++)); + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + driver.setNormalizedValue(driver.getNormalizedValue(span.getStart()) + correctedState.getEntry(i++), span.getStart()); + } } } diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalMeasurementHandler.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalMeasurementHandler.java index 845135845d..1209978669 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalMeasurementHandler.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalMeasurementHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalProcess.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalProcess.java index e5734ea7a2..b928de5991 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalProcess.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalProcess.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimator.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimator.java index bebfd2f1dc..9906b278fa 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimator.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorBuilder.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorBuilder.java index dd4209e16f..eec202f902 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorBuilder.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java index ea2629b71e..e0479f64d1 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,7 +33,7 @@ import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.conversion.DSSTPropagatorBuilder; @@ -73,7 +73,7 @@ public class SemiAnalyticalUnscentedKalmanModel implements KalmanEstimation, Uns private final CovarianceMatrixProvider measurementProcessNoiseMatrix; /** Position angle type used during orbit determination. */ - private final PositionAngle angleType; + private final PositionAngleType angleType; /** Orbit type used during orbit determination. */ private final OrbitType orbitType; @@ -132,7 +132,7 @@ protected SemiAnalyticalUnscentedKalmanModel(final DSSTPropagatorBuilder propaga final CovarianceMatrixProvider measurementProcessNoiseMatrix) { this.builder = propagatorBuilder; - this.angleType = propagatorBuilder.getPositionAngle(); + this.angleType = propagatorBuilder.getPositionAngleType(); this.orbitType = propagatorBuilder.getOrbitType(); this.estimatedMeasurementsParameters = estimatedMeasurementParameters; this.currentMeasurementNumber = 0; @@ -312,27 +312,12 @@ public UnscentedEvolution getEvolution(final double previousTime, final RealVect // STM for the prediction of the filter correction final RealMatrix stm = getStm(); - // Estimate the measurement for each predicted state - final RealVector[] predictedStates = new RealVector[sigmaPoints.length]; - final RealVector[] predictedMeasurements = new RealVector[sigmaPoints.length]; + // Predicted states + final RealVector[] predictedStates = new RealVector[sigmaPoints.length]; for (int k = 0; k < sigmaPoints.length; ++k) { - // Predict filter correction for the current sigma point final RealVector predicted = stm.operate(sigmaPoints[k]); predictedStates[k] = predicted; - - // Calculate the predicted osculating elements for the current mean state - final RealVector osculating = computeOsculatingElements(predicted, nominalMeanSpacecraftState, shortPeriodicTerms); - final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, angleType, - currentDate, builder.getMu(), builder.getFrame()); - - // Then, estimate the measurement - final EstimatedMeasurement estimated = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - new SpacecraftState[] { - new SpacecraftState(osculatingOrbit) - }); - predictedMeasurements[k] = new ArrayRealVector(estimated.getEstimatedValue()); } // Number of estimated measurement parameters @@ -357,7 +342,40 @@ public UnscentedEvolution getEvolution(final double previousTime, final RealVect estimatedMeasurementsParameters); // Return - return new UnscentedEvolution(measurement.getTime(), predictedStates, predictedMeasurements, noiseK); + return new UnscentedEvolution(measurement.getTime(), predictedStates, noiseK); + + } + + /** {@inheritDoc} */ + @Override + public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPoints, final MeasurementDecorator measurement) { + + // Observed measurement + final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); + + // Initialize arrays of predicted states and measurements + final RealVector[] predictedMeasurements = new RealVector[predictedSigmaPoints.length]; + + // Loop on sigma points + for (int k = 0; k < predictedSigmaPoints.length; ++k) { + + // Calculate the predicted osculating elements for the current mean state + final RealVector osculating = computeOsculatingElements(predictedSigmaPoints[k], nominalMeanSpacecraftState, shortPeriodicTerms); + final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, angleType, + currentDate, builder.getMu(), builder.getFrame()); + + // Then, estimate the measurement + final EstimatedMeasurement estimated = observedMeasurement.estimate(currentMeasurementNumber, + currentMeasurementNumber, + new SpacecraftState[] { + new SpacecraftState(osculatingOrbit) + }); + predictedMeasurements[k] = new ArrayRealVector(estimated.getEstimatedValue()); + + } + + // Return + return predictedMeasurements; } @@ -375,6 +393,7 @@ public RealVector getInnovation(final MeasurementDecorator measurement, final Re currentDate, builder.getMu(), builder.getFrame()); predictedSpacecraftState = new SpacecraftState(osculatingOrbit); predictedMeasurement = measurement.getObservedMeasurement().estimate(currentMeasurementNumber, currentMeasurementNumber, getPredictedSpacecraftStates()); + predictedMeasurement.setEstimatedValue(predictedMeas.toArray()); // Apply the dynamic outlier filter, if it exists KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix); @@ -400,7 +419,7 @@ public void finalizeEstimation(final ObservedMeasurement observedMeasurement, // Update the previous nominal mean spacecraft state // Calculate the corrected osculating elements final RealVector osculating = computeOsculatingElements(correctedFilterCorrection, nominalMeanSpacecraftState, shortPeriodicTerms); - final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, builder.getPositionAngle(), + final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, builder.getPositionAngleType(), currentDate, builder.getMu(), builder.getFrame()); // Compute the corrected measurements diff --git a/src/main/java/org/orekit/estimation/sequential/TLEKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/TLEKalmanModel.java deleted file mode 100644 index 965c44755d..0000000000 --- a/src/main/java/org/orekit/estimation/sequential/TLEKalmanModel.java +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.estimation.sequential; - -import java.util.List; - -import org.orekit.annotation.DefaultDataContext; -import org.orekit.propagation.MatricesHarvester; -import org.orekit.propagation.PropagationType; -import org.orekit.propagation.Propagator; -import org.orekit.propagation.analytical.tle.TLEJacobiansMapper; -import org.orekit.propagation.analytical.tle.TLEPropagator; -import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder; -import org.orekit.utils.ParameterDriversList; - -/** Class defining the process model dynamics to use with a {@link KalmanEstimator}. - *

      - * This class is an adaption of the {@link KalmanModel} class - * but for the {@link TLEPropagator TLE propagator}. - *

      - * @author Romain Gerbaud - * @author Maxime Journot - * @author Bryan Cazabonne - * @author Thomas Paulet - * @since 11.0 - * @deprecated as of 11.1, replaced by {@link KalmanModel} - */ -@Deprecated -public class TLEKalmanModel extends AbstractKalmanModel { - - /** Kalman process model constructor (package private). - * @param propagatorBuilders propagators builders used to evaluate the orbits. - * @param covarianceMatricesProviders providers for covariance matrices - * @param estimatedMeasurementParameters measurement parameters to estimate - * @param measurementProcessNoiseMatrix provider for measurement process noise matrix - */ - public TLEKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - // call super constructor - super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, - measurementProcessNoiseMatrix, new TLEJacobiansMapper[propagatorBuilders.size()]); - } - - /** {@inheritDoc} */ - @Override - @DefaultDataContext - protected void updateReferenceTrajectories(final Propagator[] propagators, - final PropagationType pType, - final PropagationType sType) { - - // Update the reference trajectory propagator - setReferenceTrajectories(propagators); - - // Jacobian harvesters - final MatricesHarvester[] harvesters = new MatricesHarvester[propagators.length]; - - for (int k = 0; k < propagators.length; ++k) { - // Link the partial derivatives to this new propagator - final String equationName = KalmanEstimator.class.getName() + "-derivatives-" + k; - harvesters[k] = ((TLEPropagator) getReferenceTrajectories()[k]).setupMatricesComputation(equationName, null, null); - } - - // Update Jacobian harvesters - setHarvesters(harvesters); - - } - -} diff --git a/src/main/java/org/orekit/estimation/sequential/UnivariateProcessNoise.java b/src/main/java/org/orekit/estimation/sequential/UnivariateProcessNoise.java index b359f20734..165d1a22a5 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnivariateProcessNoise.java +++ b/src/main/java/org/orekit/estimation/sequential/UnivariateProcessNoise.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,9 +20,9 @@ import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.orekit.frames.LOFType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.propagation.StateCovariance; /** Provider for a temporal evolution of the process noise matrix. * All parameters (orbital or propagation) are time dependent and provided as {@link UnivariateFunction}. @@ -31,7 +31,7 @@ * The method {@link #getProcessNoiseMatrix} then square the values so that they are consistent with a covariance matrix. *

      * The orbital parameters evolutions are provided in LOF frame and Cartesian (PV); - * then converted in inertial frame and current {@link org.orekit.orbits.OrbitType} and {@link PositionAngle} + * then converted in inertial frame and current {@link org.orekit.orbits.OrbitType} and {@link PositionAngleType} * when method {@link #getProcessNoiseMatrix} is called. *

      *

      @@ -67,7 +67,7 @@ public class UnivariateProcessNoise extends AbstractCovarianceMatrixProvider { private final LOFType lofType; /** Position angle for the orbital process noise matrix. */ - private final PositionAngle positionAngle; + private final PositionAngleType positionAngleType; /** Array of univariate functions for the six orbital parameters process noise evolution in LOF frame and Cartesian formalism. */ private final UnivariateFunction[] lofCartesianOrbitalParametersEvolution; @@ -81,24 +81,24 @@ public class UnivariateProcessNoise extends AbstractCovarianceMatrixProvider { /** Simple constructor. * @param initialCovarianceMatrix initial covariance matrix * @param lofType the LOF type used - * @param positionAngle the position angle used for the computation of the process noise + * @param positionAngleType the position angle used for the computation of the process noise * @param lofCartesianOrbitalParametersEvolution Array of univariate functions for the six orbital parameters process noise evolution in LOF frame and Cartesian orbit type * @param propagationParametersEvolution Array of univariate functions for the propagation parameters process noise evolution */ public UnivariateProcessNoise(final RealMatrix initialCovarianceMatrix, final LOFType lofType, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final UnivariateFunction[] lofCartesianOrbitalParametersEvolution, final UnivariateFunction[] propagationParametersEvolution) { // Call the new constructor with an empty array for measurements parameters - this(initialCovarianceMatrix, lofType, positionAngle, lofCartesianOrbitalParametersEvolution, propagationParametersEvolution, new UnivariateFunction[0]); + this(initialCovarianceMatrix, lofType, positionAngleType, lofCartesianOrbitalParametersEvolution, propagationParametersEvolution, new UnivariateFunction[0]); } /** Simple constructor. * @param initialCovarianceMatrix initial covariance matrix * @param lofType the LOF type used - * @param positionAngle the position angle used for the computation of the process noise + * @param positionAngleType the position angle used for the computation of the process noise * @param lofCartesianOrbitalParametersEvolution Array of univariate functions for the six orbital parameters process noise evolution in LOF frame and Cartesian orbit type * @param propagationParametersEvolution Array of univariate functions for the propagation parameters process noise evolution * @param measurementsParametersEvolution Array of univariate functions for the measurements parameters process noise evolution @@ -106,14 +106,14 @@ public UnivariateProcessNoise(final RealMatrix initialCovarianceMatrix, */ public UnivariateProcessNoise(final RealMatrix initialCovarianceMatrix, final LOFType lofType, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final UnivariateFunction[] lofCartesianOrbitalParametersEvolution, final UnivariateFunction[] propagationParametersEvolution, final UnivariateFunction[] measurementsParametersEvolution) { super(initialCovarianceMatrix); this.lofType = lofType; - this.positionAngle = positionAngle; + this.positionAngleType = positionAngleType; this.lofCartesianOrbitalParametersEvolution = lofCartesianOrbitalParametersEvolution.clone(); this.propagationParametersEvolution = propagationParametersEvolution.clone(); this.measurementsParametersEvolution = measurementsParametersEvolution.clone(); @@ -129,8 +129,8 @@ public LOFType getLofType() { /** Getter for the positionAngle. * @return the positionAngle */ - public PositionAngle getPositionAngle() { - return positionAngle; + public PositionAngleType getPositionAngleType() { + return positionAngleType; } /** Getter for the lofCartesianOrbitalParametersEvolution. @@ -204,12 +204,12 @@ private RealMatrix getInertialOrbitalProcessNoiseMatrix(final SpacecraftState pr final double deltaT = current.getDate().durationFrom(previous.getDate()); // Evaluate the functions, using ΔT as argument - final int lofOrbitalprocessNoiseLength = lofCartesianOrbitalParametersEvolution.length; - final double[] lofOrbitalProcessNoiseValues = new double[lofOrbitalprocessNoiseLength]; + final int lofOrbitalProcessNoiseLength = lofCartesianOrbitalParametersEvolution.length; + final double[] lofOrbitalProcessNoiseValues = new double[lofOrbitalProcessNoiseLength]; // The function return a value which dimension is that of a standard deviation // It needs to be squared before being put in the process noise covariance matrix - for (int i = 0; i < lofOrbitalprocessNoiseLength; i++) { + for (int i = 0; i < lofOrbitalProcessNoiseLength; i++) { final double functionValue = lofCartesianOrbitalParametersEvolution[i].value(deltaT); lofOrbitalProcessNoiseValues[i] = functionValue * functionValue; } @@ -217,26 +217,20 @@ private RealMatrix getInertialOrbitalProcessNoiseMatrix(final SpacecraftState pr // Form the diagonal matrix in LOF frame and Cartesian formalism final RealMatrix lofCartesianProcessNoiseMatrix = MatrixUtils.createRealDiagonalMatrix(lofOrbitalProcessNoiseValues); - // Get the Jacobian from LOF to Inertial - final double[][] dLofdInertial = new double[6][6]; - lofType.transformFromInertial(current.getDate(), - current.getPVCoordinates()).getInverse().getJacobian(CartesianDerivativesFilter.USE_PV, - dLofdInertial); - final RealMatrix jacLofToInertial = MatrixUtils.createRealMatrix(dLofdInertial); - - // Jacobian of orbit parameters with respect to Cartesian parameters - final double[][] dYdC = new double[6][6]; - current.getOrbit().getJacobianWrtCartesian(positionAngle, dYdC); - final RealMatrix jacOrbitWrtCartesian = MatrixUtils.createRealMatrix(dYdC); - - // Complete Jacobian of the transformation - final RealMatrix jacobian = jacOrbitWrtCartesian.multiply(jacLofToInertial); - - // Return the orbital process noise matrix in inertial frame and proper orbit type - final RealMatrix inertialOrbitalProcessNoiseMatrix = jacobian. - multiply(lofCartesianProcessNoiseMatrix). - multiplyTransposed(jacobian); - return inertialOrbitalProcessNoiseMatrix; + // Create state covariance object in LOF + final StateCovariance lofCartesianProcessNoiseCov = + new StateCovariance(lofCartesianProcessNoiseMatrix, current.getDate(), lofType); + + // Convert to Cartesian in orbital frame + final StateCovariance inertialCartesianProcessNoiseCov = + lofCartesianProcessNoiseCov.changeCovarianceFrame(current.getOrbit(), current.getFrame()); + + // Convert to current orbit type and position angle + final StateCovariance inertialOrbitalProcessNoiseCov = + inertialCartesianProcessNoiseCov.changeCovarianceType(current.getOrbit(), + current.getOrbit().getType(), positionAngleType); + // Return inertial orbital covariance matrix + return inertialOrbitalProcessNoiseCov.getMatrix(); } /** Get the process noise for the propagation parameters. diff --git a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java index 3b66110308..efc97a352f 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java +++ b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,8 @@ import org.hipparchus.util.UnscentedTransformProvider; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.Propagator; -import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.propagation.conversion.DSSTPropagatorBuilder; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; @@ -32,7 +33,7 @@ /** * Implementation of an Unscented Kalman filter to perform orbit determination. *

      - * The filter uses a {@link NumericalPropagatorBuilder} to initialize its reference trajectory. + * The filter uses a {@link PropagatorBuilder} to initialize its reference trajectory. *

      *

      * The estimated parameters are driven by {@link ParameterDriver} objects. They are of 3 different types:

        @@ -51,7 +52,11 @@ *

        * *

        An {@link UnscentedKalmanEstimator} object is built using the {@link UnscentedKalmanEstimatorBuilder#build() build} - * method of a {@link UnscentedKalmanEstimatorBuilder}.

        + * method of a {@link UnscentedKalmanEstimatorBuilder}. The builder is generalized to accept any {@link PropagatorBuilder}. + * Howerver, it is absolutely not recommended to use a {@link DSSTPropagatorBuilder}. + * A specific {@link SemiAnalyticalUnscentedKalmanEstimatorBuilder semi-analytical unscented Kalman Filter} is implemented + * and shall be used. + *

        * * @author Gaëtan Pierre * @author Bryan Cazabonne @@ -80,7 +85,7 @@ public class UnscentedKalmanEstimator extends AbstractKalmanEstimator { * @param utProvider provider for the unscented transform. */ UnscentedKalmanEstimator(final MatrixDecomposer decomposer, - final List propagatorBuilders, + final List propagatorBuilders, final List processNoiseMatricesProviders, final ParameterDriversList estimatedMeasurementParameters, final CovarianceMatrixProvider measurementProcessNoiseMatrix, diff --git a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorBuilder.java b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorBuilder.java index 1cac72bb10..3e9eae527b 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorBuilder.java +++ b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,10 +24,17 @@ import org.hipparchus.util.UnscentedTransformProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.propagation.conversion.DSSTPropagatorBuilder; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.utils.ParameterDriversList; /** Builder for an Unscented Kalman filter estimator. + *

        + * The builder is generalized to accept any {@link PropagatorBuilder}. + * Howerver, it is absolutely not recommended to use a {@link DSSTPropagatorBuilder}. + * A specific {@link SemiAnalyticalUnscentedKalmanEstimatorBuilder semi-analytical + * unscented Kalman Filter} is implemented and shall be used. + *

        * @author Gaëtan Pierre * @author Bryan Cazabonne * @since 11.3 @@ -38,7 +45,7 @@ public class UnscentedKalmanEstimatorBuilder { private MatrixDecomposer decomposer; /** Builders for propagators. */ - private List propagatorBuilders; + private List propagatorBuilders; /** Estimated measurements parameters. */ private ParameterDriversList estimatedMeasurementsParameters; @@ -66,7 +73,7 @@ public UnscentedKalmanEstimatorBuilder() { /** Construct a {@link UnscentedKalmanEstimator} from the data in this builder. *

        - * Before this method is called, {@link #addPropagationConfiguration(NumericalPropagatorBuilder, + * Before this method is called, {@link #addPropagationConfiguration(PropagatorBuilder, * CovarianceMatrixProvider) addPropagationConfiguration()} must have been called * at least once, otherwise configuration is incomplete and an exception will be raised. *

        @@ -131,7 +138,7 @@ public UnscentedKalmanEstimatorBuilder unscentedTransformProvider(final Unscente * org.orekit.propagation.SpacecraftState) getProcessNoiseMatrix(previous, current) * @return this object. */ - public UnscentedKalmanEstimatorBuilder addPropagationConfiguration(final NumericalPropagatorBuilder builder, + public UnscentedKalmanEstimatorBuilder addPropagationConfiguration(final PropagatorBuilder builder, final CovarianceMatrixProvider provider) { propagatorBuilders.add(builder); processNoiseMatrixProviders.add(provider); diff --git a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java index a62d5f3095..39d5193873 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,12 +34,11 @@ import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.PropagatorsParallelizer; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.conversion.NumericalPropagatorBuilder; -import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; @@ -53,7 +52,7 @@ public class UnscentedKalmanModel implements KalmanEstimation, UnscentedProcess { /** Builders for propagators. */ - private final List builders; + private final List builders; /** Estimated orbital parameters. */ private final ParameterDriversList allEstimatedOrbitalParameters; @@ -92,7 +91,7 @@ public class UnscentedKalmanModel implements KalmanEstimation, UnscentedProcess< private final int[][] covarianceIndirection; /** Position angle types used during orbit determination. */ - private final PositionAngle[] angleTypes; + private final PositionAngleType[] angleTypes; /** Orbit types used during orbit determination. */ private final OrbitType[] orbitTypes; @@ -127,7 +126,7 @@ public class UnscentedKalmanModel implements KalmanEstimation, UnscentedProcess< * @param estimatedMeasurementParameters measurement parameters to estimate * @param measurementProcessNoiseMatrix provider for measurement process noise matrix */ - protected UnscentedKalmanModel(final List propagatorBuilders, + protected UnscentedKalmanModel(final List propagatorBuilders, final List covarianceMatrixProviders, final ParameterDriversList estimatedMeasurementParameters, final CovarianceMatrixProvider measurementProcessNoiseMatrix) { @@ -210,10 +209,10 @@ protected UnscentedKalmanModel(final List propagator } // set angle and orbit types - angleTypes = new PositionAngle[builders.size()]; + angleTypes = new PositionAngleType[builders.size()]; orbitTypes = new OrbitType[builders.size()]; for (int k = 0; k < builders.size(); k++) { - angleTypes[k] = builders.get(k).getPositionAngle(); + angleTypes[k] = builders.get(k).getPositionAngleType(); orbitTypes[k] = builders.get(k).getOrbitType(); } @@ -325,10 +324,6 @@ public UnscentedEvolution getEvolution(final double previousTime, final RealVect // Initialize arrays of predicted states and measurements final RealVector[] predictedStates = new RealVector[sigmaPoints.length]; - final RealVector[] predictedMeasurements = new RealVector[sigmaPoints.length]; - - // Initialize the relevant states used for measurement estimation - final SpacecraftState[][] statesForMeasurementEstimation = new SpacecraftState[sigmaPoints.length][builders.size()]; // Loop on builders for (int k = 0; k < builders.size(); k++ ) { @@ -353,18 +348,8 @@ public UnscentedEvolution getEvolution(final double previousTime, final RealVect final double[] predictedArray = new double[currentSigmaPoints[i].getDimension()]; orbitTypes[k].mapOrbitToArray(predicted.getOrbit(), angleTypes[k], predictedArray, null); predictedStates[i].setSubVector(orbitsStartColumns[k], new ArrayRealVector(predictedArray)); - // One has the same information in predictedStates - // and statesForMeasurementEstimation but differently arranged - statesForMeasurementEstimation[i][k] = predicted; } - } - // Loop on sigma points to predict measurements - for (int i = 0; i < sigmaPoints.length; i++) { - final EstimatedMeasurement estimated = observedMeasurement.estimate(currentMeasurementNumber, currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(observedMeasurement, - statesForMeasurementEstimation[i])); - predictedMeasurements[i] = new ArrayRealVector(estimated.getEstimatedValue()); } // compute process noise matrix @@ -414,7 +399,46 @@ public UnscentedEvolution getEvolution(final double previousTime, final RealVect previousDate = currentDate; // Return - return new UnscentedEvolution(measurement.getTime(), predictedStates, predictedMeasurements, processNoise); + return new UnscentedEvolution(measurement.getTime(), predictedStates, processNoise); + } + + /** {@inheritDoc} */ + @Override + public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPoints, final MeasurementDecorator measurement) { + + // Observed measurement + final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); + + // Initialize arrays of predicted states and measurements + final RealVector[] predictedMeasurements = new RealVector[predictedSigmaPoints.length]; + + // Initialize the relevant states used for measurement estimation + final SpacecraftState[][] statesForMeasurementEstimation = new SpacecraftState[predictedSigmaPoints.length][builders.size()]; + + // Convert sigma points to spacecraft states + for (int k = 0; k < builders.size(); k++ ) { + + // Loop on sigma points + for (int i = 0; i < predictedSigmaPoints.length; i++) { + final SpacecraftState state = new SpacecraftState(orbitTypes[k].mapArrayToOrbit(predictedSigmaPoints[i].toArray(), null, + angleTypes[k], currentDate, builders.get(k).getMu(), + builders.get(k).getFrame())); + statesForMeasurementEstimation[i][k] = state; + } + + } + + // Loop on sigma points to predict measurements + for (int i = 0; i < predictedSigmaPoints.length; i++) { + final EstimatedMeasurement estimated = observedMeasurement.estimate(currentMeasurementNumber, currentMeasurementNumber, + KalmanEstimatorUtil.filterRelevant(observedMeasurement, + statesForMeasurementEstimation[i])); + predictedMeasurements[i] = new ArrayRealVector(estimated.getEstimatedValue()); + } + + // Return the predicted measurements + return predictedMeasurements; + } /** {@inheritDoc} */ @@ -487,13 +511,13 @@ private List predictStates(final RealVector[] sigmaPoints, fina */ private Propagator createPropagator(final double[] point, final int index) { // Create a new instance of the current propagator builder - final NumericalPropagatorBuilder copy = builders.get(index).copy(); + final PropagatorBuilder copy = builders.get(index).copy(); // Convert the given sigma point to an orbit final Orbit orbit = orbitTypes[index].mapArrayToOrbit(point, null, angleTypes[index], copy.getInitialOrbitDate(), copy.getMu(), copy.getFrame()); copy.resetOrbit(orbit); // Create the propagator - final NumericalPropagator propagator = copy.buildPropagator(copy.getSelectedNormalizedParameters()); + final Propagator propagator = copy.buildPropagator(copy.getSelectedNormalizedParameters()); return propagator; } diff --git a/src/main/java/org/orekit/estimation/sequential/package-info.java b/src/main/java/org/orekit/estimation/sequential/package-info.java index 38e8702b69..8270abd5c5 100644 --- a/src/main/java/org/orekit/estimation/sequential/package-info.java +++ b/src/main/java/org/orekit/estimation/sequential/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/AdMethodType.java b/src/main/java/org/orekit/files/ccsds/definitions/AdMethodType.java new file mode 100644 index 0000000000..2cb4a79c63 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/definitions/AdMethodType.java @@ -0,0 +1,45 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.definitions; + + +/** Type of attitude determination method used in CCSDS + * {@link org.orekit.files.ccsds.ndm.adm.acm.Acm Attitude Comprehensive Messages}. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AdMethodType { + + /** Extended Kalman Filter. */ + EKF, + + /** TRIaxial Attitude Determination. */ + TRIAD, + + /** QUaternion ESTimator. */ + QUEST, + + /** Batch least squares. */ + BATCH, + + /** Q-method. */ + Q_METHOD, + + /** Method smoothing noisy processes. */ + FILTER_SMOOTHER; + +} diff --git a/src/main/java/org/orekit/files/ccsds/definitions/BodyFacade.java b/src/main/java/org/orekit/files/ccsds/definitions/BodyFacade.java index ffbe828a5b..5d4e3f126e 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/BodyFacade.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/BodyFacade.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.files.ccsds.definitions; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.CelestialBodies; import org.orekit.bodies.CelestialBody; import org.orekit.data.DataContext; @@ -66,7 +67,31 @@ public CelestialBody getBody() { */ @DefaultDataContext public static BodyFacade create(final CenterName centerName) { - return new BodyFacade(centerName.name(), centerName.getCelestialBody()); + return create(centerName, DataContext.getDefault()); + } + + /** + * Create a body facade from an input center name. + * + * @param centerName input center name + * @param context data context + * @return a body facade corresponding to the input center name + * @since 12.0 + */ + public static BodyFacade create(final CenterName centerName, final DataContext context) { + return create(centerName, context.getCelestialBodies()); + } + + /** + * Create a body facade from an input center name. + * + * @param centerName input center name + * @param bodies celestial bodies + * @return a body facade corresponding to the input center name + * @since 12.0 + */ + public static BodyFacade create(final CenterName centerName, final CelestialBodies bodies) { + return new BodyFacade(centerName.name(), centerName.getCelestialBody(bodies)); } } diff --git a/src/main/java/org/orekit/files/ccsds/definitions/CelestialBodyFrame.java b/src/main/java/org/orekit/files/ccsds/definitions/CelestialBodyFrame.java index 81be5da936..904ddb2610 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/CelestialBodyFrame.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/CelestialBodyFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -120,6 +120,19 @@ public Frame getFrame(final IERSConventions conventions, }, + /** Latest International Terrestrial Reference Frame. */ + ITRF { + + /** {@inheritDoc} */ + @Override + public Frame getFrame(final IERSConventions conventions, + final boolean simpleEOP, + final DataContext dataContext) { + return ITRF2020.getFrame(conventions, simpleEOP, dataContext); + } + + }, + /** International Terrestrial Reference Frame 2020. */ ITRF2020 { @@ -203,12 +216,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1997. */ ITRF1997 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-97"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -225,12 +232,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1996. */ ITRF1996 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-96"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -247,12 +248,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1994. */ ITRF1994 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-94"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -269,12 +264,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1993. */ ITRF1993 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-93"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -291,12 +280,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1992. */ ITRF1992 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-92"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -313,12 +296,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1991. */ ITRF1991 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-91"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -335,12 +312,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1990. */ ITRF1990 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-90"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -357,12 +328,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1989. */ ITRF1989 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-89"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, @@ -379,12 +344,6 @@ public Frame getFrame(final IERSConventions conventions, /** International Terrestrial Reference Frame 1988. */ ITRF1988 { - /** {@inheritDoc} */ - @Override - public String getName() { - return "ITRF-88"; - } - /** {@inheritDoc} */ @Override public Frame getFrame(final IERSConventions conventions, diff --git a/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java b/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java index bc17e24bc5..7cd1c39700 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/CenterName.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/DutyCycleType.java b/src/main/java/org/orekit/files/ccsds/definitions/DutyCycleType.java index bf597d55f2..9c3d7ed9ce 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/DutyCycleType.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/DutyCycleType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/FrameFacade.java b/src/main/java/org/orekit/files/ccsds/definitions/FrameFacade.java index 916d1e35e1..32f49a3bf5 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/FrameFacade.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/FrameFacade.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/OdMethodFacade.java b/src/main/java/org/orekit/files/ccsds/definitions/OdMethodFacade.java index c735315e0c..1574860831 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/OdMethodFacade.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/OdMethodFacade.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/OdMethodType.java b/src/main/java/org/orekit/files/ccsds/definitions/OdMethodType.java index ffcb4a55c2..6ea0efdfd5 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/OdMethodType.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/OdMethodType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,9 @@ */ package org.orekit.files.ccsds.definitions; -import org.orekit.files.ccsds.ndm.odm.ocm.Ocm; -/** Type of orbit determination method used in CCSDS {@link Ocm Orbit Comprehensive Messages}. +/** Type of orbit determination method used in CCSDS + * {@link org.orekit.files.ccsds.ndm.odm.ocm.Ocm Orbit Comprehensive Messages}. * @author Luc Maisonobe * @since 11.0 */ diff --git a/src/main/java/org/orekit/files/ccsds/definitions/OnOff.java b/src/main/java/org/orekit/files/ccsds/definitions/OnOff.java index eb492c8943..35ba856c4d 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/OnOff.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/OnOff.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/OrbitRelativeFrame.java b/src/main/java/org/orekit/files/ccsds/definitions/OrbitRelativeFrame.java index f79fcca44c..f47f96416d 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/OrbitRelativeFrame.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/OrbitRelativeFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,7 +31,7 @@ public enum OrbitRelativeFrame { LVLH_ROTATING(LOFType.LVLH_CCSDS, false), /** Local vertical, Local Horizontal (Z towards nadir, Y opposite to momentum). */ - LVLH_INERTIAL(LOFType.LVLH_CCSDS, true), + LVLH_INERTIAL(LOFType.LVLH_CCSDS_INERTIAL, true), /** Local vertical, Local Horizontal (Z towards nadir, Y opposite to momentum). */ LVLH(LOFType.LVLH_CCSDS, false), @@ -46,7 +46,7 @@ public enum OrbitRelativeFrame { NTW_ROTATING(LOFType.NTW, false), /** Transverse Velocity Normal coordinate system (Y towards velocity, Z towards momentum). */ - NTW_INERTIAL(LOFType.NTW, true), + NTW_INERTIAL(LOFType.NTW_INERTIAL, true), /** Perifocal coordinate system (X towards periapsis, Z towards momentum). */ PQW_INERTIAL(null, true), @@ -55,7 +55,7 @@ public enum OrbitRelativeFrame { RSW_ROTATING(LOFType.QSW, false), /** Another name for Radial, Transverse (along-track) and Normal (X towards zenith, Z towards momentum). */ - RSW_INERTIAL(LOFType.QSW, true), + RSW_INERTIAL(LOFType.QSW_INERTIAL, true), /** Another name for Radial, Transverse (along-track) and Normal. */ RSW(LOFType.QSW, false), @@ -73,10 +73,10 @@ public enum OrbitRelativeFrame { TNW_ROTATING(LOFType.TNW, false), /** Tangential, Normal, Cross-track coordinate system (X towards velocity, Z towards momentum). */ - TNW_INERTIAL(LOFType.TNW, true), + TNW_INERTIAL(LOFType.TNW_INERTIAL, true), /** TNW : x-axis along the velocity vector, W along the orbital angular momentum vector and - N completes the right handed system. */ + N completes the right-handed system. */ TNW(LOFType.TNW, false), /** South, East, Zenith coordinate system. */ @@ -89,9 +89,9 @@ public enum OrbitRelativeFrame { VNC_ROTATING(LOFType.VNC, false), /** Velocity, Normal, Co-normal coordinate system (X towards velocity, Y towards momentum). */ - VNC_INERTIAL(LOFType.VNC, true); + VNC_INERTIAL(LOFType.VNC_INERTIAL, true); - /** Type of Local Orbital Frame (may be null). */ + /** Type of Local Orbital Frame (may-be null). */ private final LOFType lofType; /** Flag for inertial orientation. */ diff --git a/src/main/java/org/orekit/files/ccsds/definitions/PocMethodFacade.java b/src/main/java/org/orekit/files/ccsds/definitions/PocMethodFacade.java index df1468b2a1..5e74198b3d 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/PocMethodFacade.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/PocMethodFacade.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/PocMethodType.java b/src/main/java/org/orekit/files/ccsds/definitions/PocMethodType.java index a22be1f6fc..7dbe0cc74a 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/PocMethodType.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/PocMethodType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,58 +16,146 @@ */ package org.orekit.files.ccsds.definitions; +import org.orekit.ssa.collision.shorttermencounter.probability.twod.ShortTermEncounter2DPOCMethodType; import org.orekit.files.ccsds.ndm.cdm.Cdm; -/** Type of probability of collision method used in CCSDS {@link Cdm Conjunction Data Messages}. +/** + * Type of probability of collision method used in CCSDS {@link Cdm Conjunction Data Messages}. *

        * The list of available methods is available on the SANA. *

        - * @see SANA CDM Collision Probability Methods + * * @author Bryan Cazabonne + * @author Vincent Cucchietti + * @see SANA CDM Collision Probability Methods * @since 11.2 */ public enum PocMethodType { - /** Akella and Alfriend - 2000 method.*/ - AKELLAALFRIEND_2000, + /** Akella and Alfriend - 2000 method. */ + AKELLAALFRIEND_2000 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Alfano 2005 method. */ - ALFANO_2005, + ALFANO_2005 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return ShortTermEncounter2DPOCMethodType.ALFANO_2005; + } + }, /** Maximum conjunction probability method from Alfano. */ - ALFANO_MAX_PROBABILITY, + ALFANO_MAX_PROBABILITY { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Adjoining parallelepipeds method from Alfano. */ - ALFANO_PARAL_2007, + ALFANO_PARAL_2007 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Adjoining tubes method from Alfano. */ - ALFANO_TUBES_2007, + ALFANO_TUBES_2007 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Voxels method from Alfano. */ - ALFANO_VOXELS_2006, + ALFANO_VOXELS_2006 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Alfriend 1999 method. */ - ALFRIEND_1999, + ALFRIEND_1999 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return ShortTermEncounter2DPOCMethodType.ALFRIEND_1999; + } + }, /** Chan 1997 method. */ - CHAN_1997, + CHAN_1997 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return ShortTermEncounter2DPOCMethodType.CHAN_1997; + } + }, /** Chan 2003 method. */ - CHAN_2003, + CHAN_2003 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Foster 1992 method. */ - FOSTER_1992, + FOSTER_1992 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** McKinley 2006 method. */ - MCKINLEY_2006, + MCKINLEY_2006 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Patera 2001 method. */ - PATERA_2001, + PATERA_2001 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Patera 2003 method. */ - PATERA_2003, + PATERA_2003 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return null; + } + }, /** Patera 2005 method. */ - PATERA_2005; + PATERA_2005 { + @Override + public ShortTermEncounter2DPOCMethodType getMethodType() { + return ShortTermEncounter2DPOCMethodType.PATERA_2005; + } + }; + + /** + * Get a probability of collision computing method type based on the short term encounter model. + * + * @return probability of collision computing method type based on the short term encounter model + */ + public abstract ShortTermEncounter2DPOCMethodType getMethodType(); + + /** Get CCSDS compatible name. + * @return CCSDS compatible name + */ + public String getCCSDSName() { + return this.name().replace("_", "-"); + } } diff --git a/src/main/java/org/orekit/files/ccsds/definitions/SpacecraftBodyFrame.java b/src/main/java/org/orekit/files/ccsds/definitions/SpacecraftBodyFrame.java index 218363b6a2..8efa9f56bf 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/SpacecraftBodyFrame.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/SpacecraftBodyFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -46,12 +46,12 @@ public enum BaseEquipment { /** Earth Sensor Assembly. */ ESA, - /** Gyro reference frame (this name was used in ADM V1.0 (CCSDS 504.0-B-1). */ - GYRO, - /** Gyro reference frame (this name is used in SANA registry https://sanaregistry.org/r/spacecraft_body_reference_frames/). */ GYRO_FRAME, + /** Gyro reference frame (this name was used in ADM V1.0 (CCSDS 504.0-B-1). */ + GYRO, + /** Inertial Measurement Unit. */ IMU_FRAME, @@ -119,7 +119,13 @@ public String getLabel() { */ @Override public String toString() { - return getBaseEquipment().name() + "_" + getLabel(); + // the names should normally have a form similar to SC_BODY_i + // however sometimes is is only SC_BODYi or even SC_BODY when parsed + // in the first case, we put the missing '_' back + // in the second case, we just keep the base equipment name + return getLabel().length() > 0 ? + getBaseEquipment().name() + "_" + getLabel() : + getBaseEquipment().name(); } /** Build an instance from a normalized descriptor. @@ -131,14 +137,16 @@ public String toString() { * @return parsed body frame */ public static SpacecraftBodyFrame parse(final String descriptor) { - final int separatorIndex = descriptor.lastIndexOf('_'); - if (separatorIndex >= 0) { - try { - final String equipmentName = descriptor.substring(0, separatorIndex); - return new SpacecraftBodyFrame(BaseEquipment.valueOf(equipmentName), - descriptor.substring(separatorIndex + 1)); - } catch (IllegalArgumentException iae) { - // ignored, errors are handled below + for (final BaseEquipment equipment : BaseEquipment.values()) { + if (descriptor.startsWith(equipment.name())) { + // the names should normally have a form similar to SC_BODY_i + // however sometimes is is only SC_BODYi or even SC_BODY + // so we try to parse these common cases + int index = equipment.name().length(); + if (index < descriptor.length() && descriptor.charAt(index) == '_') { + ++index; + } + return new SpacecraftBodyFrame(equipment, descriptor.substring(index)); } } throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, descriptor); diff --git a/src/main/java/org/orekit/files/ccsds/definitions/TimeConverter.java b/src/main/java/org/orekit/files/ccsds/definitions/TimeConverter.java index 7e3d9a21c5..aa19f0e18f 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/TimeConverter.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/TimeConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -93,4 +93,12 @@ public TimeScale getTimeScale() { return timeScale; } + /** Get the reference date for relative dates (may be null if no relative dates are used). + * @return reference date for relative dates (may be null if no relative dates are used) + * @since 12.0 + */ + public AbsoluteDate getReferenceDate() { + return referenceDate; + } + } diff --git a/src/main/java/org/orekit/files/ccsds/definitions/Units.java b/src/main/java/org/orekit/files/ccsds/definitions/Units.java index b108735080..bbd3437ce5 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/Units.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/Units.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -106,9 +106,20 @@ public class Units { /** Degree per second unit. */ public static final Unit DEG_PER_S = Unit.parse("°/s"); + /** Degree per second^3/2 unit. */ + public static final Unit DEG_PER_S_3_2 = Unit.parse("°/√(s³)"); + + /** Degree per second^1/2 unit. */ + public static final Unit DEG_PER_S_1_2 = Unit.parse("°/√s"); + /** Newton metre unit. */ public static final Unit N_M = Unit.parse("N.m"); + /** Newton metre second unit. + * @since 12.0 + */ + public static final Unit N_M_S = Unit.parse("N.m.s"); + /** Nano Tesla unit. */ public static final Unit NANO_TESLA = Unit.parse("nT"); diff --git a/src/main/java/org/orekit/files/ccsds/definitions/YesNoUnknown.java b/src/main/java/org/orekit/files/ccsds/definitions/YesNoUnknown.java index 14628aa784..6becbd8243 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/YesNoUnknown.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/YesNoUnknown.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/definitions/package-info.java b/src/main/java/org/orekit/files/ccsds/definitions/package-info.java index 4e73bb23ff..da4f77570a 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/definitions/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/AbstractBuilder.java b/src/main/java/org/orekit/files/ccsds/ndm/AbstractBuilder.java index b3a85fd353..2db200b014 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/AbstractBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/AbstractBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -37,6 +37,16 @@ public abstract class AbstractBuilder> { /** IERS conventions used. */ private final IERSConventions conventions; + /** Central body equatorial radius. + * @since 12.0 + */ + private final double equatorialRadius; + + /** Central body flattening. + * @since 12.0 + */ + private final double flattening; + /** Data context. */ private final DataContext dataContext; @@ -49,14 +59,20 @@ public abstract class AbstractBuilder> { /** * Complete constructor. * @param conventions IERS Conventions + * @param equatorialRadius central body equatorial radius + * @param flattening central body flattening * @param dataContext used to retrieve frames, time scales, etc. * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems * @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units} */ - protected AbstractBuilder(final IERSConventions conventions, final DataContext dataContext, + protected AbstractBuilder(final IERSConventions conventions, + final double equatorialRadius, final double flattening, + final DataContext dataContext, final AbsoluteDate missionReferenceDate, final RangeUnitsConverter rangeUnitsConverter) { this.conventions = conventions; + this.equatorialRadius = equatorialRadius; + this.flattening = flattening; this.dataContext = dataContext; this.missionReferenceDate = missionReferenceDate; this.rangeUnitsConverter = rangeUnitsConverter; @@ -64,12 +80,15 @@ protected AbstractBuilder(final IERSConventions conventions, final DataContext d /** Build an instance. * @param newConventions IERS Conventions + * @param newEquatorialRadius central body equatorial radius + * @param newFlattening central body flattening * @param newDataContext used to retrieve frames, time scales, etc. * @param newMissionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems * @param newRangeUnitsConverter converter for {@link RangeUnits#RU Range Units} * @return new instance */ - protected abstract T create(IERSConventions newConventions, DataContext newDataContext, + protected abstract T create(IERSConventions newConventions, double newEquatorialRadius, double newFlattening, + DataContext newDataContext, AbsoluteDate newMissionReferenceDate, RangeUnitsConverter newRangeUnitsConverter); /** Set up IERS conventions. @@ -77,7 +96,8 @@ protected abstract T create(IERSConventions newConventions, DataContext newDataC * @return a new builder with updated configuration (the instance is not changed) */ public T withConventions(final IERSConventions newConventions) { - return create(newConventions, getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter()); + return create(newConventions, getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter()); } /** Get the IERS conventions. @@ -87,12 +107,45 @@ public IERSConventions getConventions() { return conventions; } + /** Set up the central body equatorial radius. + * @param newEquatorialRadius central body equatorial radius + * @return a new builder with updated configuration (the instance is not changed) + */ + public T withEquatorialRadius(final double newEquatorialRadius) { + return create(getConventions(), newEquatorialRadius, getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter()); + } + + /** Get the central body equatorial radius. + * @return central body equatorial radius + */ + public double getEquatorialRadius() { + return equatorialRadius; + } + + /** Set up the central body flattening. + * @param newFlattening central body flattening + * @return a new builder with updated configuration (the instance is not changed) + */ + public T withFlattening(final double newFlattening) { + return create(getConventions(), getEquatorialRadius(), newFlattening, getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter()); + } + + /** Get the central body flattening. + * @return central body flattening + */ + public double getFlattening() { + return flattening; + } + /** Set up data context used to retrieve frames, time scales, etc.. * @param newDataContext data context used to retrieve frames, time scales, etc. * @return a new builder with updated configuration (the instance is not changed) */ public T withDataContext(final DataContext newDataContext) { - return create(getConventions(), newDataContext, getMissionReferenceDate(), getRangeUnitsConverter()); + return create(getConventions(), getEquatorialRadius(), getFlattening(), newDataContext, + getMissionReferenceDate(), getRangeUnitsConverter()); } /** Get the data context. @@ -113,7 +166,8 @@ public DataContext getDataContext() { * @return a new builder with updated configuration (the instance is not changed) */ public T withMissionReferenceDate(final AbsoluteDate newMissionReferenceDate) { - return create(getConventions(), getDataContext(), newMissionReferenceDate, getRangeUnitsConverter()); + return create(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + newMissionReferenceDate, getRangeUnitsConverter()); } /** Get the mission reference date or Mission Elapsed Time or Mission Relative Time time systems. @@ -128,7 +182,8 @@ public AbsoluteDate getMissionReferenceDate() { * @return a new builder with updated configuration (the instance is not changed) */ public T withRangeUnitsConverter(final RangeUnitsConverter newRangeUnitsConverter) { - return create(getConventions(), getDataContext(), getMissionReferenceDate(), newRangeUnitsConverter); + return create(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), newRangeUnitsConverter); } /** Get the converter for {@link RangeUnits#RU Range Units}. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/CommonPhysicalProperties.java b/src/main/java/org/orekit/files/ccsds/ndm/CommonPhysicalProperties.java index 7b742abad8..a145a317cd 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/CommonPhysicalProperties.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/CommonPhysicalProperties.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,11 +20,11 @@ import org.orekit.files.ccsds.definitions.FrameFacade; import org.orekit.files.ccsds.definitions.OrbitRelativeFrame; import org.orekit.files.ccsds.ndm.cdm.AdditionalParameters; -import org.orekit.files.ccsds.ndm.odm.ocm.PhysicalProperties; +import org.orekit.files.ccsds.ndm.odm.ocm.OrbitPhysicalProperties; import org.orekit.files.ccsds.section.CommentsContainer; import org.orekit.time.AbsoluteDate; -/** Container for common physical properties for both {@link PhysicalProperties} and {@link AdditionalParameters}. +/** Container for common physical properties for both {@link OrbitPhysicalProperties} and {@link AdditionalParameters}. * * @author Maxime Journot * @since 11.3 diff --git a/src/main/java/org/orekit/files/ccsds/ndm/Ndm.java b/src/main/java/org/orekit/files/ccsds/ndm/Ndm.java index fe1afac949..8ae1af9a37 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/Ndm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/Ndm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/NdmConstituent.java b/src/main/java/org/orekit/files/ccsds/ndm/NdmConstituent.java index e15e94038a..10a3a8855f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/NdmConstituent.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/NdmConstituent.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -39,10 +39,10 @@ public abstract class NdmConstituent> { /** Header. */ - private final H header; + private H header; /** segments list. */ - private final List segments; + private List segments; /** IERS conventions used. */ private final IERSConventions conventions; @@ -74,6 +74,14 @@ public H getHeader() { return header; } + /** + * Set the header. + * @param header the header + */ + public void setHeader(final H header) { + this.header = header; + } + /** * Get the segments. * @return segments @@ -83,6 +91,14 @@ public List getSegments() { return Collections.unmodifiableList(segments); } + /** + * Set the segments. + * @param segments the segments + */ + public void setSegments(final List segments) { + this.segments = segments; + } + /** * Get IERS conventions. * @return IERS conventions diff --git a/src/main/java/org/orekit/files/ccsds/ndm/NdmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/NdmParser.java index 97fad8cf7d..97d8792635 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/NdmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/NdmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.function.Supplier; import org.orekit.errors.OrekitException; @@ -54,9 +55,12 @@ public class NdmParser extends AbstractMessageParser { * parserBuilder.buildNdmParser()}. *

        * @param builder builder for the constituents parsers + * @param filters filters to apply to parse tokens + * @since 12.0 */ - public NdmParser(final ParserBuilder builder) { - super(NdmStructureKey.ndm.name(), null); + public NdmParser(final ParserBuilder builder, + final Function>[] filters) { + super(NdmStructureKey.ndm.name(), null, filters); this.builder = builder; } @@ -73,6 +77,7 @@ public Map getSpecialXmlElementsBuilders() { builders.putAll(builder.buildOcmParser().getSpecialXmlElementsBuilders()); builders.putAll(builder.buildApmParser().getSpecialXmlElementsBuilders()); builders.putAll(builder.buildAemParser().getSpecialXmlElementsBuilders()); + builders.putAll(builder.buildAcmParser().getSpecialXmlElementsBuilders()); builders.putAll(builder.buildCdmParser().getSpecialXmlElementsBuilders()); return builders; @@ -157,6 +162,14 @@ boolean manageAemConstituent() { return manageConstituent(builder::buildAemParser); } + /** Prepare parsing of a ACM constituent. + * @return always return true + * @since 12.0 + */ + boolean manageAcmConstituent() { + return manageConstituent(builder::buildAcmParser); + } + /** Prepare parsing of a CDM constituent. * @return always return true */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/NdmStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/NdmStructureKey.java index 47d65db016..fc746905fe 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/NdmStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/NdmStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -53,6 +53,9 @@ enum NdmStructureKey { /** AEM constituent. */ aem((token, parser) -> parser.manageAemConstituent()), + /** ACM constituent. */ + acm((token, parser) -> parser.manageAcmConstituent()), + /** CDM constituent. */ cdm((token, parser) -> parser.manageCdmConstituent()); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/NdmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/NdmWriter.java index caeb55258b..7014a5af17 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/NdmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/NdmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.ndm.adm.acm.Acm; import org.orekit.files.ccsds.ndm.adm.aem.Aem; import org.orekit.files.ccsds.ndm.adm.apm.Apm; import org.orekit.files.ccsds.ndm.odm.ocm.Ocm; @@ -166,6 +167,8 @@ MessageWriter buildWriter(final F constituent) throws IOException { return (MessageWriter) builder.buildApmWriter(); } else if (constituent instanceof Aem) { return (MessageWriter) builder.buildAemWriter(); + } else if (constituent instanceof Acm) { + return (MessageWriter) builder.buildAcmWriter(); } else { // this should never happen throw new OrekitInternalError(null); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/ParsedUnitsBehavior.java b/src/main/java/org/orekit/files/ccsds/ndm/ParsedUnitsBehavior.java index fce0cd0ffa..337fba86c4 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/ParsedUnitsBehavior.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/ParsedUnitsBehavior.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/ParserBuilder.java b/src/main/java/org/orekit/files/ccsds/ndm/ParserBuilder.java index e96d859e66..e11a45b4f9 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/ParserBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/ParserBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,13 @@ */ package org.orekit.files.ccsds.ndm; +import java.lang.reflect.Array; +import java.util.List; +import java.util.function.Function; + import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; +import org.orekit.files.ccsds.ndm.adm.acm.AcmParser; import org.orekit.files.ccsds.ndm.adm.aem.AemParser; import org.orekit.files.ccsds.ndm.adm.apm.ApmParser; import org.orekit.files.ccsds.ndm.cdm.CdmParser; @@ -29,6 +34,7 @@ import org.orekit.files.ccsds.ndm.tdm.RangeUnits; import org.orekit.files.ccsds.ndm.tdm.RangeUnitsConverter; import org.orekit.files.ccsds.ndm.tdm.TdmParser; +import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.time.AbsoluteDate; import org.orekit.utils.IERSConventions; @@ -59,6 +65,11 @@ public class ParserBuilder extends AbstractBuilder { /** Behavior adopted for units that have been parsed from a CCSDS message. */ private final ParsedUnitsBehavior parsedUnitsBehavior; + /** Filters for parse tokens. + * @since 12.0 + */ + private final Function>[] filters; + /** * Simple constructor. *

        @@ -69,6 +80,8 @@ public class ParserBuilder extends AbstractBuilder { *

      1. {@link #getDataContext() data context} set to {@link DataContext#getDefault() default context}
      2. *
      3. {@link #getMissionReferenceDate() mission reference date} set to {@code null}
      4. *
      5. {@link #getMu() gravitational coefficient} set to {@code Double.NaN}
      6. + *
      7. {@link #getEquatorialRadius() central body equatorial radius} set to {@code Double.NaN}
      8. + *
      9. {@link #getFlattening() central body flattening} set to {@code Double.NaN}
      10. *
      11. {@link #getDefaultMass() default mass} set to {@code Double.NaN}
      12. *
      13. {@link #getDefaultInterpolationDegree() default interpolation degree} set to {@code 1}
      14. *
      15. {@link #getParsedUnitsBehavior() parsed unit behavior} set to {@link ParsedUnitsBehavior#CONVERT_COMPATIBLE}
      16. @@ -89,6 +102,8 @@ public ParserBuilder() { *
      17. {@link #isSimpleEOP() simple EOP} set to {@code true}
      18. *
      19. {@link #getMissionReferenceDate() mission reference date} set to {@code null}
      20. *
      21. {@link #getMu() gravitational coefficient} set to {@code Double.NaN}
      22. + *
      23. {@link #getEquatorialRadius() central body equatorial radius} set to {@code Double.NaN}
      24. + *
      25. {@link #getFlattening() central body flattening} set to {@code Double.NaN}
      26. *
      27. {@link #getDefaultMass() default mass} set to {@code Double.NaN}
      28. *
      29. {@link #getDefaultInterpolationDegree() default interpolation degree} set to {@code 1}
      30. *
      31. {@link #getParsedUnitsBehavior() parsed unit behavior} set to {@link ParsedUnitsBehavior#CONVERT_COMPATIBLE}
      32. @@ -96,42 +111,55 @@ public ParserBuilder() { * * @param dataContext data context used to retrieve frames, time scales, etc. */ + @SuppressWarnings("unchecked") public ParserBuilder(final DataContext dataContext) { - this(IERSConventions.IERS_2010, dataContext, null, new IdentityConverter(), - true, Double.NaN, Double.NaN, 1, ParsedUnitsBehavior.CONVERT_COMPATIBLE); + this(IERSConventions.IERS_2010, Double.NaN, Double.NaN, dataContext, + null, new IdentityConverter(), true, Double.NaN, Double.NaN, + 1, ParsedUnitsBehavior.CONVERT_COMPATIBLE, + (Function>[]) Array.newInstance(Function.class, 0)); } /** Complete constructor. * @param conventions IERS Conventions + * @param equatorialRadius central body equatorial radius + * @param flattening central body flattening * @param dataContext used to retrieve frames, time scales, etc. * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems + * @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units} * @param simpleEOP if true, tidal effects are ignored when interpolating EOP * @param mu gravitational coefficient * @param defaultMass default mass * @param defaultInterpolationDegree default interpolation degree * @param parsedUnitsBehavior behavior to adopt for handling parsed units - * @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units} + * @param filters filters to apply to parse tokens + * @since 12.0 */ - private ParserBuilder(final IERSConventions conventions, final DataContext dataContext, - final AbsoluteDate missionReferenceDate, + private ParserBuilder(final IERSConventions conventions, + final double equatorialRadius, final double flattening, + final DataContext dataContext, final AbsoluteDate missionReferenceDate, final RangeUnitsConverter rangeUnitsConverter, - final boolean simpleEOP, final double mu, final double defaultMass, - final int defaultInterpolationDegree, - final ParsedUnitsBehavior parsedUnitsBehavior) { - super(conventions, dataContext, missionReferenceDate, rangeUnitsConverter); + final boolean simpleEOP, final double mu, + final double defaultMass, final int defaultInterpolationDegree, + final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(conventions, equatorialRadius, flattening, dataContext, missionReferenceDate, rangeUnitsConverter); this.simpleEOP = simpleEOP; this.mu = mu; this.defaultMass = defaultMass; this.defaultInterpolationDegree = defaultInterpolationDegree; this.parsedUnitsBehavior = parsedUnitsBehavior; + this.filters = filters.clone(); } /** {@inheritDoc} */ @Override - protected ParserBuilder create(final IERSConventions newConventions, final DataContext newDataContext, + protected ParserBuilder create(final IERSConventions newConventions, + final double newEquatorialRadius, final double newFlattening, + final DataContext newDataContext, final AbsoluteDate newMissionReferenceDate, final RangeUnitsConverter newRangeUnitsConverter) { - return new ParserBuilder(newConventions, newDataContext, newMissionReferenceDate, newRangeUnitsConverter, - simpleEOP, mu, defaultMass, defaultInterpolationDegree, parsedUnitsBehavior); + return new ParserBuilder(newConventions, newEquatorialRadius, newFlattening, newDataContext, + newMissionReferenceDate, newRangeUnitsConverter, simpleEOP, mu, + defaultMass, defaultInterpolationDegree, parsedUnitsBehavior, filters); } /** Set up flag for ignoring tidal effects when interpolating EOP. @@ -139,9 +167,9 @@ protected ParserBuilder create(final IERSConventions newConventions, final DataC * @return a new builder with updated configuration (the instance is not changed) */ public ParserBuilder withSimpleEOP(final boolean newSimpleEOP) { - return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(), - newSimpleEOP, getMu(), getDefaultMass(), - getDefaultInterpolationDegree(), getParsedUnitsBehavior()); + return new ParserBuilder(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter(), newSimpleEOP, getMu(), getDefaultMass(), + getDefaultInterpolationDegree(), getParsedUnitsBehavior(), getFilters()); } /** Check if tidal effects are ignored when interpolating EOP. @@ -156,9 +184,9 @@ public boolean isSimpleEOP() { * @return a new builder with updated configuration (the instance is not changed) */ public ParserBuilder withMu(final double newMu) { - return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(), - isSimpleEOP(), newMu, getDefaultMass(), - getDefaultInterpolationDegree(), getParsedUnitsBehavior()); + return new ParserBuilder(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter(), isSimpleEOP(), newMu, getDefaultMass(), + getDefaultInterpolationDegree(), getParsedUnitsBehavior(), getFilters()); } /** Get the gravitational coefficient. @@ -176,9 +204,9 @@ public double getMu() { * @return a new builder with updated configuration (the instance is not changed) */ public ParserBuilder withDefaultMass(final double newDefaultMass) { - return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(), - isSimpleEOP(), getMu(), newDefaultMass, - getDefaultInterpolationDegree(), getParsedUnitsBehavior()); + return new ParserBuilder(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter(), isSimpleEOP(), getMu(), newDefaultMass, + getDefaultInterpolationDegree(), getParsedUnitsBehavior(), getFilters()); } /** Get the default mass. @@ -197,9 +225,9 @@ public double getDefaultMass() { * @return a new builder with updated configuration (the instance is not changed) */ public ParserBuilder withDefaultInterpolationDegree(final int newDefaultInterpolationDegree) { - return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(), - isSimpleEOP(), getMu(), getDefaultMass(), - newDefaultInterpolationDegree, getParsedUnitsBehavior()); + return new ParserBuilder(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter(), isSimpleEOP(), getMu(), getDefaultMass(), + newDefaultInterpolationDegree, getParsedUnitsBehavior(), getFilters()); } /** Get the default interpolation degree. @@ -214,9 +242,9 @@ public int getDefaultInterpolationDegree() { * @return a new builder with updated configuration (the instance is not changed) */ public ParserBuilder withParsedUnitsBehavior(final ParsedUnitsBehavior newParsedUnitsBehavior) { - return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(), - isSimpleEOP(), getMu(), getDefaultMass(), - getDefaultInterpolationDegree(), newParsedUnitsBehavior); + return new ParserBuilder(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter(), isSimpleEOP(), getMu(), getDefaultMass(), + getDefaultInterpolationDegree(), newParsedUnitsBehavior, getFilters()); } /** Get the behavior to adopt for handling parsed units. @@ -226,11 +254,114 @@ public ParsedUnitsBehavior getParsedUnitsBehavior() { return parsedUnitsBehavior; } + /** Add a filter for parsed tokens. + *

        + * This filter allows to change parsed tokens. This method can be called several times, + * once for each filter to set up. The filters are always applied in the order they were set. + * There are several use cases for this feature. + *

        + *

        + * The first use case is to allow parsing malformed CCSDS messages with some known + * discrepancies that can be fixed. One real life example (the one that motivated the + * development of this feature) is OMM files in XML format that add an empty + * OBJECT_ID. This could be fixed by setting a filter as follows: + *

        + *
        {@code
        +     * Omm omm = new ParserBuilder().
        +     *           withFilter(token -> {
        +     *                          if ("OBJECT_ID".equals(token.getName()) &&
        +     *                              (token.getRawContent() == null || token.getRawContent().isEmpty())) {
        +     *                              // replace null/empty entries with "unknown"
        +     *                              return Collections.singletonList(new ParseToken(token.getType(), token.getName(),
        +     *                                                                              "unknown", token.getUnits(),
        +     *                                                                              token.getLineNumber(), token.getFileName()));
        +     *                          } else {
        +     *                              return Collections.singletonList(token);
        +     *                          }
        +     *                     }).
        +     *           buildOmmParser().
        +     *           parseMessage(message);
        +     * }
        + *

        + * A second use case is to remove unwanted data. For example in order to remove all user-defined data + * one could use: + *

        + *
        {@code
        +     * Omm omm = new ParserBuilder().
        +     *           withFilter(token -> {
        +     *                          if (token.getName().startsWith("USER_DEFINED")) {
        +     *                              return Collections.emptyList();
        +     *                          } else {
        +     *                              return Collections.singletonList(token);
        +     *                          }
        +     *                     }).
        +     *           buildOmmmParser().
        +     *           parseMessage(message);
        +     * }
        + *

        + * A third use case is to add data not originally present in the file. For example in order + * to add a generated ODM V3 message id to an ODM V2 message that lacks it, one could do: + *

        + *
        {@code
        +     * final String myMessageId = ...; // this could be computed from a counter, or a SHA256 digest, or some metadata
        +     * Omm omm = new ParserBuilder()
        +     *           withFilter(token -> {
        +     *                          if ("CCSDS_OMM_VERS".equals(token.getName())) {
        +     *                              // enforce ODM V3
        +     *                              return Collections.singletonList(new ParseToken(token.getType(), token.getName(),
        +     *                                                                              "3.0", token.getUnits(),
        +     *                                                                              token.getLineNumber(), token.getFileName()));
        +     *                          } else {
        +     *                              return Collections.singletonList(token);
        +     *                          }
        +     *                      }).
        +     *           withFilter(token -> {
        +     *                          if ("ORIGINATOR".equals(token.getName())) {
        +     *                              // add generated message ID after ORIGINATOR entry
        +     *                              return Arrays.asList(token,
        +     *                                                   new ParseToken(TokenType.ENTRY, "MESSAGE_ID",
        +     *                                                                  myMessageId, null,
        +     *                                                                  -1, token.getFileName()));
        +     *                          } else {
        +     *                              return Collections.singletonList(token);
        +     *                          }
        +     *                      }).
        +     *           buildOmmmParser().
        +     *           parseMessage(message);
        +     * }
        + * @param filter token filter to add + * @return a new builder with updated configuration (the instance is not changed) + * @since 12.0 + */ + public ParserBuilder withFilter(final Function> filter) { + + // populate new filters array + @SuppressWarnings("unchecked") + final Function>[] newFilters = + (Function>[]) Array.newInstance(Function.class, filters.length + 1); + System.arraycopy(filters, 0, newFilters, 0, filters.length); + newFilters[filters.length] = filter; + + return new ParserBuilder(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext(), + getMissionReferenceDate(), getRangeUnitsConverter(), isSimpleEOP(), getMu(), getDefaultMass(), + getDefaultInterpolationDegree(), getParsedUnitsBehavior(), + newFilters); + + } + + /** Get the filters to apply to parse tokens. + * @return filters to apply to parse tokens + * @since 12.0 + */ + public Function>[] getFilters() { + return filters.clone(); + } + /** Build a parser for {@link org.orekit.files.ccsds.ndm.Ndm Navigation Data Messages}. * @return a new parser */ public NdmParser buildNdmParser() { - return new NdmParser(this); + return new NdmParser(this, getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.opm.Opm Orbit Parameters Messages}. @@ -238,7 +369,7 @@ public NdmParser buildNdmParser() { */ public OpmParser buildOpmParser() { return new OpmParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(), - getMu(), getDefaultMass(), getParsedUnitsBehavior()); + getMu(), getDefaultMass(), getParsedUnitsBehavior(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.omm.Omm Orbit Mean elements Messages}. @@ -246,7 +377,7 @@ public OpmParser buildOpmParser() { */ public OmmParser buildOmmParser() { return new OmmParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(), - getMu(), getDefaultMass(), getParsedUnitsBehavior()); + getMu(), getDefaultMass(), getParsedUnitsBehavior(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.oem.Oem Orbit Ephemeris Messages}. @@ -254,14 +385,16 @@ public OmmParser buildOmmParser() { */ public OemParser buildOemParser() { return new OemParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(), - getMu(), getDefaultInterpolationDegree(), getParsedUnitsBehavior()); + getMu(), getDefaultInterpolationDegree(), getParsedUnitsBehavior(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.ocm.Ocm Orbit Comprehensive Messages}. * @return a new parser */ public OcmParser buildOcmParser() { - return new OcmParser(getConventions(), isSimpleEOP(), getDataContext(), getMu(), getParsedUnitsBehavior()); + return new OcmParser(getConventions(), getEquatorialRadius(), getFlattening(), + isSimpleEOP(), getDataContext(), getMu(), + getParsedUnitsBehavior(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.adm.apm.Apm Attitude Parameters Messages}. @@ -269,7 +402,7 @@ public OcmParser buildOcmParser() { */ public ApmParser buildApmParser() { return new ApmParser(getConventions(), isSimpleEOP(), getDataContext(), - getMissionReferenceDate(), getParsedUnitsBehavior()); + getMissionReferenceDate(), getParsedUnitsBehavior(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.adm.aem.Aem Attitude Ephemeris Messages}. @@ -277,7 +410,16 @@ public ApmParser buildApmParser() { */ public AemParser buildAemParser() { return new AemParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(), - getDefaultInterpolationDegree(), getParsedUnitsBehavior()); + getDefaultInterpolationDegree(), getParsedUnitsBehavior(), getFilters()); + } + + /** Build a parser for {@link org.orekit.files.ccsds.ndm.adm.acm.Acm Attitude Comprehensive Messages}. + * @return a new parser + * @since 12.0 + */ + public AcmParser buildAcmParser() { + return new AcmParser(getConventions(), isSimpleEOP(), getDataContext(), + getParsedUnitsBehavior(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.tdm.Tdm Tracking Data Messages}. @@ -285,7 +427,7 @@ public AemParser buildAemParser() { */ public TdmParser buildTdmParser() { return new TdmParser(getConventions(), isSimpleEOP(), getDataContext(), - getParsedUnitsBehavior(), getRangeUnitsConverter()); + getParsedUnitsBehavior(), getRangeUnitsConverter(), getFilters()); } /** Build a parser for {@link org.orekit.files.ccsds.ndm.cdm.Cdm Conjunction Data Messages}. @@ -293,7 +435,7 @@ public TdmParser buildTdmParser() { */ public CdmParser buildCdmParser() { return new CdmParser(getConventions(), isSimpleEOP(), getDataContext(), - getParsedUnitsBehavior()); + getParsedUnitsBehavior(), getFilters()); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/WriterBuilder.java b/src/main/java/org/orekit/files/ccsds/ndm/WriterBuilder.java index a5ffff4c7d..ca7fcbacf0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/WriterBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/WriterBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; +import org.orekit.files.ccsds.ndm.adm.acm.AcmWriter; import org.orekit.files.ccsds.ndm.adm.aem.AemWriter; import org.orekit.files.ccsds.ndm.adm.apm.ApmWriter; import org.orekit.files.ccsds.ndm.cdm.CdmWriter; @@ -50,6 +51,8 @@ public class WriterBuilder extends AbstractBuilder { * This constructor creates a builder with *
          *
        • {@link #getConventions() IERS conventions} set to {@link IERSConventions#IERS_2010}
        • + *
        • {@link #getEquatorialRadius() central body equatorial radius} set to {@code Double.NaN}
        • + *
        • {@link #getFlattening() central body flattening} set to {@code Double.NaN}
        • *
        • {@link #getDataContext() data context} set to {@link DataContext#getDefault() default context}
        • *
        • {@link #getMissionReferenceDate() mission reference date} set to {@code null}
        • *
        • {@link #getRangeUnitsConverter() converter for range units} set to {@link IdentityConverter}
        • @@ -66,31 +69,40 @@ public WriterBuilder() { * This constructor creates a builder with *
            *
          • {@link #getConventions() IERS conventions} set to {@link IERSConventions#IERS_2010}
          • + *
          • {@link #getEquatorialRadius() central body equatorial radius} set to {@code Double.NaN}
          • + *
          • {@link #getFlattening() central body flattening} set to {@code Double.NaN}
          • *
          • {@link #getMissionReferenceDate() mission reference date} set to {@code null}
          • *
          • {@link #getRangeUnitsConverter() converter for range units} set to {@link IdentityConverter}
          • *
          * @param dataContext data context used to retrieve frames, time scales, etc. */ public WriterBuilder(final DataContext dataContext) { - this(IERSConventions.IERS_2010, dataContext, null, new IdentityConverter()); + this(IERSConventions.IERS_2010, Double.NaN, Double.NaN, dataContext, null, new IdentityConverter()); } /** Complete constructor. * @param conventions IERS Conventions + * @param equatorialRadius central body equatorial radius + * @param flattening central body flattening * @param dataContext used to retrieve frames, time scales, etc. * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems * @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units} */ - private WriterBuilder(final IERSConventions conventions, final DataContext dataContext, + private WriterBuilder(final IERSConventions conventions, + final double equatorialRadius, final double flattening, + final DataContext dataContext, final AbsoluteDate missionReferenceDate, final RangeUnitsConverter rangeUnitsConverter) { - super(conventions, dataContext, missionReferenceDate, rangeUnitsConverter); + super(conventions, equatorialRadius, flattening, dataContext, missionReferenceDate, rangeUnitsConverter); } /** {@inheritDoc} */ @Override - protected WriterBuilder create(final IERSConventions newConventions, final DataContext newDataContext, + protected WriterBuilder create(final IERSConventions newConventions, + final double newEquatorialRadius, final double newFlattening, + final DataContext newDataContext, final AbsoluteDate newMissionReferenceDate, final RangeUnitsConverter newRangeUnitsConverter) { - return new WriterBuilder(newConventions, newDataContext, newMissionReferenceDate, newRangeUnitsConverter); + return new WriterBuilder(newConventions, newEquatorialRadius, newFlattening, newDataContext, + newMissionReferenceDate, newRangeUnitsConverter); } /** Build a writer for {@link org.orekit.files.ccsds.ndm.Ndm Navigation Data Messages}. @@ -125,7 +137,7 @@ public OemWriter buildOemWriter() { * @return a new writer */ public OcmWriter buildOcmWriter() { - return new OcmWriter(getConventions(), getDataContext()); + return new OcmWriter(getConventions(), getEquatorialRadius(), getFlattening(), getDataContext()); } /** Build a writer for {@link org.orekit.files.ccsds.ndm.adm.apm.Apm Attitude Parameters Messages}. @@ -142,6 +154,14 @@ public AemWriter buildAemWriter() { return new AemWriter(getConventions(), getDataContext(), getMissionReferenceDate()); } + /** Build a writer for {@link org.orekit.files.ccsds.ndm.adm.acm.Acm Attitude Comprehensive Messages}. + * @return a new writer + * @since 12.0 + */ + public AcmWriter buildAcmWriter() { + return new AcmWriter(getConventions(), getDataContext()); + } + /** Build a writer for {@link org.orekit.files.ccsds.ndm.tdm.Tdm Tracking Data Messages}. * @return a new writer */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmCommonMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmCommonMetadataKey.java new file mode 100644 index 0000000000..6996f76651 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmCommonMetadataKey.java @@ -0,0 +1,63 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm; + +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; + + +/** Keys for {@link AdmMetadata APM and AEM} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AdmCommonMetadataKey { + + /** Object ID entry. */ + OBJECT_ID((token, context, container) -> token.processAsUppercaseString(container::setObjectID)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AdmCommonMetadataKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process one token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final AdmMetadata container) { + return processor.process(token, context, container); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AdmMetadata container); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmCommonMetadataWriter.java similarity index 78% rename from src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataWriter.java rename to src/main/java/org/orekit/files/ccsds/ndm/adm/AdmCommonMetadataWriter.java index 4240f4b8d9..c657889719 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmCommonMetadataWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,7 +29,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class AdmMetadataWriter extends AbstractWriter { +public class AdmCommonMetadataWriter extends AbstractWriter { /** Metadata. */ private final AdmMetadata metadata; @@ -37,7 +37,7 @@ public class AdmMetadataWriter extends AbstractWriter { /** Simple constructor. * @param metadata metadata to write */ - public AdmMetadataWriter(final AdmMetadata metadata) { + public AdmCommonMetadataWriter(final AdmMetadata metadata) { super(XmlStructureKey.metadata.name(), null); this.metadata = metadata; } @@ -49,11 +49,13 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeComments(metadata.getComments()); // object - generator.writeEntry(AdmMetadataKey.OBJECT_NAME.name(), metadata.getObjectName(), null, true); - generator.writeEntry(AdmMetadataKey.OBJECT_ID.name(), metadata.getObjectID(), null, true); + generator.writeEntry(AdmMetadataKey.OBJECT_NAME.name(), metadata.getObjectName(), null, true); + generator.writeEntry(AdmCommonMetadataKey.OBJECT_ID.name(), metadata.getObjectID(), null, true); - // center - generator.writeEntry(AdmMetadataKey.CENTER_NAME.name(), metadata.getCenter().getName(), null, true); + if (metadata.getCenter() != null) { + // center + generator.writeEntry(AdmMetadataKey.CENTER_NAME.name(), metadata.getCenter().getName(), null, true); + } // time generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), metadata.getTimeSystem(), true); diff --git a/src/main/java/org/orekit/utils/ParametersDriversProvider.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmHeader.java similarity index 71% rename from src/main/java/org/orekit/utils/ParametersDriversProvider.java rename to src/main/java/org/orekit/files/ccsds/ndm/adm/AdmHeader.java index 3df31011f3..b6085a8428 100644 --- a/src/main/java/org/orekit/utils/ParametersDriversProvider.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2023 Luc Maisonobe * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,19 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.utils; +package org.orekit.files.ccsds.ndm.adm; -import java.util.List; +import org.orekit.files.ccsds.section.Header; -/** Provider for {@link ParameterDriver parameters drivers}. +/** + * Header of a CCSDS Attitude Data Message. * @author Luc Maisonobe - * @since 11.2 + * @since 12.0 */ -public interface ParametersDriversProvider { +public class AdmHeader extends Header { - /** Get the drivers for parameters. - * @return drivers for parameters + /** + * Constructor. */ - List getParametersDrivers(); + public AdmHeader() { + super(2.0, 2.0); + } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadata.java index d8912480fa..8d5b9a0209 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -46,8 +46,8 @@ public AdmMetadata() { @Override public void validate(final double version) { super.validate(version); - checkNotNull(objectName, AdmMetadataKey.OBJECT_NAME); - checkNotNull(objectID, AdmMetadataKey.OBJECT_ID); + checkNotNull(objectName, AdmMetadataKey.OBJECT_NAME.name()); + checkNotNull(objectID, AdmCommonMetadataKey.OBJECT_ID.name()); } /** diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataKey.java index f0e97523cf..943db8e60d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,10 +27,7 @@ public enum AdmMetadataKey { /** Object name entry. */ - OBJECT_NAME((token, context, container) -> token.processAsUppercaseString(container::setObjectName)), - - /** Object ID entry. */ - OBJECT_ID((token, context, container) -> token.processAsUppercaseString(container::setObjectID)), + OBJECT_NAME((token, context, container) -> token.processAsFreeTextString(container::setObjectName)), /** Center name entry. */ CENTER_NAME((token, context, container) -> token.processAsCenter(container::setCenter, diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmParser.java index dfb75f9146..a26de7ccaa 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AdmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,16 +16,14 @@ */ package org.orekit.files.ccsds.ndm.adm; +import java.util.List; import java.util.Map; +import java.util.function.Function; -import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.ndm.NdmConstituent; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.utils.lexical.ParseToken; -import org.orekit.files.ccsds.utils.lexical.TokenType; import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder; import org.orekit.files.ccsds.utils.parsing.AbstractConstituentParser; import org.orekit.time.AbsoluteDate; @@ -47,8 +45,8 @@ * @author Luc Maisonobe * @since 11.0 */ -public abstract class AdmParser, P extends AbstractConstituentParser> - extends AbstractConstituentParser { +public abstract class AdmParser, P extends AbstractConstituentParser> + extends AbstractConstituentParser { /** Index rotation element name. */ private static final String ROTATION_1 = "rotation1"; @@ -71,11 +69,14 @@ public abstract class AdmParser, P extends Abstra * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems * (may be null if time system is absolute) * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ protected AdmParser(final String root, final String formatVersionKey, final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, - final AbsoluteDate missionReferenceDate, final ParsedUnitsBehavior parsedUnitsBehavior) { - super(root, formatVersionKey, conventions, simpleEOP, dataContext, parsedUnitsBehavior); + final AbsoluteDate missionReferenceDate, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(root, formatVersionKey, conventions, simpleEOP, dataContext, parsedUnitsBehavior, filters); this.missionReferenceDate = missionReferenceDate; } @@ -102,34 +103,4 @@ public AbsoluteDate getMissionReferenceDate() { return missionReferenceDate; } - /** Process a CCSDS Euler angles sequence as a {@link RotationOrder}. - * @param sequence Euler angles sequence token - * @param consumer consumer of the rotation order - * @return always return {@code true} - */ - public static boolean processRotationOrder(final ParseToken sequence, - final RotationOrderConsumer consumer) { - if (sequence.getType() == TokenType.ENTRY) { - try { - consumer.accept(RotationOrder.valueOf(sequence.getContentAsUppercaseString(). - replace('1', 'X'). - replace('2', 'Y'). - replace('3', 'Z'))); - } catch (IllegalArgumentException iae) { - throw new OrekitException(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE, - sequence.getContentAsUppercaseString(), - sequence.getLineNumber(), sequence.getFileName()); - } - } - return true; - } - - /** Interface representing instance methods that consume otation order values. */ - public interface RotationOrderConsumer { - /** Consume a data. - * @param value value to consume - */ - void accept(RotationOrder value); - } - } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeEndoints.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeEndpoints.java similarity index 91% rename from src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeEndoints.java rename to src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeEndpoints.java index 1a69658ab1..162ae49b67 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeEndoints.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeEndpoints.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,8 @@ */ package org.orekit.files.ccsds.ndm.adm; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.Attitude; @@ -51,7 +51,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class AttitudeEndoints implements AttitudeBuilder { +public class AttitudeEndpoints implements AttitudeBuilder { /** Constant for A → B diraction. */ public static final String A2B = "A2B"; @@ -68,6 +68,17 @@ public class AttitudeEndoints implements AttitudeBuilder { /** Flag for frames direction. */ private Boolean a2b; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public AttitudeEndpoints() { + // nothing to do + } + /** Complain if a field is null. * @param field field to check * @param key key associated with the field @@ -98,11 +109,13 @@ public void checkExternalFrame(final Enum aKey, final Enum bKey) { *

          * This method should throw an exception if some mandatory entry is missing *

          + * @param version format version * @param aKey key for frame A * @param bKey key for frame B * @param dirKey key for direction */ - public void checkMandatoryEntriesExceptExternalFrame(final Enum aKey, final Enum bKey, + public void checkMandatoryEntriesExceptExternalFrame(final double version, + final Enum aKey, final Enum bKey, final Enum dirKey) { if (frameA == null) { @@ -118,7 +131,14 @@ public void checkMandatoryEntriesExceptExternalFrame(final Enum aKey, final E } } - checkNotNull(a2b, dirKey); + if (version < 2.0) { + // in ADM version 1, direction is mandatory + checkNotNull(a2b, dirKey); + } else if (!isA2b()) { + // in ADM version 2, direction is always A → B + throw new OrekitException(OrekitMessages.CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION, + dirKey, version); + } } @@ -181,7 +201,7 @@ public FrameFacade getSpacecraftBodyFrame() { /** Check if attitude is from external frame to spacecraft body frame. *

          - * {@link #checkMandatoryEntriesExceptExternalFrame(Enum, Enum, Enum) + * {@link #checkMandatoryEntriesExceptExternalFrame(double, Enum, Enum, Enum) * Mandatory entries} must have been initialized properly to non-null * values before this method is called, otherwise {@code NullPointerException} * will be thrown. @@ -189,7 +209,7 @@ public FrameFacade getSpacecraftBodyFrame() { * @return true if attitude is from external frame to spacecraft body frame */ public boolean isExternal2SpacecraftBody() { - return a2b ^ frameB.asSpacecraftBodyFrame() == null; + return isA2b() ^ frameB.asSpacecraftBodyFrame() == null; } /** Check if a endpoint is compatible with another one. @@ -200,7 +220,7 @@ public boolean isExternal2SpacecraftBody() { * @param other other endpoints to check against * @return true if both endpoints are compatible with each other */ - public boolean isCompatibleWith(final AttitudeEndoints other) { + public boolean isCompatibleWith(final AttitudeEndpoints other) { return frameA.getName().equals(other.frameA.getName()) && frameB.getName().equals(other.frameB.getName()) && a2b.equals(other.a2b); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeType.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeType.java index e607fe1e33..c1c1e00b7e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/AttitudeType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,15 +16,22 @@ */ package org.orekit.files.ccsds.ndm.adm; -import java.util.regex.Pattern; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.hipparchus.analysis.differentiation.UnivariateDerivative1; +import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.hipparchus.util.SinCos; import org.orekit.attitudes.Attitude; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -43,33 +50,32 @@ public enum AttitudeType { /** Quaternion. */ - QUATERNION("QUATERNION", AngularDerivativesFilter.USE_R, + QUATERNION(Collections.singleton(new VersionedName(1.0, "QUATERNION")), + AngularDerivativesFilter.USE_R, Unit.ONE, Unit.ONE, Unit.ONE, Unit.ONE) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { - - // Initialize the array of attitude data - final double[] data = new double[4]; + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { // Data index final int[] quaternionIndex = isFirst ? new int[] {0, 1, 2, 3} : new int[] {3, 0, 1, 2}; - // Fill the array Rotation rotation = coordinates.getRotation(); if (!isExternal2SpacecraftBody) { rotation = rotation.revert(); } + + // Fill the array, taking care of quaternion ordering + final double[] data = new double[4]; data[quaternionIndex[0]] = rotation.getQ0(); data[quaternionIndex[1]] = rotation.getQ1(); data[quaternionIndex[2]] = rotation.getQ2(); data[quaternionIndex[3]] = rotation.getQ3(); - // Convert units and format - return QUATERNION.formatData(data); + return data; } @@ -97,18 +103,16 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, }, /** Quaternion and derivatives. */ - QUATERNION_DERIVATIVE("QUATERNION/DERIVATIVE", AngularDerivativesFilter.USE_RR, + QUATERNION_DERIVATIVE(Collections.singleton(new VersionedName(1.0, "QUATERNION/DERIVATIVE")), + AngularDerivativesFilter.USE_RR, Unit.ONE, Unit.ONE, Unit.ONE, Unit.ONE, Units.ONE_PER_S, Units.ONE_PER_S, Units.ONE_PER_S, Units.ONE_PER_S) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { - - // Initialize the array of attitude data - final double[] data = new double[8]; + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { FieldRotation rotation = coordinates.toUnivariateDerivative1Rotation(); if (!isExternal2SpacecraftBody) { @@ -120,7 +124,8 @@ public String[] createDataFields(final boolean isFirst, final boolean isExternal new int[] {0, 1, 2, 3, 4, 5, 6, 7} : new int[] {3, 0, 1, 2, 7, 4, 5, 6}; - // Fill the array + // Fill the array, taking care of quaternion ordering + final double[] data = new double[8]; data[quaternionIndex[0]] = rotation.getQ0().getValue(); data[quaternionIndex[1]] = rotation.getQ1().getValue(); data[quaternionIndex[2]] = rotation.getQ2().getValue(); @@ -130,8 +135,7 @@ public String[] createDataFields(final boolean isFirst, final boolean isExternal data[quaternionIndex[6]] = rotation.getQ2().getFirstDerivative(); data[quaternionIndex[7]] = rotation.getQ3().getFirstDerivative(); - // Convert units and format - return QUATERNION_DERIVATIVE.formatData(data); + return data; } @@ -165,28 +169,90 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, }, - /** Quaternion and rotation rate. */ - QUATERNION_RATE("QUATERNION/RATE", AngularDerivativesFilter.USE_RR, - Unit.ONE, Unit.ONE, Unit.ONE, Unit.ONE, - Units.DEG_PER_S, Units.DEG_PER_S, Units.DEG_PER_S) { + /** Quaternion and Euler angles rates (only in ADM V1). */ + QUATERNION_EULER_RATES(Collections.singleton(new VersionedName(1.0, "QUATERNION/RATE")), + AngularDerivativesFilter.USE_RR, + Unit.ONE, Unit.ONE, Unit.ONE, Unit.ONE, + Units.DEG_PER_S, Units.DEG_PER_S, Units.DEG_PER_S) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { + + // Data index + final int[] quaternionIndex = isFirst ? new int[] {0, 1, 2, 3} : new int[] {3, 0, 1, 2}; - // Initialize the array of attitude data + // Attitude + FieldRotation rotation = coordinates.toUnivariateDerivative1Rotation(); + if (!isExternal2SpacecraftBody) { + rotation = rotation.revert(); + } + final UnivariateDerivative1[] euler = rotation.getAngles(eulerRotSequence, RotationConvention.FRAME_TRANSFORM); + + // Fill the array, taking care of quaternion ordering final double[] data = new double[7]; + data[quaternionIndex[0]] = rotation.getQ0().getValue(); + data[quaternionIndex[1]] = rotation.getQ1().getValue(); + data[quaternionIndex[2]] = rotation.getQ2().getValue(); + data[quaternionIndex[3]] = rotation.getQ3().getValue(); + data[4] = euler[0].getFirstDerivative(); + data[5] = euler[1].getFirstDerivative(); + data[6] = euler[2].getFirstDerivative(); + + return data; + + } + + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates build(final boolean isFirst, + final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, + final boolean isSpacecraftBodyRate, + final AbsoluteDate date, + final double... components) { + // Build the needed objects + final Rotation rotation = isFirst ? + new Rotation(components[0], components[1], components[2], components[3], true) : + new Rotation(components[3], components[0], components[1], components[2], true); + final double[] euler = rotation.getAngles(eulerRotSequence, RotationConvention.FRAME_TRANSFORM); + final FieldRotation rUD1 = + new FieldRotation<>(eulerRotSequence, RotationConvention.FRAME_TRANSFORM, + new UnivariateDerivative1(euler[0], components[4]), + new UnivariateDerivative1(euler[1], components[5]), + new UnivariateDerivative1(euler[2], components[6])); + + // Return + final TimeStampedAngularCoordinates ac = new TimeStampedAngularCoordinates(date, rUD1); + return isExternal2SpacecraftBody ? ac : ac.revert(); + + } + + }, + + /** Quaternion and angular velocity. */ + QUATERNION_ANGVEL(Collections.singleton(new VersionedName(2.0, "QUATERNION/ANGVEL")), + AngularDerivativesFilter.USE_RR, + Unit.ONE, Unit.ONE, Unit.ONE, Unit.ONE, + Units.DEG_PER_S, Units.DEG_PER_S, Units.DEG_PER_S) { + + /** {@inheritDoc} */ + @Override + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { // Data index final int[] quaternionIndex = isFirst ? new int[] {0, 1, 2, 3} : new int[] {3, 0, 1, 2}; // Attitude final TimeStampedAngularCoordinates c = isExternal2SpacecraftBody ? coordinates : coordinates.revert(); - final Vector3D rotationRate = QUATERNION_RATE.metadataRate(isSpacecraftBodyRate, c.getRotationRate(), c.getRotation()); + final Vector3D rotationRate = QUATERNION_ANGVEL.metadataRate(isSpacecraftBodyRate, c.getRotationRate(), c.getRotation()); - // Fill the array + // Fill the array, taking care of quaternion ordering + final double[] data = new double[7]; data[quaternionIndex[0]] = c.getRotation().getQ0(); data[quaternionIndex[1]] = c.getRotation().getQ1(); data[quaternionIndex[2]] = c.getRotation().getQ2(); @@ -195,8 +261,7 @@ public String[] createDataFields(final boolean isFirst, final boolean isExternal data[5] = rotationRate.getY(); data[6] = rotationRate.getZ(); - // Convert units and format - return QUATERNION_RATE.formatData(data); + return data; } @@ -212,11 +277,9 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, final Rotation rotation = isFirst ? new Rotation(components[0], components[1], components[2], components[3], true) : new Rotation(components[3], components[0], components[1], components[2], true); - final Vector3D rotationRate = QUATERNION_RATE.orekitRate(isSpacecraftBodyRate, - new Vector3D(components[4], - components[5], - components[6]), - rotation); + final Vector3D rotationRate = QUATERNION_ANGVEL.orekitRate(isSpacecraftBodyRate, + new Vector3D(components[4], components[5], components[6]), + rotation); // Return final TimeStampedAngularCoordinates ac = @@ -228,14 +291,15 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, }, /** Euler angles. */ - EULER_ANGLE("EULER ANGLE", AngularDerivativesFilter.USE_R, + EULER_ANGLE(Collections.singleton(new VersionedName(1.0, "EULER_ANGLE")), + AngularDerivativesFilter.USE_R, Unit.DEGREE, Unit.DEGREE, Unit.DEGREE) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { // Attitude Rotation rotation = coordinates.getRotation(); @@ -243,10 +307,7 @@ public String[] createDataFields(final boolean isFirst, final boolean isExternal rotation = rotation.revert(); } - final double[] data = rotation.getAngles(eulerRotSequence, RotationConvention.FRAME_TRANSFORM); - - // Convert units and format - return EULER_ANGLE.formatData(data); + return rotation.getAngles(eulerRotSequence, RotationConvention.FRAME_TRANSFORM); } @@ -273,34 +334,89 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, }, /** Euler angles and rotation rate. */ - EULER_ANGLE_RATE("EULER ANGLE/RATE", AngularDerivativesFilter.USE_RR, - Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, - Units.DEG_PER_S, Units.DEG_PER_S, Units.DEG_PER_S) { + EULER_ANGLE_DERIVATIVE(Arrays.asList(new VersionedName(1.0, "EULER_ANGLE/RATE"), + new VersionedName(2.0, "EULER_ANGLE/DERIVATIVE")), + AngularDerivativesFilter.USE_RR, + Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, + Units.DEG_PER_S, Units.DEG_PER_S, Units.DEG_PER_S) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { + + // Attitude + FieldRotation rotation = coordinates.toUnivariateDerivative1Rotation(); + if (!isExternal2SpacecraftBody) { + rotation = rotation.revert(); + } + + final UnivariateDerivative1[] angles = rotation.getAngles(eulerRotSequence, RotationConvention.FRAME_TRANSFORM); - // Initialize the array of attitude data - final double[] data = new double[6]; + return new double[] { + angles[0].getValue(), + angles[1].getValue(), + angles[2].getValue(), + angles[0].getFirstDerivative(), + angles[1].getFirstDerivative(), + angles[2].getFirstDerivative() + }; + + } + + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates build(final boolean isFirst, + final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, + final boolean isSpacecraftBodyRate, + final AbsoluteDate date, + final double... components) { + + // Build the needed objects + FieldRotation rotation = + new FieldRotation<>(eulerRotSequence, RotationConvention.FRAME_TRANSFORM, + new UnivariateDerivative1(components[0], components[3]), + new UnivariateDerivative1(components[1], components[4]), + new UnivariateDerivative1(components[2], components[5])); + if (!isExternal2SpacecraftBody) { + rotation = rotation.revert(); + } + + return new TimeStampedAngularCoordinates(date, rotation); + + } + + }, + + /** Euler angles and angular velocity. + * @since 12.0 + */ + EULER_ANGLE_ANGVEL(Collections.singleton(new VersionedName(2.0, "EULER_ANGLE/ANGVEL")), + AngularDerivativesFilter.USE_RR, + Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, + Units.DEG_PER_S, Units.DEG_PER_S, Units.DEG_PER_S) { + + /** {@inheritDoc} */ + @Override + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { // Attitude final TimeStampedAngularCoordinates c = isExternal2SpacecraftBody ? coordinates : coordinates.revert(); - final Vector3D rotationRate = EULER_ANGLE_RATE.metadataRate(isSpacecraftBodyRate, c.getRotationRate(), c.getRotation()); + final Vector3D rotationRate = EULER_ANGLE_ANGVEL.metadataRate(isSpacecraftBodyRate, c.getRotationRate(), c.getRotation()); final double[] angles = c.getRotation().getAngles(eulerRotSequence, RotationConvention.FRAME_TRANSFORM); - // Fill the array - data[0] = angles[0]; - data[1] = angles[1]; - data[2] = angles[2]; - data[3] = Vector3D.dotProduct(rotationRate, eulerRotSequence.getA1()); - data[4] = Vector3D.dotProduct(rotationRate, eulerRotSequence.getA2()); - data[5] = Vector3D.dotProduct(rotationRate, eulerRotSequence.getA3()); - - // Convert units and format - return EULER_ANGLE_RATE.formatData(data); + return new double[] { + angles[0], + angles[1], + angles[2], + rotationRate.getX(), + rotationRate.getY(), + rotationRate.getZ() + }; } @@ -319,11 +435,9 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, components[0], components[1], components[2]); - final Vector3D rotationRate = EULER_ANGLE_RATE.orekitRate(isSpacecraftBodyRate, - new Vector3D(components[3], eulerRotSequence.getA1(), - components[4], eulerRotSequence.getA2(), - components[5], eulerRotSequence.getA3()), - rotation); + final Vector3D rotationRate = EULER_ANGLE_ANGVEL.orekitRate(isSpacecraftBodyRate, + new Vector3D(components[3], components[4], components[5]), + rotation); // Return final TimeStampedAngularCoordinates ac = new TimeStampedAngularCoordinates(date, rotation, rotationRate, Vector3D.ZERO); @@ -333,37 +447,25 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, }, - /** Spin. - *

          - * CCSDS enforces that spin axis is +Z, so if {@link #createDataFields(boolean, boolean, RotationOrder, boolean, - * TimeStampedAngularCoordinates) createDataFields} is called with {@code coordinates} with {@link - * TimeStampedAngularCoordinates#getRotationRate() rotation rate} that is not along the Z axis, result is - * undefined. - *

          - */ - SPIN("SPIN", AngularDerivativesFilter.USE_RR, + /** Spin. */ + SPIN(Collections.singleton(new VersionedName(1.0, "SPIN")), + AngularDerivativesFilter.USE_R, Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, Units.DEG_PER_S) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { - - // Initialize the array of attitude data - final double[] data = new double[4]; + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { - // Attitude - final double[] angles = coordinates.getRotation().getAngles(RotationOrder.ZYZ, RotationConvention.FRAME_TRANSFORM); - - // Fill the array - data[0] = angles[0]; - data[1] = 0.5 * FastMath.PI - angles[1]; - data[2] = angles[2]; - data[3] = coordinates.getRotationRate().getZ(); + // spin axis is forced to Z (but it is not the instantaneous rotation rate as it also moves) + final TimeStampedAngularCoordinates c = isExternal2SpacecraftBody ? coordinates : coordinates.revert(); + final SpinFinder sf = new SpinFinder(c); + final double spinAngleVel = coordinates.getRotationRate().getZ(); - // Convert units and format - return SPIN.formatData(data); + return new double[] { + sf.getSpinAlpha(), sf.getSpinDelta(), sf.getSpinAngle(), spinAngleVel + }; } @@ -377,34 +479,61 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, final double... components) { // Build the needed objects - final Rotation rotation = new Rotation(RotationOrder.ZYZ, + final Rotation rotation = new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, - components[0], - 0.5 * FastMath.PI - components[1], + MathUtils.SEMI_PI + components[0], + MathUtils.SEMI_PI - components[1], components[2]); final Vector3D rotationRate = new Vector3D(0, 0, components[3]); // Return - return new TimeStampedAngularCoordinates(date, rotation, rotationRate, Vector3D.ZERO); + final TimeStampedAngularCoordinates ac = + new TimeStampedAngularCoordinates(date, rotation, rotationRate, Vector3D.ZERO); + return isExternal2SpacecraftBody ? ac : ac.revert(); } }, /** Spin and nutation. */ - SPIN_NUTATION("SPIN/NUTATION", AngularDerivativesFilter.USE_RR, + SPIN_NUTATION(Collections.singleton(new VersionedName(1.0, "SPIN/NUTATION")), + AngularDerivativesFilter.USE_RR, Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, Units.DEG_PER_S, Unit.DEGREE, Unit.SECOND, Unit.DEGREE) { /** {@inheritDoc} */ @Override - public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, - final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, - final TimeStampedAngularCoordinates coordinates) { - // Attitude parameters in the Specified Reference Frame for a Spin Stabilized Satellite - // are optional in CCSDS AEM format. Support for this attitude type is not implemented - // yet in Orekit. - throw new OrekitException(OrekitMessages.CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED, name()); + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { + + // spin data + final TimeStampedAngularCoordinates c = isExternal2SpacecraftBody ? coordinates : coordinates.revert(); + final SpinFinder sf = new SpinFinder(c); + + // Orekit/CCSDS naming difference: for CCSDS this is nutation, for Orekit this is precession + final FieldRotation c2 = c.toUnivariateDerivative2Rotation(); + final FieldVector3D spinAxis = c2.applyInverseTo(Vector3D.PLUS_K); + final PrecessionFinder pf = new PrecessionFinder(spinAxis); + + // intermediate inertial frame, with Z axis aligned with angular momentum + final Rotation intermediate2Inert = new Rotation(Vector3D.PLUS_K, pf.getAxis()); + + // recover Euler rotations starting from frame aligned with angular momentum + final FieldRotation intermediate2Body = c2.applyTo(intermediate2Inert); + final UnivariateDerivative2[] euler = intermediate2Body. + getAngles(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM); + + return new double[] { + sf.getSpinAlpha(), + sf.getSpinDelta(), + sf.getSpinAngle(), + euler[2].getFirstDerivative(), + pf.getPrecessionAngle(), + MathUtils.TWO_PI / pf.getAngularVelocity(), + euler[2].getValue() - MathUtils.SEMI_PI + }; + } /** {@inheritDoc} */ @@ -415,19 +544,150 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, final boolean isSpacecraftBodyRate, final AbsoluteDate date, final double... components) { - // Attitude parameters in the Specified Reference Frame for a Spin Stabilized Satellite - // are optional in CCSDS AEM format. Support for this attitude type is not implemented - // yet in Orekit. - throw new OrekitException(OrekitMessages.CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED, name()); + + // Build the needed objects + final Rotation inert2Body0 = new Rotation(RotationOrder.ZXZ, + RotationConvention.FRAME_TRANSFORM, + MathUtils.SEMI_PI + components[0], + MathUtils.SEMI_PI - components[1], + components[2]); + + // intermediate inertial frame, with Z axis aligned with angular momentum + final SinCos scNutation = FastMath.sinCos(components[4]); + final SinCos scPhase = FastMath.sinCos(components[6]); + final Vector3D momentumBody = new Vector3D( scNutation.sin() * scPhase.cos(), + -scNutation.sin() * scPhase.sin(), + scNutation.cos()); + final Vector3D momentumInert = inert2Body0.applyInverseTo(momentumBody); + final Rotation inert2Intermediate = new Rotation(momentumInert, Vector3D.PLUS_K); + + // base Euler angles from the intermediate frame to body + final Rotation intermediate2Body0 = inert2Body0.applyTo(inert2Intermediate.revert()); + final double[] euler0 = intermediate2Body0.getAngles(RotationOrder.ZXZ, + RotationConvention.FRAME_TRANSFORM); + + // add Euler angular rates to base Euler angles + final FieldRotation intermediate2Body = + new FieldRotation<>(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + new UnivariateDerivative2(euler0[0], MathUtils.TWO_PI / components[5], 0.0), + new UnivariateDerivative2(euler0[1], 0.0, 0.0), + new UnivariateDerivative2(euler0[2], components[3], 0.0)); + + // final rotation, including derivatives + final FieldRotation inert2Body = intermediate2Body.applyTo(inert2Intermediate); + + final TimeStampedAngularCoordinates ac = + new TimeStampedAngularCoordinates(date, inert2Body); + return isExternal2SpacecraftBody ? ac : ac.revert(); + + } + + }, + + /** Spin and momentum. + * @since 12.0 + */ + SPIN_NUTATION_MOMENTUM(Collections.singleton(new VersionedName(2.0, "SPIN/NUTATION_MOM")), + AngularDerivativesFilter.USE_RR, + Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, Units.DEG_PER_S, + Unit.DEGREE, Unit.DEGREE, Units.DEG_PER_S) { + + /** {@inheritDoc} */ + @Override + public double[] generateData(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates coordinates) { + + // spin data + final TimeStampedAngularCoordinates c = isExternal2SpacecraftBody ? coordinates : coordinates.revert(); + final SpinFinder sf = new SpinFinder(c); + + // Orekit/CCSDS naming difference: for CCSDS this is nutation, for Orekit this is precession + final FieldRotation c2 = c.toUnivariateDerivative2Rotation(); + final FieldVector3D spinAxis = c2.applyInverseTo(Vector3D.PLUS_K); + final PrecessionFinder pf = new PrecessionFinder(spinAxis); + + // intermediate inertial frame, with Z axis aligned with angular momentum + final Rotation intermediate2Inert = new Rotation(Vector3D.PLUS_K, pf.getAxis()); + + // recover spin angle velocity + final FieldRotation intermediate2Body = c2.applyTo(intermediate2Inert); + final double spinAngleVel = intermediate2Body. + getAngles(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM)[2]. + getFirstDerivative(); + + return new double[] { + sf.getSpinAlpha(), + sf.getSpinDelta(), + sf.getSpinAngle(), + spinAngleVel, + pf.getAxis().getAlpha(), + pf.getAxis().getDelta(), + pf.getAngularVelocity() + }; + + } + + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates build(final boolean isFirst, + final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, + final boolean isSpacecraftBodyRate, + final AbsoluteDate date, + final double... components) { + + // Build the needed objects + final SinCos scAlpha = FastMath.sinCos(components[4]); + final SinCos scDelta = FastMath.sinCos(components[5]); + final Vector3D momentumInert = new Vector3D(scAlpha.cos() * scDelta.cos(), + scAlpha.sin() * scDelta.cos(), + scDelta.sin()); + final Rotation inert2Intermediate = new Rotation(momentumInert, Vector3D.PLUS_K); + + // base Euler angles from the intermediate frame to body + final Rotation inert2Body0 = new Rotation(RotationOrder.ZXZ, + RotationConvention.FRAME_TRANSFORM, + MathUtils.SEMI_PI + components[0], + MathUtils.SEMI_PI - components[1], + components[2]); + final Rotation intermediate2Body0 = inert2Body0.applyTo(inert2Intermediate.revert()); + final double[] euler0 = intermediate2Body0.getAngles(RotationOrder.ZXZ, + RotationConvention.FRAME_TRANSFORM); + + // add Euler angular rates to base Euler angles + final FieldRotation intermediate2Body = + new FieldRotation<>(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + new UnivariateDerivative2(euler0[0], components[6], 0.0), + new UnivariateDerivative2(euler0[1], 0.0, 0.0), + new UnivariateDerivative2(euler0[2], components[3], 0.0)); + + // final rotation, including derivatives + final FieldRotation inert2Body = intermediate2Body.applyTo(inert2Intermediate); + + // return + final TimeStampedAngularCoordinates ac = + new TimeStampedAngularCoordinates(date, inert2Body); + return isExternal2SpacecraftBody ? ac : ac.revert(); + } }; - /** Pattern for normalizing attitude types. */ - private static final Pattern TYPE_SEPARATORS = Pattern.compile("[ _/]+"); + /** Names map. + * @since 12.0 + */ + private static final Map MAP = new HashMap<>(); + static { + for (final AttitudeType type : values()) { + for (final VersionedName vn : type.ccsdsNames) { + MAP.put(vn.name, type); + } + } + } - /** CCSDS name of the attitude type. */ - private final String ccsdsName; + /** CCSDS names of the attitude type. */ + private final Iterable ccsdsNames; /** Derivatives filter. */ private final AngularDerivativesFilter filter; @@ -436,28 +696,48 @@ public TimeStampedAngularCoordinates build(final boolean isFirst, private final Unit[] units; /** Private constructor. - * @param ccsdsName CCSDS name of the attitude type + * @param ccsdsNames CCSDS names of the attitude type * @param filter derivative filter * @param units components units (used only for parsing) */ - AttitudeType(final String ccsdsName, final AngularDerivativesFilter filter, final Unit... units) { - this.ccsdsName = ccsdsName; - this.filter = filter; - this.units = units.clone(); + AttitudeType(final Iterable ccsdsNames, final AngularDerivativesFilter filter, final Unit... units) { + this.ccsdsNames = ccsdsNames; + this.filter = filter; + this.units = units.clone(); + } + + /** Get the type name for a given format version. + * @param formatVersion format version + * @return type name + * @since 12.0 + */ + public String getName(final double formatVersion) { + String name = null; + for (final VersionedName vn : ccsdsNames) { + if (name == null || formatVersion >= vn.since) { + name = vn.name; + } + } + return name; } /** {@inheritDoc} */ @Override public String toString() { - return ccsdsName; + // use the most recent name by default + return getName(Double.POSITIVE_INFINITY); } /** Parse an attitude type. - * @param type unnormalized type name + * @param typeSpecification unnormalized type name * @return parsed type */ - public static AttitudeType parseType(final String type) { - return AttitudeType.valueOf(TYPE_SEPARATORS.matcher(type).replaceAll("_")); + public static AttitudeType parseType(final String typeSpecification) { + final AttitudeType type = MAP.get(typeSpecification); + if (type == null) { + throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_ATTITUDE_TYPE, typeSpecification); + } + return type; } /** @@ -473,9 +753,41 @@ public static AttitudeType parseType(final String type) { * (i.e. from inertial frame to spacecraft frame) * @return the attitude data in CCSDS units */ - public abstract String[] createDataFields(boolean isFirst, boolean isExternal2SpacecraftBody, - RotationOrder eulerRotSequence, boolean isSpacecraftBodyRate, - TimeStampedAngularCoordinates attitude); + public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody, + final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate, + final TimeStampedAngularCoordinates attitude) { + + // generate the double data + final double[] data = generateData(isFirst, isExternal2SpacecraftBody, + eulerRotSequence, isSpacecraftBodyRate, attitude); + + // format as string array with CCSDS units + final String[] fields = new String[data.length]; + for (int i = 0; i < data.length; ++i) { + fields[i] = AccurateFormatter.format(units[i].fromSI(data[i])); + } + + return fields; + + } + + /** + * Generate the attitude data corresponding to the attitude type. + *

          + * This method returns the components in SI units. + *

          + * @param isFirst if true the first quaternion component is the scalar component + * @param isExternal2SpacecraftBody true attitude is from external frame to spacecraft body frame + * @param eulerRotSequence sequance of Euler angles + * @param isSpacecraftBodyRate if true Euler rates are specified in spacecraft body frame + * @param attitude angular coordinates, using {@link Attitude Attitude} convention + * (i.e. from inertial frame to spacecraft frame) + * @return the attitude data in CCSDS units + * @since 12.0 + */ + public abstract double[] generateData(boolean isFirst, boolean isExternal2SpacecraftBody, + RotationOrder eulerRotSequence, boolean isSpacecraftBodyRate, + TimeStampedAngularCoordinates attitude); /** * Get the angular coordinates corresponding to the attitude data. @@ -514,8 +826,7 @@ public TimeStampedAngularCoordinates parse(final boolean isFirst, final boolean * @param eulerRotSequence sequance of Euler angles * @param isSpacecraftBodyRate if true Euler rates are specified in spacecraft body frame * @param date entry date - * @param components entry components with CCSDS units (i.e. angles - * must still be in degrees here), semantic depends on attitude type + * @param components entry components with SI units, semantic depends on attitude type * @return the angular coordinates, using {@link Attitude Attitude} convention * (i.e. from inertial frame to spacecraft frame) */ @@ -531,14 +842,6 @@ public AngularDerivativesFilter getAngularDerivativesFilter() { return filter; } - private String[] formatData(final double[] data) { - final String[] fields = new String[data.length]; - for (int i = 0; i < data.length; ++i) { - fields[i] = AccurateFormatter.format(units[i].fromSI(data[i])); - } - return fields; - } - /** Convert a rotation rate for Orekit convention to metadata convention. * @param isSpacecraftBodyRate if true Euler rates are specified in spacecraft body frame * @param rate rotation rate from Orekit attitude @@ -559,4 +862,26 @@ private Vector3D orekitRate(final boolean isSpacecraftBodyRate, final Vector3D r return isSpacecraftBodyRate ? rate : rotation.applyTo(rate); } + /** Container for a name associated to a format version. + * @since 12.0 + */ + private static class VersionedName { + + /** Version at which this name was defined. */ + private final double since; + + /** Name. */ + private final String name; + + /** Simple constructor. + * @param since version at which this name was defined + * @param name name + */ + VersionedName(final double since, final String name) { + this.since = since; + this.name = name; + } + + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/PrecessionFinder.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/PrecessionFinder.java new file mode 100644 index 0000000000..9ce52d4eff --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/PrecessionFinder.java @@ -0,0 +1,160 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm; + +import org.hipparchus.analysis.differentiation.UnivariateDerivative2; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.SinCos; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** Utility to extract precession from the motion of a spin axis. + *

          + * The precession is used in {@link AttitudeType#SPIN_NUTATION} and + * {@link AttitudeType#SPIN_NUTATION_MOMENTUM} attitude types. CCSDS + * calls it nutation, but it is really precession. + *

          + * @author Luc Maisonobe + * @since 12.0 + */ +class PrecessionFinder { + + /** Precession axis (axis of the cone described by spin). */ + private final Vector3D axis; + + /** Precession angle (fixed cone angle between precession axis and spin axis). */ + private final double precessionAngle; + + /** Angular velocity around the precession axis (rad/s). */ + private final double angularVelocity; + + /** Build from spin axis motion. + *

          + * Note that the derivatives up to second order are really needed + * in order to retrieve the precession motion. + *

          + * @param spin spin axis, including value, first and second derivative + */ + PrecessionFinder(final FieldVector3D spin) { + + // using a suitable coordinates frame, the spin axis can be considered to describe + // a cone of half aperture angle 0 ≤ η ≤ π around k axis, at angular rate ω ≥ 0 + // with an initial position in the (+i,±k) half-plane. In this frame, the normalized + // direction of spin s = spin/||spin|| and its first and second time derivatives + // have coordinates: + // s(t): (sin η cos ω(t-t₀), sin η sin ω(t-t₀), cos η) + // ds/dt(t): (-ω sin η sin ω(t-t₀), ω sin η cos ω(t-t₀), 0) + // d²s/dt²(t): (-ω² sin η cos ω(t-t₀), -ω² sin η sin ω(t-t₀), 0) + // at initial time t = t₀ this leads to: + // s₀ = s(t₀): (sin η, 0, cos η) + // s₁ = ds/dt(t₀): (0, ω sin η, 0) + // s₂ = d²s/dt²(t₀): (-ω² sin η, 0, 0) + // however, only the spin vector itself is provided, we don't initially know the unit + // vectors (i, j, k) of this "suitable coordinates frame". We can however easily + // build another frame (u, v, w) from the normalized spin vector s as follows: + // u = s₀, v = s₁/||s₁||, w = u ⨯ v + // the coordinates of vectors u, v and w in the "suitable coordinates frame" are: + // u: ( sin η, 0, cos η) + // v: ( 0, 1, 0) + // w: (-cos η, 0, sin η) + // we can then deduce the following relations, which can be computed regardless + // of frames used to express the various vectors: + // s₁ · v = ω sin η = ||s₁|| + // s₂ · w = ω² sin η cos η + // these relations can be solved for η and ω (we know that 0 ≤ η ≤ π and ω ≥ 0): + // η = atan2(||s₁||², s₂ · w) + // ω = ||s₁|| / sin η + // then the k axis, which is the precession axis, can be deduced as: + // k = cos η u + sin η w + + final UnivariateDerivative2 nS = spin.getNorm(); + if (nS.getValue() == 0) { + // special case, no motion at all, set up arbitrary values + axis = Vector3D.PLUS_K; + precessionAngle = 0.0; + angularVelocity = 0.0; + } else { + + // build the derivatives vectors + final FieldVector3D s = spin.scalarMultiply(nS.reciprocal()); + final Vector3D s0 = new Vector3D(s.getX().getValue(), + s.getY().getValue(), + s.getZ().getValue()); + final Vector3D s1 = new Vector3D(s.getX().getFirstDerivative(), + s.getY().getFirstDerivative(), + s.getZ().getFirstDerivative()); + final Vector3D s2 = new Vector3D(s.getX().getSecondDerivative(), + s.getY().getSecondDerivative(), + s.getZ().getSecondDerivative()); + + final double nV2 = s1.getNormSq(); + if (nV2 == 0.0) { + // special case: we have a fixed spin vector + axis = s0; + precessionAngle = 0.0; + angularVelocity = 0.0; + } else { + + // check second derivatives were provided ; we do it on spin rather than s2 + // and use test against exact 0.0 because the normalization process changes + // the derivatives and what we really want to check are missing derivatives + if (new Vector3D(spin.getX().getSecondDerivative(), + spin.getY().getSecondDerivative(), + spin.getZ().getSecondDerivative()).getNormSq() == 0) { + throw new OrekitException(OrekitMessages.CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES); + } + + // build the unit vectors + final double nV = FastMath.sqrt(nV2); + final Vector3D v = s1.scalarMultiply(1.0 / nV); + final Vector3D w = Vector3D.crossProduct(s0, v); + + // compute precession model + precessionAngle = FastMath.atan2(nV2, Vector3D.dotProduct(s2, w)); + final SinCos sc = FastMath.sinCos(precessionAngle); + angularVelocity = nV / sc.sin(); + axis = new Vector3D(sc.cos(), s0, sc.sin(), w); + + } + } + + } + + /** Get the precession axis. + * @return precession axis + */ + public Vector3D getAxis() { + return axis; + } + + /** Get the precession angle. + * @return fixed cone angle between precession axis an spin axis (rad) + */ + public double getPrecessionAngle() { + return precessionAngle; + } + + /** Get the angular velocity around precession axis. + * @return angular velocity around precession axis (rad/s) + */ + public double getAngularVelocity() { + return angularVelocity; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/RotationXmlTokenBuilder.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/RotationXmlTokenBuilder.java index a79b4da990..5d88c25ab1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/RotationXmlTokenBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/RotationXmlTokenBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,15 +16,16 @@ */ package org.orekit.files.ccsds.ndm.adm; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.lexical.TokenType; import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder; import org.orekit.utils.units.Unit; import org.orekit.utils.units.UnitsCache; -import org.xml.sax.Attributes; /** Builder for rotation angles and rates. *

          @@ -55,26 +56,29 @@ public RotationXmlTokenBuilder() { /** {@inheritDoc} */ @Override - public List buildTokens(final boolean startTag, final String qName, - final String content, final Attributes attributes, + public List buildTokens(final boolean startTag, final boolean isLeaf, final String qName, + final String content, final Map attributes, final int lineNumber, final String fileName) { // get the token name from the first attribute found - String name = attributes.getValue(ANGLE); + String name = attributes.get(ANGLE); if (name == null) { - name = attributes.getValue(RATE); + name = attributes.get(RATE); } - // elaborate the token type - final TokenType type = (content == null) ? (startTag ? TokenType.START : TokenType.STOP) : TokenType.ENTRY; - - // get units - final Unit units = cache.getUnits(attributes.getValue(UNITS)); - - // final build - final ParseToken token = new ParseToken(type, name, content, units, lineNumber, fileName); - - return Collections.singletonList(token); + if (startTag) { + return Collections.singletonList(new ParseToken(TokenType.START, name, content, Unit.NONE, lineNumber, fileName)); + } else { + final List built = new ArrayList<>(2); + if (isLeaf) { + // get units + final Unit units = cache.getUnits(attributes.get(UNITS)); + + built.add(new ParseToken(TokenType.ENTRY, name, content, units, lineNumber, fileName)); + } + built.add(new ParseToken(TokenType.STOP, name, null, Unit.NONE, lineNumber, fileName)); + return built; + } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/SpinFinder.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/SpinFinder.java new file mode 100644 index 0000000000..d333d0fa20 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/SpinFinder.java @@ -0,0 +1,97 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.attitudes.Attitude; +import org.orekit.utils.TimeStampedAngularCoordinates; + +/** Utility to extract spin data. + *

          + * In CCSDS ADM, spin axis is forced to Z. It is not the instantaneous rotation + * rate as it also moves. + *

          + * @author Luc Maisonobe + * @since 12.0 + */ +class SpinFinder { + + /** Spin axis. */ + private final Vector3D spin; + + /** Right ascension of spin axis (rad). */ + private final double spinAlpha; + + /** declination of spin axis (rad). */ + private final double spinDelta; + + /** Spin angle. */ + private final double spinAngle; + + /** Build from attitude rotation. + * @param attitude angular coordinates, using {@link Attitude Attitude} convention + * (i.e. from inertial frame to spacecraft frame) + */ + SpinFinder(final TimeStampedAngularCoordinates attitude) { + // spin axis is forced to Z (but it is not the instantaneous rotation rate as it also moves) + spin = attitude.getRotation().applyInverseTo(Vector3D.PLUS_K); + spinAlpha = spin.getAlpha(); + spinDelta = spin.getDelta(); + final Rotation alignSpin = new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM, + MathUtils.SEMI_PI + spinAlpha, + MathUtils.SEMI_PI - spinDelta, + 0.0); + final Rotation phasing = attitude.getRotation().applyTo(alignSpin.revert()); + spinAngle = FastMath.copySign(phasing.getAngle(), + phasing.getAxis(RotationConvention.FRAME_TRANSFORM).getZ()); + + } + + /** Get the spin axis in inertial frame. + * @return spin axis in inertial frame + */ + public Vector3D getSpin() { + return spin; + } + + /** Get the declination of spin axis. + * @return declination of spin axis (rad) + */ + public double getSpinDelta() { + return spinDelta; + } + + /** Get the right ascension of spin axis. + * @return right ascension of spin axis (rad) + */ + public double getSpinAlpha() { + return spinAlpha; + } + + /** Get the spin angle. + * @return spin angle (rad) + */ + public double getSpinAngle() { + return spinAngle; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/Acm.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/Acm.java new file mode 100644 index 0000000000..8ea234a698 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/Acm.java @@ -0,0 +1,100 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.orekit.data.DataContext; +import org.orekit.files.ccsds.ndm.NdmConstituent; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; +import org.orekit.files.ccsds.section.Segment; +import org.orekit.files.general.AttitudeEphemerisFile; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.TimeStampedAngularCoordinates; + +/** This class gathers the informations present in the Attitude Comprehensive Message (ACM). + * @author Luc Maisonobe + * @since 12.0 + */ +public class Acm extends NdmConstituent> + implements AttitudeEphemerisFile { + + /** Root element for XML messages. */ + public static final String ROOT = "acm"; + + /** Key for format version. */ + public static final String FORMAT_VERSION_KEY = "CCSDS_ACM_VERS"; + + /** Attitude line element for XML messages. */ + public static final String ATT_LINE = "attLine"; + + /** Covariance line element for XML messages. */ + public static final String COV_LINE = "covLine"; + + /** Default name for unknown object. */ + public static final String UNKNOWN_OBJECT = "UNKNOWN"; + + /** Simple constructor. + * @param header file header + * @param segments ile segments + * @param conventions IERS conventions + * @param dataContext used for creating frames, time scales, etc. + */ + public Acm(final AdmHeader header, final List> segments, + final IERSConventions conventions, final DataContext dataContext) { + super(header, segments, conventions, dataContext); + } + + /** Get the metadata from the single {@link #getSegments() segment}. + * @return metadata from the single {@link #getSegments() segment} + */ + public AcmMetadata getMetadata() { + return getSegments().get(0).getMetadata(); + } + + /** Get the data from the single {@link #getSegments() segment}. + * @return data from the single {@link #getSegments() segment} + */ + public AcmData getData() { + return getSegments().get(0).getData(); + } + + /** {@inheritDoc} */ + @Override + public Map getSatellites() { + // the ACM file has only one segment and a deep structure + // the real ephemeris is buried within the attitude state time history logical block + final String name; + if (getMetadata().getObjectName() != null) { + name = getMetadata().getObjectName(); + } else if (getMetadata().getInternationalDesignator() != null) { + name = getMetadata().getInternationalDesignator(); + } else if (getMetadata().getObjectDesignator() != null) { + name = getMetadata().getObjectDesignator(); + } else { + name = UNKNOWN_OBJECT; + } + final List histories = getSegments().get(0).getData().getAttitudeBlocks(); + return (histories == null) ? + Collections.emptyMap() : + Collections.singletonMap(name, new AcmSatelliteEphemeris(name, histories)); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmData.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmData.java new file mode 100644 index 0000000000..7dc449f9d3 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmData.java @@ -0,0 +1,142 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; + +import org.orekit.files.ccsds.ndm.odm.UserDefined; +import org.orekit.files.ccsds.section.Data; + +/** Data container for Attitude Comprehensive Messages. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AcmData implements Data { + + /** Attitude state histories logical blocks. */ + private final List attitudeBlocks; + + /** Physical properties logical block. */ + private final AttitudePhysicalProperties physicBlock; + + /** Covariance logical blocks. */ + private final List covarianceBlocks; + + /** Maneuvers logical blocks. */ + private final List maneuverBlocks; + + /** Attitude determination logical block. */ + private final AttitudeDetermination attitudeDeterminationBlock; + + /** User defined parameters logical block. */ + private final UserDefined userDefinedBlock; + + /** Simple constructor. + * @param attitudeBlocks attitude state histories logical blocks (may be empty) + * @param physicBlock physical properties logical block (may be null) + * @param covarianceBlocks covariance logical blocks (may be empty) + * @param maneuverBlocks maneuvers logical blocks (may be empty) + * @param attitudeDeterminationBlock attitude determination logical block (may be null) + * @param userDefinedBlock user defined parameters logical block (may be null) + */ + public AcmData(final List attitudeBlocks, + final AttitudePhysicalProperties physicBlock, + final List covarianceBlocks, + final List maneuverBlocks, + final AttitudeDetermination attitudeDeterminationBlock, + final UserDefined userDefinedBlock) { + this.attitudeBlocks = attitudeBlocks; + this.physicBlock = physicBlock; + this.covarianceBlocks = covarianceBlocks; + this.maneuverBlocks = maneuverBlocks; + this.attitudeDeterminationBlock = attitudeDeterminationBlock; + this.userDefinedBlock = userDefinedBlock; + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + if (attitudeBlocks != null) { + for (final AttitudeStateHistory ash : attitudeBlocks) { + ash.getMetadata().validate(version); + } + } + if (physicBlock != null) { + physicBlock.validate(version); + } + if (covarianceBlocks != null) { + for (final AttitudeCovarianceHistory ch : covarianceBlocks) { + ch.getMetadata().validate(version); + } + } + if (maneuverBlocks != null) { + for (final AttitudeManeuver m : maneuverBlocks) { + m.validate(version); + } + } + if (attitudeDeterminationBlock != null) { + attitudeDeterminationBlock.validate(version); + } + if (userDefinedBlock != null) { + userDefinedBlock.validate(version); + } + } + + /** Get attitude state histories logical blocks. + * @return attitude state histories logical blocks (may be null) + */ + public List getAttitudeBlocks() { + return attitudeBlocks; + } + + /** Get physical properties logical block. + * @return physical properties logical block (may be null) + */ + public AttitudePhysicalProperties getPhysicBlock() { + return physicBlock; + } + + /** Get covariance logical blocks. + * @return covariance logical blocks (may be null) + */ + public List getCovarianceBlocks() { + return covarianceBlocks; + } + + /** Get maneuvers logical blocks. + * @return maneuvers logical block (may be null) + */ + public List getManeuverBlocks() { + return maneuverBlocks; + } + + /** Get attitude determination logical block. + * @return attitude determination logical block (may be null) + */ + public AttitudeDetermination getAttitudeDeterminationBlock() { + return attitudeDeterminationBlock; + } + + /** Get user defined parameters logical block. + * @return user defined parameters logical block (may be null) + */ + public UserDefined getUserDefinedBlock() { + return userDefinedBlock; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmDataSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmDataSubStructureKey.java new file mode 100644 index 0000000000..158dab0b45 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmDataSubStructureKey.java @@ -0,0 +1,93 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; + +/** Keywords for ACM data sub-structure. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AcmDataSubStructureKey { + + /** Attitude state time history section. */ + ATT((token, parser) -> parser.manageAttitudeStateSection(token.getType() == TokenType.START)), + + /** Trajectory state time history section. */ + att((token, parser) -> parser.manageAttitudeStateSection(token.getType() == TokenType.START)), + + /** Physical properties section. */ + PHYS((token, parser) -> parser.managePhysicalPropertiesSection(token.getType() == TokenType.START)), + + /** Physical properties section. */ + phys((token, parser) -> parser.managePhysicalPropertiesSection(token.getType() == TokenType.START)), + + /** Covariance time history section. */ + COV((token, parser) -> parser.manageCovarianceHistorySection(token.getType() == TokenType.START)), + + /** Covariance time history section. */ + cov((token, parser) -> parser.manageCovarianceHistorySection(token.getType() == TokenType.START)), + + /** Maneuvers section. */ + MAN((token, parser) -> parser.manageManeuversSection(token.getType() == TokenType.START)), + + /** Maneuvers section. */ + man((token, parser) -> parser.manageManeuversSection(token.getType() == TokenType.START)), + + /** Attitude determination section. */ + AD((token, parser) -> parser.manageAttitudeDeterminationSection(token.getType() == TokenType.START)), + + /** Orbit determination section. */ + ad((token, parser) -> parser.manageAttitudeDeterminationSection(token.getType() == TokenType.START)), + + /** User-defined parameters section. */ + USER((token, parser) -> parser.manageUserDefinedParametersSection(token.getType() == TokenType.START)), + + /** User-defined parameters section. */ + user((token, parser) -> parser.manageUserDefinedParametersSection(token.getType() == TokenType.START)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AcmDataSubStructureKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process one token. + * @param token token to process + * @param parser ACM file parser + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final AcmParser parser) { + return processor.process(token, parser); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param parser ACM file parser + * @return true of token was accepted + */ + boolean process(ParseToken token, AcmParser parser); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmElements.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmElements.java new file mode 100644 index 0000000000..62b8cb43f7 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmElements.java @@ -0,0 +1,43 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +/** Data elements types used in CCSDS {@link Acm Attitude Comprehensive Messages}. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AcmElements { + + /** Attitude state time history. */ + ATT, + + /** Physical characteristics. */ + PHYS, + + /** Covariance time history. */ + COV, + + /** Maneuver. */ + MAN, + + /** Attitude determination. */ + AD, + + /** User defined. */ + USER; + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadata.java new file mode 100644 index 0000000000..4c2a4cd4f4 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadata.java @@ -0,0 +1,344 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; + +import org.orekit.data.DataContext; +import org.orekit.files.ccsds.ndm.adm.AdmMetadata; +import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; +import org.orekit.files.ccsds.section.MetadataKey; +import org.orekit.time.AbsoluteDate; + +/** Meta-data for {@link AcmMetadata Attitude Comprehensive Message}. + * @since 12.0 + */ +public class AcmMetadata extends AdmMetadata { + + /** Specification of satellite catalog source. */ + private String catalogName; + + /** Unique satellite identification designator for the object. */ + private String objectDesignator; + + /** Programmatic Point Of Contact at originator. */ + private String originatorPOC; + + /** Position of Programmatic Point Of Contact at originator. */ + private String originatorPosition; + + /** Phone number of Programmatic Point Of Contact at originator. */ + private String originatorPhone; + + /** Email address of Programmatic Point Of Contact at originator. */ + private String originatorEmail; + + /** Address of Programmatic Point Of Contact at originator. */ + private String originatorAddress; + + /** Unique identifier of Orbit Data Message linked to this Orbit Data Message. */ + private String odmMessageLink; + + /** Epoch to which all relative times are referenced in data blocks; + * unless overridden by block-specific {@code EPOCH_TZERO} values. */ + private AbsoluteDate epochT0; + + /** List of elements of information data blocks included in this message. */ + private List acmDataElements; + + /** Time of the earliest data contained in the OCM. */ + private AbsoluteDate startTime; + + /** Time of the latest data contained in the OCM. */ + private AbsoluteDate stopTime; + + /** Difference (TAI – UTC) in seconds at epoch {@link #epochT0}. */ + private double taimutcT0; + + /** Epoch of next leap second. */ + private AbsoluteDate nextLeapEpoch; + + /** Difference (TAI – UTC) in seconds incorporated at {@link #nextLeapEpoch}. */ + private double nextLeapTaimutc; + + /** Create a new meta-data. + * @param dataContext data context + */ + public AcmMetadata(final DataContext dataContext) { + + // set up the few fields that have default values as per CCSDS standard + taimutcT0 = Double.NaN; + nextLeapTaimutc = Double.NaN; + + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + // we don't call super.checkMandatoryEntries() because + // all of the parameters considered mandatory at ADM level + // for APM and AEM are in fact optional in ACM + // only OBJECT_NAME, TIME_SYSTEM and EPOCH_TZERO are mandatory + checkNotNull(getObjectName(), AdmMetadataKey.OBJECT_NAME.name()); + checkNotNull(getTimeSystem(), MetadataKey.TIME_SYSTEM.name()); + checkNotNull(epochT0, AcmMetadataKey.EPOCH_TZERO.name()); + if (nextLeapEpoch != null) { + checkNotNaN(nextLeapTaimutc, AcmMetadataKey.NEXT_LEAP_TAIMUTC.name()); + } + } + + /** Get the international designator for the object. + * @return international designator for the object + */ + public String getInternationalDesignator() { + return getObjectID(); + } + + /** Set the international designator for the object. + * @param internationalDesignator international designator for the object + */ + public void setInternationalDesignator(final String internationalDesignator) { + setObjectID(internationalDesignator); + } + + /** Get the specification of satellite catalog source. + * @return specification of satellite catalog source + */ + public String getCatalogName() { + return catalogName; + } + + /** Set the specification of satellite catalog source. + * @param catalogName specification of satellite catalog source + */ + public void setCatalogName(final String catalogName) { + refuseFurtherComments(); + this.catalogName = catalogName; + } + + /** Get the unique satellite identification designator for the object. + * @return unique satellite identification designator for the object. + */ + public String getObjectDesignator() { + return objectDesignator; + } + + /** Set the unique satellite identification designator for the object. + * @param objectDesignator unique satellite identification designator for the object + */ + public void setObjectDesignator(final String objectDesignator) { + refuseFurtherComments(); + this.objectDesignator = objectDesignator; + } + + /** Get the programmatic Point Of Contact at originator. + * @return programmatic Point Of Contact at originator + */ + public String getOriginatorPOC() { + return originatorPOC; + } + + /** Set the programmatic Point Of Contact at originator. + * @param originatorPOC programmatic Point Of Contact at originator + */ + public void setOriginatorPOC(final String originatorPOC) { + refuseFurtherComments(); + this.originatorPOC = originatorPOC; + } + + /** Get the position of Programmatic Point Of Contact at originator. + * @return position of Programmatic Point Of Contact at originator + */ + public String getOriginatorPosition() { + return originatorPosition; + } + + /** Set the position of Programmatic Point Of Contact at originator. + * @param originatorPosition position of Programmatic Point Of Contact at originator + */ + public void setOriginatorPosition(final String originatorPosition) { + refuseFurtherComments(); + this.originatorPosition = originatorPosition; + } + + /** Get the phone number of Programmatic Point Of Contact at originator. + * @return phone number of Programmatic Point Of Contact at originator + */ + public String getOriginatorPhone() { + return originatorPhone; + } + + /** Set the phone number of Programmatic Point Of Contact at originator. + * @param originatorPhone phone number of Programmatic Point Of Contact at originator + */ + public void setOriginatorPhone(final String originatorPhone) { + refuseFurtherComments(); + this.originatorPhone = originatorPhone; + } + + /** Get the email address of Programmatic Point Of Contact at originator. + * @return email address of Programmatic Point Of Contact at originator + */ + public String getOriginatorEmail() { + return originatorEmail; + } + + /** Set the email address of Programmatic Point Of Contact at originator. + * @param originatorEmail email address of Programmatic Point Of Contact at originator + */ + public void setOriginatorEmail(final String originatorEmail) { + refuseFurtherComments(); + this.originatorEmail = originatorEmail; + } + + /** Get the address of Programmatic Point Of Contact at originator. + * @return address of Programmatic Point Of Contact at originator + */ + public String getOriginatorAddress() { + return originatorAddress; + } + + /** Set the address of Programmatic Point Of Contact at originator. + * @param originatorAddress address of Programmatic Point Of Contact at originator + */ + public void setOriginatorAddress(final String originatorAddress) { + refuseFurtherComments(); + this.originatorAddress = originatorAddress; + } + + /** Get the Unique identifier of Orbit Data Message linked to this Attitude Data Message. + * @return Unique identifier of Orbit Data Message linked to this Attitude Data Message + */ + public String getOdmMessageLink() { + return odmMessageLink; + } + + /** Set the Unique identifier of Orbit Data Message linked to this Attitude Data Message. + * @param odmMessageLink Unique identifier of Orbit Data Message linked to this Attitude Data Message + */ + public void setOdmMessageLink(final String odmMessageLink) { + refuseFurtherComments(); + this.odmMessageLink = odmMessageLink; + } + + /** Get the epoch to which all relative times are referenced in data blocks. + * @return epoch to which all relative times are referenced in data blocks + */ + public AbsoluteDate getEpochT0() { + return epochT0; + } + + /** Set the epoch to which all relative times are referenced in data blocks. + * @param epochT0 epoch to which all relative times are referenced in data blocks + */ + public void setEpochT0(final AbsoluteDate epochT0) { + refuseFurtherComments(); + this.epochT0 = epochT0; + } + + /** Get the list of elements of information data blocks included in this message. + * @return list of elements of information data blocks included in this message + */ + public List getAcmDataElements() { + return acmDataElements; + } + + /** Set the list of elements of information data blocks included in this message. + * @param acmDataElements list of elements of information data blocks included in this message + */ + public void setAcmDataElements(final List acmDataElements) { + refuseFurtherComments(); + this.acmDataElements = acmDataElements; + } + + /** Get the time of the earliest data contained in the OCM. + * @return time of the earliest data contained in the OCM + */ + public AbsoluteDate getStartTime() { + return startTime; + } + + /** Set the time of the earliest data contained in the OCM. + * @param startTime time of the earliest data contained in the OCM + */ + public void setStartTime(final AbsoluteDate startTime) { + refuseFurtherComments(); + this.startTime = startTime; + } + + /** Get the time of the latest data contained in the OCM. + * @return time of the latest data contained in the OCM + */ + public AbsoluteDate getStopTime() { + return stopTime; + } + + /** Set the time of the latest data contained in the OCM. + * @param stopTime time of the latest data contained in the OCM + */ + public void setStopTime(final AbsoluteDate stopTime) { + refuseFurtherComments(); + this.stopTime = stopTime; + } + + /** Get the difference (TAI – UTC) in seconds at epoch {@link #getEpochT0()}. + * @return difference (TAI – UTC) in seconds at epoch {@link #getEpochT0()} + */ + public double getTaimutcT0() { + return taimutcT0; + } + + /** Set the difference (TAI – UTC) in seconds at epoch {@link #getEpochT0()}. + * @param taimutcT0 difference (TAI – UTC) in seconds at epoch {@link #getEpochT0()} + */ + public void setTaimutcT0(final double taimutcT0) { + refuseFurtherComments(); + this.taimutcT0 = taimutcT0; + } + + /** Get the epoch of next leap second. + * @return epoch of next leap second + */ + public AbsoluteDate getNextLeapEpoch() { + return nextLeapEpoch; + } + + /** Set the epoch of next leap second. + * @param nextLeapEpoch epoch of next leap second + */ + public void setNextLeapEpoch(final AbsoluteDate nextLeapEpoch) { + refuseFurtherComments(); + this.nextLeapEpoch = nextLeapEpoch; + } + + /** Get the difference (TAI – UTC) in seconds incorporated at epoch {@link #getNextLeapEpoch()}. + * @return difference (TAI – UTC) in seconds incorporated at epoch {@link #getNextLeapEpoch()} + */ + public double getNextLeapTaimutc() { + return nextLeapTaimutc; + } + + /** Set the difference (TAI – UTC) in seconds incorporated at epoch {@link #getNextLeapEpoch()}. + * @param nextLeapTaimutc difference (TAI – UTC) in seconds incorporated at epoch {@link #getNextLeapEpoch()} + */ + public void setNextLeapTaimutc(final double nextLeapTaimutc) { + refuseFurtherComments(); + this.nextLeapTaimutc = nextLeapTaimutc; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadataKey.java new file mode 100644 index 0000000000..05b0db6f89 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadataKey.java @@ -0,0 +1,113 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.utils.units.Unit; + + +/** Keys for {@link AcmMetadata ACM container} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AcmMetadataKey { + + /** International designator for the object as assigned by the UN Committee + * on Space Research (COSPAR) and the US National Space Science Data Center (NSSDC). */ + INTERNATIONAL_DESIGNATOR((token, context, container) -> token.processAsNormalizedString(container::setInternationalDesignator)), + + /** Specification of satellite catalog source. */ + CATALOG_NAME((token, context, container) -> token.processAsNormalizedString(container::setCatalogName)), + + /** Unique satellite identification designator for the object. */ + OBJECT_DESIGNATOR((token, context, container) -> token.processAsNormalizedString(container::setObjectDesignator)), + + /** Programmatic Point Of Contact at originator. */ + ORIGINATOR_POC((token, context, container) -> token.processAsFreeTextString(container::setOriginatorPOC)), + + /** Position of Programmatic Point Of Contact at originator. */ + ORIGINATOR_POSITION((token, context, container) -> token.processAsFreeTextString(container::setOriginatorPosition)), + + /** Phone number of Programmatic Point Of Contact at originator. */ + ORIGINATOR_PHONE((token, context, container) -> token.processAsFreeTextString(container::setOriginatorPhone)), + + /** Email address of Programmatic Point Of Contact at originator. */ + ORIGINATOR_EMAIL((token, context, container) -> token.processAsFreeTextString(container::setOriginatorEmail)), + + /** Address of Programmatic Point Of Contact at originator. */ + ORIGINATOR_ADDRESS((token, context, container) -> token.processAsFreeTextString(container::setOriginatorAddress)), + + /** Unique identifier of Orbit Data Message linked to this Attitude Data Message. */ + ODM_MSG_LINK((token, context, container) -> token.processAsFreeTextString(container::setOdmMessageLink)), + + /** Default epoch to which all relative times are referenced in data blocks, + * unless overridden by block-specific {@link #EPOCH_TZERO} values. */ + EPOCH_TZERO((token, context, container) -> token.processAsDate(container::setEpochT0, context)), + + /** List of elements of information data blocks included in this message. */ + ACM_DATA_ELEMENTS((token, context, container) -> token.processAsEnumsList(AcmElements.class, container::setAcmDataElements)), + + /** Start time entry. */ + START_TIME((token, context, container) -> token.processAsDate(container::setStartTime, context)), + + /** Stop time entry. */ + STOP_TIME((token, context, container) -> token.processAsDate(container::setStopTime, context)), + + /** Difference (TAI – UTC) in seconds at epoch {@link #EPOCH_TZERO}. */ + TAIMUTC_AT_TZERO((token, context, container) -> token.processAsDouble(Unit.SECOND, context.getParsedUnitsBehavior(), + container::setTaimutcT0)), + + /** Epoch of next leap second. */ + NEXT_LEAP_EPOCH((token, context, container) -> token.processAsDate(container::setNextLeapEpoch, context)), + + /** Difference (TAI – UTC) in seconds incorporated at {@link #NEXT_LEAP_EPOCH}. */ + NEXT_LEAP_TAIMUTC((token, context, container) -> token.processAsDouble(Unit.SECOND, context.getParsedUnitsBehavior(), + container::setNextLeapTaimutc)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AcmMetadataKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final AcmMetadata container) { + return processor.process(token, context, container); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AcmMetadata container); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadataWriter.java new file mode 100644 index 0000000000..9364cbe458 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmMetadataWriter.java @@ -0,0 +1,116 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; +import java.util.stream.Collectors; + +import org.orekit.files.ccsds.definitions.TimeConverter; +import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.section.KvnStructureKey; +import org.orekit.files.ccsds.section.MetadataKey; +import org.orekit.files.ccsds.section.XmlStructureKey; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.units.Unit; + +/** + * Writer for Common metadata for CCSDS Attitude Comprehensive Messages. + * + * @author Luc Maisonobe + * @since 12.0 + */ +class AcmMetadataWriter extends AbstractWriter { + + /** Metadata. */ + private final AcmMetadata metadata; + + /** Converter for dates. */ + private final TimeConverter timeConverter; + + /** Simple constructor. + * @param metadata metadata to write + * @param timeConverter converter for dates + */ + AcmMetadataWriter(final AcmMetadata metadata, final TimeConverter timeConverter) { + super(XmlStructureKey.metadata.name(), KvnStructureKey.META.name()); + this.metadata = metadata; + this.timeConverter = timeConverter; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + generator.writeComments(metadata.getComments()); + + // object + generator.writeEntry(AdmMetadataKey.OBJECT_NAME.name(), + metadata.getObjectName(), null, false); + generator.writeEntry(AcmMetadataKey.INTERNATIONAL_DESIGNATOR.name(), + metadata.getInternationalDesignator(), null, false); + generator.writeEntry(AcmMetadataKey.CATALOG_NAME.name(), + metadata.getCatalogName(), null, false); + generator.writeEntry(AcmMetadataKey.OBJECT_DESIGNATOR.name(), + metadata.getObjectDesignator(), null, false); + + // originator + generator.writeEntry(AcmMetadataKey.ORIGINATOR_POC.name(), + metadata.getOriginatorPOC(), null, false); + generator.writeEntry(AcmMetadataKey.ORIGINATOR_POSITION.name(), + metadata.getOriginatorPosition(), null, false); + generator.writeEntry(AcmMetadataKey.ORIGINATOR_PHONE.name(), + metadata.getOriginatorPhone(), null, false); + generator.writeEntry(AcmMetadataKey.ORIGINATOR_EMAIL.name(), + metadata.getOriginatorEmail(), null, false); + generator.writeEntry(AcmMetadataKey.ORIGINATOR_ADDRESS.name(), + metadata.getOriginatorAddress(), null, false); + + // messages + generator.writeEntry(AcmMetadataKey.ODM_MSG_LINK.name(), + metadata.getOdmMessageLink(), null, false); + + // time + if (metadata.getCenter() != null) { + generator.writeEntry(AdmMetadataKey.CENTER_NAME.name(), metadata.getCenter().getName(), null, false); + } + generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), + metadata.getTimeSystem(), false); + generator.writeEntry(AcmMetadataKey.EPOCH_TZERO.name(), timeConverter, + metadata.getEpochT0(), true, true); + + // definitions + if (metadata.getAcmDataElements() != null) { + generator.writeEntry(AcmMetadataKey.ACM_DATA_ELEMENTS.name(), + metadata.getAcmDataElements().stream().map(e -> e.name()).collect(Collectors.toList()), false); + } + + // other times + generator.writeEntry(AcmMetadataKey.START_TIME.name(), timeConverter, + metadata.getStartTime(), false, false); + generator.writeEntry(AcmMetadataKey.STOP_TIME.name(), timeConverter, + metadata.getStopTime(), false, false); + generator.writeEntry(AcmMetadataKey.TAIMUTC_AT_TZERO.name(), metadata.getTaimutcT0(), Unit.SECOND, false); + if (metadata.getNextLeapEpoch() != null) { + generator.writeEntry(AcmMetadataKey.NEXT_LEAP_EPOCH.name(), timeConverter, + metadata.getNextLeapEpoch(), true, true); + generator.writeEntry(AcmMetadataKey.NEXT_LEAP_TAIMUTC.name(), metadata.getNextLeapTaimutc(), Unit.SECOND, true); + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmParser.java new file mode 100644 index 0000000000..34a286fa8b --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmParser.java @@ -0,0 +1,589 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.regex.Pattern; + +import org.orekit.data.DataContext; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; +import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; +import org.orekit.files.ccsds.ndm.adm.AdmParser; +import org.orekit.files.ccsds.ndm.odm.UserDefined; +import org.orekit.files.ccsds.ndm.odm.ocm.Ocm; +import org.orekit.files.ccsds.section.HeaderProcessingState; +import org.orekit.files.ccsds.section.KvnStructureProcessingState; +import org.orekit.files.ccsds.section.MetadataKey; +import org.orekit.files.ccsds.section.Segment; +import org.orekit.files.ccsds.section.XmlStructureProcessingState; +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; +import org.orekit.files.ccsds.utils.lexical.UserDefinedXmlTokenBuilder; +import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder; +import org.orekit.files.ccsds.utils.parsing.ProcessingState; +import org.orekit.files.general.AttitudeEphemerisFileParser; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.IERSConventions; + +/** A parser for the CCSDS ACM (Attitude Comprehensive Message). + * @author Luc Maisonobe + * @since 12.0 + */ +public class AcmParser extends AdmParser implements AttitudeEphemerisFileParser { + + /** Pattern for splitting strings at blanks. */ + private static final Pattern SPLIT_AT_BLANKS = Pattern.compile("\\s+"); + + /** File header. */ + private AdmHeader header; + + /** Metadata for current observation block. */ + private AcmMetadata metadata; + + /** Context binding valid for current metadata. */ + private ContextBinding context; + + /** Attitude state histories logical blocks. */ + private List attitudeBlocks; + + /** Current attitude state metadata. */ + private AttitudeStateHistoryMetadata currentAttitudeStateHistoryMetadata; + + /** Current attitude state time history being read. */ + private List currentAttitudeStateHistory; + + /** Physical properties logical block. */ + private AttitudePhysicalProperties physicBlock; + + /** Covariance logical blocks. */ + private List covarianceBlocks; + + /** Current covariance metadata. */ + private AttitudeCovarianceHistoryMetadata currentCovarianceHistoryMetadata; + + /** Current covariance history being read. */ + private List currentCovarianceHistory; + + /** Maneuver logical blocks. */ + private List maneuverBlocks; + + /** Current maneuver history being read. */ + private AttitudeManeuver currentManeuver; + + /** Attitude determination logical block. */ + private AttitudeDetermination attitudeDeterminationBlock; + + /** Attitude determination sensor logical block. */ + private AttitudeDeterminationSensor attitudeDeterminationSensorBlock; + + /** User defined parameters logical block. */ + private UserDefined userDefinedBlock; + + /** Processor for global message structure. */ + private ProcessingState structureProcessor; + + /** + * Complete constructor. + *

          + * Calling this constructor directly is not recommended. Users should rather use + * {@link org.orekit.files.ccsds.ndm.ParserBuilder#buildAcmParser() + * parserBuilder.buildAcmParser()}. + *

          + * @param conventions IERS Conventions + * @param simpleEOP if true, tidal effects are ignored when interpolating EOP + * @param dataContext used to retrieve frames, time scales, etc. + * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 + */ + public AcmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, + final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(Acm.ROOT, Acm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, null, + parsedUnitsBehavior, filters); + } + + /** {@inheritDoc} */ + @Override + public Map getSpecialXmlElementsBuilders() { + + final Map builders = super.getSpecialXmlElementsBuilders(); + + // special handling of user-defined parameters + builders.put(UserDefined.USER_DEFINED_XML_TAG, new UserDefinedXmlTokenBuilder()); + + return builders; + + } + + /** {@inheritDoc} */ + @Override + public Acm parse(final DataSource source) { + return parseMessage(source); + } + + /** {@inheritDoc} */ + @Override + public AdmHeader getHeader() { + return header; + } + + /** {@inheritDoc} */ + @Override + public void reset(final FileFormat fileFormat) { + header = new AdmHeader(); + metadata = null; + context = null; + attitudeBlocks = null; + physicBlock = null; + covarianceBlocks = null; + maneuverBlocks = null; + attitudeDeterminationBlock = null; + userDefinedBlock = null; + if (fileFormat == FileFormat.XML) { + structureProcessor = new XmlStructureProcessingState(Acm.ROOT, this); + reset(fileFormat, structureProcessor); + } else { + structureProcessor = new KvnStructureProcessingState(this); + reset(fileFormat, new HeaderProcessingState(this)); + } + } + + /** {@inheritDoc} */ + @Override + public boolean prepareHeader() { + anticipateNext(new HeaderProcessingState(this)); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean inHeader() { + anticipateNext(structureProcessor); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean finalizeHeader() { + header.validate(header.getFormatVersion()); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean prepareMetadata() { + if (metadata != null) { + return false; + } + metadata = new AcmMetadata(getDataContext()); + context = new ContextBinding(this::getConventions, this::isSimpleEOP, this::getDataContext, + this::getParsedUnitsBehavior, metadata::getEpochT0, metadata::getTimeSystem, + () -> 0.0, () -> 1.0); + anticipateNext(this::processMetadataToken); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean inMetadata() { + anticipateNext(structureProcessor); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean finalizeMetadata() { + metadata.validate(header.getFormatVersion()); + anticipateNext(this::processDataSubStructureToken); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean prepareData() { + anticipateNext(this::processDataSubStructureToken); + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean inData() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean finalizeData() { + return true; + } + + /** Manage attitude state history section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean manageAttitudeStateSection(final boolean starting) { + if (starting) { + if (attitudeBlocks == null) { + // this is the first attitude block, we need to allocate the container + attitudeBlocks = new ArrayList<>(); + } + currentAttitudeStateHistoryMetadata = new AttitudeStateHistoryMetadata(); + currentAttitudeStateHistory = new ArrayList<>(); + anticipateNext(this::processAttitudeStateToken); + } else { + anticipateNext(structureProcessor); + attitudeBlocks.add(new AttitudeStateHistory(currentAttitudeStateHistoryMetadata, + currentAttitudeStateHistory)); + } + return true; + } + + /** Manage physical properties section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean managePhysicalPropertiesSection(final boolean starting) { + if (starting) { + physicBlock = new AttitudePhysicalProperties(metadata.getEpochT0()); + anticipateNext(this::processPhysicalPropertyToken); + } else { + anticipateNext(structureProcessor); + } + return true; + } + + /** Manage covariance history section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean manageCovarianceHistorySection(final boolean starting) { + if (starting) { + if (covarianceBlocks == null) { + // this is the first covariance block, we need to allocate the container + covarianceBlocks = new ArrayList<>(); + } + currentCovarianceHistoryMetadata = new AttitudeCovarianceHistoryMetadata(); + currentCovarianceHistory = new ArrayList<>(); + anticipateNext(this::processCovarianceToken); + } else { + anticipateNext(structureProcessor); + covarianceBlocks.add(new AttitudeCovarianceHistory(currentCovarianceHistoryMetadata, + currentCovarianceHistory)); + currentCovarianceHistoryMetadata = null; + currentCovarianceHistory = null; + } + return true; + } + + /** Manage maneuvers section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean manageManeuversSection(final boolean starting) { + if (starting) { + if (maneuverBlocks == null) { + // this is the first maneuver block, we need to allocate the container + maneuverBlocks = new ArrayList<>(); + } + currentManeuver = new AttitudeManeuver(); + anticipateNext(this::processManeuverToken); + } else { + anticipateNext(structureProcessor); + maneuverBlocks.add(currentManeuver); + currentManeuver = null; + } + return true; + } + + /** Manage attitude determination section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean manageAttitudeDeterminationSection(final boolean starting) { + if (starting) { + attitudeDeterminationBlock = new AttitudeDetermination(); + anticipateNext(this::processAttitudeDeterminationToken); + } else { + anticipateNext(structureProcessor); + } + return true; + } + + /** Manage attitude determination sensor section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean manageAttitudeDeterminationSensorSection(final boolean starting) { + if (starting) { + attitudeDeterminationSensorBlock = new AttitudeDeterminationSensor(); + anticipateNext(this::processAttitudeDeterminationSensorToken); + } else { + anticipateNext(this::processDataSubStructureToken); + } + return true; + } + + /** Manage user-defined parameters section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + */ + boolean manageUserDefinedParametersSection(final boolean starting) { + if (starting) { + userDefinedBlock = new UserDefined(); + anticipateNext(this::processUserDefinedToken); + } else { + anticipateNext(structureProcessor); + } + return true; + } + + /** {@inheritDoc} */ + @Override + public Acm build() { + + if (userDefinedBlock != null && userDefinedBlock.getParameters().isEmpty()) { + userDefinedBlock = null; + } + + final AcmData data = new AcmData(attitudeBlocks, physicBlock, covarianceBlocks, + maneuverBlocks, attitudeDeterminationBlock, userDefinedBlock); + data.validate(header.getFormatVersion()); + + return new Acm(header, Collections.singletonList(new Segment<>(metadata, data)), + getConventions(), getDataContext()); + + } + + /** Process one metadata token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processMetadataToken(final ParseToken token) { + inMetadata(); + try { + return token.getName() != null && + MetadataKey.valueOf(token.getName()).process(token, context, metadata); + } catch (IllegalArgumentException iaeM) { + try { + return AdmMetadataKey.valueOf(token.getName()).process(token, context, metadata); + } catch (IllegalArgumentException iaeD) { + try { + return AcmMetadataKey.valueOf(token.getName()).process(token, context, metadata); + } catch (IllegalArgumentException iaeC) { + // token has not been recognized + return false; + } + } + } + } + + /** Process one data substructure token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processDataSubStructureToken(final ParseToken token) { + try { + return token.getName() != null && + AcmDataSubStructureKey.valueOf(token.getName()).process(token, this); + } catch (IllegalArgumentException iae) { + // token has not been recognized + return false; + } + } + + /** Process one attitude state history data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processAttitudeStateToken(final ParseToken token) { + if (token.getName() != null && !token.getName().equals(Acm.ATT_LINE)) { + // we are in the section metadata part + try { + return AttitudeStateHistoryMetadataKey.valueOf(token.getName()). + process(token, context, currentAttitudeStateHistoryMetadata); + } catch (IllegalArgumentException iae) { + // token has not been recognized + return false; + } + } else { + // we are in the section data part + if (currentAttitudeStateHistory.isEmpty()) { + // we are starting the real data section, we can now check metadata is complete + currentAttitudeStateHistoryMetadata.validate(header.getFormatVersion()); + anticipateNext(this::processDataSubStructureToken); + } + if (token.getType() == TokenType.START || token.getType() == TokenType.STOP) { + return true; + } + try { + final String[] fields = SPLIT_AT_BLANKS.split(token.getRawContent().trim()); + final AbsoluteDate epoch = context.getTimeSystem().getConverter(context).parse(fields[0]); + return currentAttitudeStateHistory.add(new AttitudeState(currentAttitudeStateHistoryMetadata.getAttitudeType(), + currentAttitudeStateHistoryMetadata.getRateType(), + epoch, fields, 1)); + } catch (NumberFormatException | OrekitIllegalArgumentException e) { + throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString()); + } + } + } + + /** Process one physical property data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processPhysicalPropertyToken(final ParseToken token) { + anticipateNext(this::processDataSubStructureToken); + try { + return token.getName() != null && + AttitudePhysicalPropertiesKey.valueOf(token.getName()).process(token, context, physicBlock); + } catch (IllegalArgumentException iae) { + // token has not been recognized + return false; + } + } + + /** Process one covariance history history data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processCovarianceToken(final ParseToken token) { + if (token.getName() != null && !token.getName().equals(Ocm.COV_LINE)) { + // we are in the section metadata part + try { + return AttitudeCovarianceHistoryMetadataKey.valueOf(token.getName()). + process(token, context, currentCovarianceHistoryMetadata); + } catch (IllegalArgumentException iae) { + // token has not been recognized + return false; + } + } else { + // we are in the section data part + if (currentCovarianceHistory.isEmpty()) { + // we are starting the real data section, we can now check metadata is complete + currentCovarianceHistoryMetadata.validate(header.getFormatVersion()); + anticipateNext(this::processDataSubStructureToken); + } + if (token.getType() == TokenType.START || token.getType() == TokenType.STOP) { + return true; + } + try { + final String[] fields = SPLIT_AT_BLANKS.split(token.getRawContent().trim()); + currentCovarianceHistory.add(new AttitudeCovariance(currentCovarianceHistoryMetadata.getCovType(), + context.getTimeSystem().getConverter(context).parse(fields[0]), + fields, 1)); + return true; + } catch (NumberFormatException nfe) { + throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString()); + } + } + } + + /** Process one maneuver data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processManeuverToken(final ParseToken token) { + anticipateNext(this::processDataSubStructureToken); + try { + return token.getName() != null && + AttitudeManeuverKey.valueOf(token.getName()).process(token, context, currentManeuver); + } catch (IllegalArgumentException iae) { + // token has not been recognized + return false; + } + } + + /** Process one attitude determination data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processAttitudeDeterminationToken(final ParseToken token) { + anticipateNext(attitudeDeterminationSensorBlock != null ? + this::processAttitudeDeterminationSensorToken : + this::processDataSubStructureToken); + if (token.getName() == null) { + return false; + } + try { + return AttitudeDeterminationKey.valueOf(token.getName()).process(token, this, context, attitudeDeterminationBlock); + } catch (IllegalArgumentException iae1) { + // token has not been recognized + return false; + } + } + + /** Process one attitude determination sensor data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processAttitudeDeterminationSensorToken(final ParseToken token) { + anticipateNext(this::processAttitudeDeterminationToken); + if (token.getName() == null) { + return false; + } + try { + return AttitudeDeterminationSensorKey.valueOf(token.getName()).process(token, context, attitudeDeterminationSensorBlock); + } catch (IllegalArgumentException iae1) { + // token has not been recognized + attitudeDeterminationBlock.addSensor(attitudeDeterminationSensorBlock); + attitudeDeterminationSensorBlock = null; + return false; + } + } + + /** Process one user-defined parameter data token. + * @param token token to process + * @return true if token was processed, false otherwise + */ + private boolean processUserDefinedToken(final ParseToken token) { + anticipateNext(this::processDataSubStructureToken); + if ("COMMENT".equals(token.getName())) { + return token.getType() == TokenType.ENTRY ? userDefinedBlock.addComment(token.getContentAsNormalizedString()) : true; + } else if (token.getName().startsWith(UserDefined.USER_DEFINED_PREFIX)) { + if (token.getType() == TokenType.ENTRY) { + userDefinedBlock.addEntry(token.getName().substring(UserDefined.USER_DEFINED_PREFIX.length()), + token.getContentAsNormalizedString()); + } + return true; + } else { + // the token was not processed + return false; + } + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmSatelliteEphemeris.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmSatelliteEphemeris.java new file mode 100644 index 0000000000..ce25a4e09c --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmSatelliteEphemeris.java @@ -0,0 +1,76 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.Collections; +import java.util.List; + +import org.orekit.files.general.AttitudeEphemerisFile; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedAngularCoordinates; + +/** ACM ephemeris blocks for a single satellite. + * @author Luc Maisonobe + * @since 11.0 + */ +public class AcmSatelliteEphemeris + implements AttitudeEphemerisFile.SatelliteAttitudeEphemeris { + + /** Name of the object. */ + private final String name; + + /** The ephemeris data for the satellite. */ + private final List blocks; + + /** + * Create a container for the set of ephemeris blocks in the file that pertain to + * a single satellite. + * + * @param name name of the object. + * @param blocks containing ephemeris data for the satellite. + */ + public AcmSatelliteEphemeris(final String name, final List blocks) { + this.name = name; + this.blocks = blocks; + } + + /** {@inheritDoc} */ + @Override + public String getId() { + return name; + } + + /** {@inheritDoc} */ + @Override + public List getSegments() { + return Collections.unmodifiableList(blocks); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStart() { + return blocks.get(0).getStart(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStop() { + return blocks.get(blocks.size() - 1).getStop(); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmWriter.java new file mode 100644 index 0000000000..b9a358374d --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AcmWriter.java @@ -0,0 +1,140 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; + +import org.orekit.data.DataContext; +import org.orekit.files.ccsds.definitions.TimeSystem; +import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; +import org.orekit.files.ccsds.ndm.odm.UserDefinedWriter; +import org.orekit.files.ccsds.section.Segment; +import org.orekit.files.ccsds.section.XmlStructureKey; +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.generation.AbstractMessageWriter; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.IERSConventions; + + +/** + * Writer for CCSDS Attitude Comprehensive Message. + * + * @author Luc Maisonobe + * @since 12.0 + */ +public class AcmWriter extends AbstractMessageWriter, Acm> { + + /** Version number implemented. **/ + public static final double CCSDS_ACM_VERS = 2.0; + + /** Padding width for aligning the '=' sign. */ + public static final int KVN_PADDING_WIDTH = 33; + + /** Complete constructor. + *

          + * Calling this constructor directly is not recommended. Users should rather use + * {@link org.orekit.files.ccsds.ndm.WriterBuilder#buildAcmWriter() + * writerBuilder.buildAcmWriter()}. + *

          + * @param conventions IERS Conventions + * @param dataContext used to retrieve frames, time scales, etc. + */ + public AcmWriter(final IERSConventions conventions, final DataContext dataContext) { + super(Acm.ROOT, Acm.FORMAT_VERSION_KEY, CCSDS_ACM_VERS, + new ContextBinding( + () -> conventions, () -> false, () -> dataContext, + () -> ParsedUnitsBehavior.STRICT_COMPLIANCE, + () -> null, () -> TimeSystem.UTC, + () -> 0.0, () -> 1.0)); + } + + /** {@inheritDoc} */ + @Override + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final Segment segment) + throws IOException { + + // write the metadata + final ContextBinding oldContext = getContext(); + final AcmMetadata metadata = segment.getMetadata(); + setContext(new ContextBinding(oldContext::getConventions, + oldContext::isSimpleEOP, + oldContext::getDataContext, + oldContext::getParsedUnitsBehavior, + metadata::getEpochT0, + metadata::getTimeSystem, + () -> 0.0, () -> 1.0)); + new AcmMetadataWriter(metadata, getTimeConverter()).write(generator); + + // start data block + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(XmlStructureKey.data.name()); + } + + // attitude history + if (segment.getData().getAttitudeBlocks() != null && !segment.getData().getAttitudeBlocks().isEmpty()) { + for (final AttitudeStateHistory history : segment.getData().getAttitudeBlocks()) { + // write optional attitude history block + new AttitudeStateHistoryWriter(history, getTimeConverter()).write(generator); + } + } + + if (segment.getData().getPhysicBlock() != null) { + // write optional physical properties block + new AttitudePhysicalPropertiesWriter(segment.getData().getPhysicBlock()). + write(generator); + } + + // covariance history + if (segment.getData().getCovarianceBlocks() != null && !segment.getData().getCovarianceBlocks().isEmpty()) { + for (final AttitudeCovarianceHistory history : segment.getData().getCovarianceBlocks()) { + // write optional covariance history block + new AttitudeCovarianceHistoryWriter(history, getTimeConverter()).write(generator); + } + } + + if (segment.getData().getManeuverBlocks() != null && !segment.getData().getManeuverBlocks().isEmpty()) { + for (final AttitudeManeuver maneuver : segment.getData().getManeuverBlocks()) { + // write optional maneuver block + new AttitudeManeuverWriter(maneuver).write(generator); + } + } + + if (segment.getData().getAttitudeDeterminationBlock() != null) { + // write optional attitude determination block + new AttitudeDeterminationWriter(segment.getData().getAttitudeDeterminationBlock()). + write(generator); + } + + if (segment.getData().getUserDefinedBlock() != null) { + // write optional user defined parameters block + new UserDefinedWriter(AcmDataSubStructureKey.user.name(), + AcmDataSubStructureKey.USER.name(), + segment.getData().getUserDefinedBlock()). + write(generator); + } + + // stop data block + if (generator.getFormat() == FileFormat.XML) { + generator.exitSection(); + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovariance.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovariance.java new file mode 100644 index 0000000000..5325647e89 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovariance.java @@ -0,0 +1,79 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; + +import org.hipparchus.linear.DiagonalMatrix; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeStamped; +import org.orekit.utils.units.Unit; + +/** Covariance entry. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeCovariance implements TimeStamped { + + /** Type of the elements. */ + private final AttitudeCovarianceType type; + + /** Entry date. */ + private final AbsoluteDate date; + + /** Covariance matrix. */ + private final DiagonalMatrix matrix; + + /** Simple constructor. + * @param type type of the elements + * @param date entry date + * @param fields matrix diagonal elements + * @param first index of first field to consider + */ + public AttitudeCovariance(final AttitudeCovarianceType type, final AbsoluteDate date, + final String[] fields, final int first) { + final List units = type.getUnits(); + this.type = type; + this.date = date; + this.matrix = new DiagonalMatrix(units.size()); + for (int k = 0; k < matrix.getRowDimension(); ++k) { + matrix.setEntry(k, k, units.get(k).toSI(Double.parseDouble(fields[first + k]))); + } + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return date; + } + + /** Get the covariance matrix. + * @return covariance matrix + */ + public DiagonalMatrix getMatrix() { + return matrix; + } + + /** Get the type of the elements. + * @return type of the elements + */ + public AttitudeCovarianceType getType() { + return type; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistory.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistory.java new file mode 100644 index 0000000000..d70ab9a8f7 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistory.java @@ -0,0 +1,59 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.Collections; +import java.util.List; + +/** Covariance history. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeCovarianceHistory { + + /** Metadata. */ + private final AttitudeCovarianceHistoryMetadata metadata; + + /** Covariance. */ + private final List covariances; + + /** Simple constructor. + * @param metadata metadata + * @param covariances covariances + */ + public AttitudeCovarianceHistory(final AttitudeCovarianceHistoryMetadata metadata, + final List covariances) { + this.metadata = metadata; + this.covariances = covariances; + } + + /** Get metadata. + * @return metadata + */ + public AttitudeCovarianceHistoryMetadata getMetadata() { + return metadata; + } + + /** Get the covariances. + * @return covariances + */ + public List getCovariances() { + return Collections.unmodifiableList(covariances); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryMetadata.java new file mode 100644 index 0000000000..76ee9abf19 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryMetadata.java @@ -0,0 +1,155 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.section.CommentsContainer; + +/** Metadata for covariance history. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeCovarianceHistoryMetadata extends CommentsContainer { + + /** Covariance identification number. */ + private String covID; + + /** Identification number of previous covariance. */ + private String covPrevID; + + /** Basis of this covariance time history data. */ + private String covBasis; + + /** Identification number of the covariance determination or simulation upon which this covariance is based. */ + private String covBasisID; + + /** Reference frame of the covariance. */ + private FrameFacade covReferenceFrame; + + /** Covariance element set type. */ + private AttitudeCovarianceType covType; + + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public AttitudeCovarianceHistoryMetadata() { + // nothing to do + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + checkNotNull(covType, AttitudeCovarianceHistoryMetadataKey.COV_TYPE.name()); + } + + /** Get covariance identification number. + * @return covariance identification number + */ + public String getCovID() { + return covID; + } + + /** Set covariance identification number. + * @param covID covariance identification number + */ + public void setCovID(final String covID) { + refuseFurtherComments(); + this.covID = covID; + } + + /** Get identification number of previous covariance. + * @return identification number of previous covariance + */ + public String getCovPrevID() { + return covPrevID; + } + + /** Set identification number of previous covariance. + * @param covPrevID identification number of previous covariance + */ + public void setCovPrevID(final String covPrevID) { + refuseFurtherComments(); + this.covPrevID = covPrevID; + } + + /** Get basis of this covariance time history data. + * @return basis of this covariance time history data + */ + public String getCovBasis() { + return covBasis; + } + + /** Set basis of this covariance time history data. + * @param covBasis basis of this covariance time history data + */ + public void setCovBasis(final String covBasis) { + refuseFurtherComments(); + this.covBasis = covBasis; + } + + /** Get identification number of the orbit determination or simulation upon which this covariance is based. + * @return identification number of the orbit determination or simulation upon which this covariance is based + */ + public String getCovBasisID() { + return covBasisID; + } + + /** Set identification number of the orbit determination or simulation upon which this covariance is based. + * @param covBasisID identification number of the orbit determination or simulation upon which this covariance is based + */ + public void setCovBasisID(final String covBasisID) { + refuseFurtherComments(); + this.covBasisID = covBasisID; + } + + /** Get reference frame of the covariance. + * @return reference frame of the covariance + */ + public FrameFacade getCovReferenceFrame() { + return covReferenceFrame; + } + + /** Set reference frame of the covariance. + * @param covReferenceFrame the reference frame to be set + */ + public void setCovReferenceFrame(final FrameFacade covReferenceFrame) { + refuseFurtherComments(); + this.covReferenceFrame = covReferenceFrame; + } + + /** Get covariance element set type. + * @return covariance element set type + */ + public AttitudeCovarianceType getCovType() { + return covType; + } + + /** Set covariance element set type. + * @param covType covariance element set type + */ + public void setCovType(final AttitudeCovarianceType covType) { + refuseFurtherComments(); + this.covType = covType; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryMetadataKey.java new file mode 100644 index 0000000000..4f7ad2530c --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryMetadataKey.java @@ -0,0 +1,85 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; + + +/** Keys for {@link AttitudeCovarianceHistoryMetadata covariance history container} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeCovarianceHistoryMetadataKey { + + /** Comment entry. */ + COMMENT((token, context, container) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), + + /** Covariance identification number. */ + COV_ID((token, context, container) -> token.processAsFreeTextString(container::setCovID)), + + /** Identification number of previous covariance. */ + COV_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setCovPrevID)), + + /** Basis of this covariance time history data. */ + COV_BASIS((token, context, container) -> token.processAsFreeTextString(container::setCovBasis)), + + /** Identification number of the orbit determination or simulation upon which this covariance is based.*/ + COV_BASIS_ID((token, context, container) -> token.processAsFreeTextString(container::setCovBasisID)), + + /** Reference frame of the covariance. */ + COV_REF_FRAME((token, context, container) -> token.processAsFrame(container::setCovReferenceFrame, context, true, true, true)), + + /** Covariance element set type. + * @see AttitudeCovarianceType + */ + COV_TYPE((token, context, container) -> token.processAsEnum(AttitudeCovarianceType.class, container::setCovType)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AttitudeCovarianceHistoryMetadataKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final AttitudeCovarianceHistoryMetadata container) { + return processor.process(token, context, container); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AttitudeCovarianceHistoryMetadata container); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryWriter.java new file mode 100644 index 0000000000..52e2bb2120 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceHistoryWriter.java @@ -0,0 +1,97 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; +import java.util.List; + +import org.hipparchus.linear.DiagonalMatrix; +import org.orekit.files.ccsds.definitions.TimeConverter; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.AccurateFormatter; +import org.orekit.utils.units.Unit; + +/** Writer for covariance history data. + * @author Luc Maisonobe + * @since 12.0 + */ +class AttitudeCovarianceHistoryWriter extends AbstractWriter { + + /** Covariance history block. */ + private final AttitudeCovarianceHistory history; + + /** Converter for dates. */ + private final TimeConverter timeConverter; + + /** Create a writer. + * @param covarianceHistory covariance history to write + * @param timeConverter converter for dates + */ + AttitudeCovarianceHistoryWriter(final AttitudeCovarianceHistory covarianceHistory, + final TimeConverter timeConverter) { + super(AcmDataSubStructureKey.cov.name(), AcmDataSubStructureKey.COV.name()); + this.history = covarianceHistory; + this.timeConverter = timeConverter; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // covariance history block + final AttitudeCovarianceHistoryMetadata metadata = history.getMetadata(); + generator.writeComments(metadata.getComments()); + + // identifiers + generator.writeEntry(AttitudeCovarianceHistoryMetadataKey.COV_ID.name(), metadata.getCovID(), null, false); + generator.writeEntry(AttitudeCovarianceHistoryMetadataKey.COV_PREV_ID.name(), metadata.getCovPrevID(), null, false); + generator.writeEntry(AttitudeCovarianceHistoryMetadataKey.COV_BASIS.name(), metadata.getCovBasis(), null, false); + generator.writeEntry(AttitudeCovarianceHistoryMetadataKey.COV_BASIS_ID.name(), metadata.getCovBasisID(), null, false); + + // references + if (metadata.getCovReferenceFrame() != null) { + generator.writeEntry(AttitudeCovarianceHistoryMetadataKey.COV_REF_FRAME.name(), metadata.getCovReferenceFrame().getName(), null, false); + } + + // elements + generator.writeEntry(AttitudeCovarianceHistoryMetadataKey.COV_TYPE.name(), metadata.getCovType(), false); + + // data + final List units = metadata.getCovType().getUnits(); + for (final AttitudeCovariance covariance : history.getCovariances()) { + final DiagonalMatrix matrix = covariance.getMatrix(); + final StringBuilder line = new StringBuilder(); + line.append(generator.dateToString(timeConverter, covariance.getDate())); + for (int k = 0; k < units.size(); ++k) { + line.append(' '); + line.append(AccurateFormatter.format(units.get(k).fromSI(matrix.getEntry(k, k)))); + } + if (generator.getFormat() == FileFormat.XML) { + generator.writeEntry(Acm.COV_LINE, line.toString(), null, true); + } else { + generator.writeRawData(line); + generator.newLine(); + } + + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceType.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceType.java new file mode 100644 index 0000000000..9373408d49 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeCovarianceType.java @@ -0,0 +1,72 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.orekit.utils.units.Unit; + +/** Attitude covariance set type used in CCSDS {@link Acm Attitude Comprehensive Messages}. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeCovarianceType { + + // CHECKSTYLE: stop MultipleStringLiterals check + + /** Angles. */ + ANGLE("°²", "°²", "°²"), + + /** Angles and gyro biases. */ + ANGLE_GYROBIAS("°²", "°²", "°²", "°²/s²", "°²/s²", "°²/s²"), + + /** Angles and angular velocities. */ + ANGLE_ANGVEL("°²", "°²", "°²", "°²/s²", "°²/s²", "°²/s²"), + + /** Quaternion. */ + QUATERNION("n/a", "n/a", "n/a", "n/a"), + + /** Quaternion and gyro biases. */ + QUATERNION_GYROBIAS("n/a", "n/a", "n/a", "n/a", "°²/s²", "°²/s²", "°²/s²"), + + /** Quaternion and angular velocities. */ + QUATERNION_ANGVEL("n/a", "n/a", "n/a", "n/a", "°²/s²", "°²/s²", "°²/s²"); + + // CHECKSTYLE: resume MultipleStringLiterals check + + /** Elements units. */ + private final List units; + + /** Simple constructor. + * @param unitsSpecifications elements units specifications + */ + AttitudeCovarianceType(final String... unitsSpecifications) { + this.units = Stream.of(unitsSpecifications). + map(s -> Unit.parse(s)). + collect(Collectors.toList()); + } + + /** Get the elements units. + * @return elements units (they correspond to diagonal elements, hence they are already squared) + */ + public List getUnits() { + return units; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDetermination.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDetermination.java new file mode 100644 index 0000000000..dec3a12fc7 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDetermination.java @@ -0,0 +1,316 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.definitions.AdMethodType; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; +import org.orekit.files.ccsds.section.CommentsContainer; + +/** Attitude determination data. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeDetermination extends CommentsContainer { + + /** Endpoints (i.e. frames A, B and their relationship). */ + private final AttitudeEndpoints endpoints; + + /** Identification number. */ + private String id; + + /** Identification of previous orbit determination. */ + private String prevId; + + /** Attitude determination method. */ + private AdMethodType method; + + /** Source of attitude estimate. */ + private String source; + + /** Rotation order for Euler angles. */ + private RotationOrder eulerRotSeq; + + /** Number of states for {@link AdMethodType#EKF}, {@link AdMethodType#BATCH} or {@link AdMethodType#FILTER_SMOOTHER}. */ + private int nbStates; + + /** Attitude states. */ + private AttitudeElementsType attitudeStates; + + /** Type of attitude error state. */ + private AttitudeCovarianceType covarianceType; + + /** Attitude rate states. */ + private RateElementsType rateStates; + + /** Rate random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS}. */ + private double sigmaU; + + /** Angle random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS}. */ + private double sigmaV; + + /** Process noise standard deviation if {@link #rateStates} is {@link RateElementsType#ANGVEL}. */ + private double rateProcessNoiseStdDev; + + /** Sensors used. */ + private List sensorsUsed; + + /** Simple constructor. + */ + public AttitudeDetermination() { + endpoints = new AttitudeEndpoints(); + sensorsUsed = new ArrayList<>(); + nbStates = -1; + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + checkNotNull(attitudeStates, AttitudeDeterminationKey.ATTITUDE_STATES.name()); + endpoints.checkExternalFrame(AttitudeDeterminationKey.REF_FRAME_A, + AttitudeDeterminationKey.REF_FRAME_B); + + // check sensors in increasing number + for (int number = 1; number <= sensorsUsed.size(); ++number) { + final AttitudeDeterminationSensor sensor = findSensor(number); + if (sensor != null) { + sensor.validate(version); + } else { + // no sensor has the expected index + throw new OrekitException(OrekitMessages.CCSDS_MISSING_SENSOR_INDEX, number); + } + + } + + } + + /** Find sensor by number. + * @param number number of the sensor + * @return sensor with specified number, or null if not found + */ + private AttitudeDeterminationSensor findSensor(final int number) { + for (final AttitudeDeterminationSensor sensor : sensorsUsed) { + if (sensor.getSensorNumber() == number) { + return sensor; + } + } + return null; + } + + /** Get the endpoints (i.e. frames A, B and their relationship). + * @return endpoints + */ + public AttitudeEndpoints getEndpoints() { + return endpoints; + } + + /** Get identification number. + * @return identification number + */ + public String getId() { + return id; + } + + /** Set identification number. + * @param id identification number + */ + public void setId(final String id) { + this.id = id; + } + + /** Get identification of previous orbit determination. + * @return identification of previous orbit determination + */ + public String getPrevId() { + return prevId; + } + + /** Set identification of previous orbit determination. + * @param prevId identification of previous orbit determination + */ + public void setPrevId(final String prevId) { + this.prevId = prevId; + } + + /** Get attitude determination method. + * @return attitude determination method + */ + public AdMethodType getMethod() { + return method; + } + + /** Set attitude determination method. + * @param method attitude determination method + */ + public void setMethod(final AdMethodType method) { + this.method = method; + } + + /** Get source of attitude estimate. + * @return source of attitude estimate + */ + public String getSource() { + return source; + } + + /** Set source of attitude estimate. + * @param source source of attitude estimate + */ + public void setSource(final String source) { + this.source = source; + } + + /** Get the rotation order for Euler angles. + * @return rotation order for Euler angles + */ + public RotationOrder getEulerRotSeq() { + return eulerRotSeq; + } + + /** Set the rotation order for Euler angles. + * @param eulerRotSeq rotation order for Euler angles + */ + public void setEulerRotSeq(final RotationOrder eulerRotSeq) { + this.eulerRotSeq = eulerRotSeq; + } + + /** Get number of states for {@link AdMethodType#EKF}, {@link AdMethodType#BATCH} or {@link AdMethodType#FILTER_SMOOTHER}. + * @return number of states + */ + public int getNbStates() { + return nbStates; + } + + /** Set number of states for {@link AdMethodType#EKF}, {@link AdMethodType#BATCH} or {@link AdMethodType#FILTER_SMOOTHER}. + * @param nbStates number of states + */ + public void setNbStates(final int nbStates) { + this.nbStates = nbStates; + } + + /** Get attitude states. + * @return attitude states + */ + public AttitudeElementsType getAttitudeStates() { + return attitudeStates; + } + + /** Set attitude states. + * @param attitudeStates attitude states + */ + public void setAttitudeStates(final AttitudeElementsType attitudeStates) { + this.attitudeStates = attitudeStates; + } + + /** Get type of attitude error state. + * @return type of attitude error state + */ + public AttitudeCovarianceType getCovarianceType() { + return covarianceType; + } + + /** Set type of attitude error state. + * @param covarianceType type of attitude error state + */ + public void setCovarianceType(final AttitudeCovarianceType covarianceType) { + this.covarianceType = covarianceType; + } + + /** Get attitude rate states. + * @return attitude rate states + */ + public RateElementsType getRateStates() { + return rateStates; + } + + /** Set attitude rate states. + * @param rateStates attitude rate states + */ + public void setRateStates(final RateElementsType rateStates) { + this.rateStates = rateStates; + } + + /** Get rate random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS}. + * @return rate random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS} + */ + public double getSigmaU() { + return sigmaU; + } + + /** Set rate random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS}. + * @param sigmaU rate random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS} + */ + public void setSigmaU(final double sigmaU) { + this.sigmaU = sigmaU; + } + + /** Get angle random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS}. + * @return angle random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS} + */ + public double getSigmaV() { + return sigmaV; + } + + /** Set angle random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS}. + * @param sigmaV angle random walk if {@link #rateStates} is {@link RateElementsType#GYRO_BIAS} + */ + public void setSigmaV(final double sigmaV) { + this.sigmaV = sigmaV; + } + + /** Get process noise standard deviation if {@link #rateStates} is {@link RateElementsType#ANGVEL}. + * @return process noise standard deviation if {@link #rateStates} is {@link RateElementsType#ANGVEL} + */ + public double getRateProcessNoiseStdDev() { + return rateProcessNoiseStdDev; + } + + /** Set process noise standard deviation if {@link #rateStates} is {@link RateElementsType#ANGVEL}. + * @param rateProcessNoiseStdDev process noise standard deviation if {@link #rateStates} is {@link RateElementsType#ANGVEL} + */ + public void setRateProcessNoiseStdDev(final double rateProcessNoiseStdDev) { + this.rateProcessNoiseStdDev = rateProcessNoiseStdDev; + } + + /** Get sensors used. + * @return sensors used + */ + public List getSensorsUsed() { + return Collections.unmodifiableList(sensorsUsed); + } + + /** Add a sensor used. + * @param sensor sensor to add + */ + public void addSensor(final AttitudeDeterminationSensor sensor) { + for (final AttitudeDeterminationSensor existing : sensorsUsed) { + if (sensor.getSensorNumber() == existing.getSensorNumber()) { + throw new OrekitException(OrekitMessages.CCSDS_SENSOR_INDEX_ALREADY_USED, sensor.getSensorNumber()); + } + } + sensorsUsed.add(sensor); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationKey.java new file mode 100644 index 0000000000..8b248aff3b --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationKey.java @@ -0,0 +1,118 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.definitions.AdMethodType; +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; + + +/** Keys for {@link AttitudeDetermination attitude determination data} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeDeterminationKey { + + /** Comment entry. */ + COMMENT((token, parser, context, container) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), + + /** Identification number. */ + AD_ID((token, parser, context, container) -> token.processAsFreeTextString(container::setId)), + + /** Identification of previous attitude determination. */ + AD_PREV_ID((token, parser, context, container) -> token.processAsFreeTextString(container::setPrevId)), + + /** Attitude determination method. */ + AD_METHOD((token, parser, context, container) -> token.processAsEnum(AdMethodType.class, container::setMethod)), + + /** Source of attitude estimate. */ + ATTITUDE_SOURCE((token, parser, context, container) -> token.processAsFreeTextString(container::setSource)), + + /** Rotation sequence entry. */ + EULER_ROT_SEQ((token, parser, context, container) -> token.processAsRotationOrder(container::setEulerRotSeq)), + + /** Number of states. */ + NUMBER_STATES((token, parser, context, container) -> token.processAsInteger(container::setNbStates)), + + /** Attitude states. */ + ATTITUDE_STATES((token, parser, context, container) -> token.processAsEnum(AttitudeElementsType.class, container::setAttitudeStates)), + + /** Type of attitude error state. */ + COV_TYPE((token, parser, context, container) -> token.processAsEnum(AttitudeCovarianceType.class, container::setCovarianceType)), + + /** Reference frame defining the starting point of the transformation. */ + REF_FRAME_A((token, parser, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, false)), + + /** Reference frame defining the end point of the transformation. */ + REF_FRAME_B((token, parser, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameB, context, false, false, true)), + + /** Attitude rate states. */ + RATE_STATES((token, parser, context, container) -> token.processAsEnum(RateElementsType.class, container::setRateStates)), + + /** Rate random walk if {@link #RATE_STATES} is {@link RateElementsType#GYRO_BIAS}. */ + SIGMA_U((token, parser, context, container) -> token.processAsDouble(Units.DEG_PER_S_3_2, context.getParsedUnitsBehavior(), + container::setSigmaU)), + + /** Angle random walk if {@link #RATE_STATES} is {@link RateElementsType#GYRO_BIAS}. */ + SIGMA_V((token, parser, context, container) -> token.processAsDouble(Units.DEG_PER_S_1_2, context.getParsedUnitsBehavior(), + container::setSigmaV)), + + /** Process noise standard deviation if {@link #RATE_STATES} is {@link RateElementsType#ANGVEL}. */ + RATE_PROCESS_NOISE_STDDEV((token, parser, context, container) -> token.processAsDouble(Units.DEG_PER_S_3_2, context.getParsedUnitsBehavior(), + container::setRateProcessNoiseStdDev)), + + /** Sensor block sub-structure. */ + SENSOR((token, parser, context, container) -> parser.manageAttitudeDeterminationSensorSection(token.getType() == TokenType.START)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AttitudeDeterminationKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param parser ACM file parser + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final AcmParser parser, final ContextBinding context, + final AttitudeDetermination container) { + return processor.process(token, parser, context, container); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param parser ACM file parser + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, AcmParser parser, ContextBinding context, AttitudeDetermination container); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationSensor.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationSensor.java new file mode 100644 index 0000000000..c2cb722a65 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationSensor.java @@ -0,0 +1,141 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.section.CommentsContainer; + +/** Attitude determination sensor data. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeDeterminationSensor extends CommentsContainer { + + /** Sensor number. */ + private int sensorNumber; + + /** Sensor used. */ + private String sensorUsed; + + /** Number of noise elements for sensor. */ + private int nbSensorNoiseCovariance; + + /** Standard deviation of sensor noises for sensor. */ + private double[] sensorNoiseCovariance; + + /** Frequency of sensor data. */ + private double sensorFrequency; + + /** Simple constructor. + */ + public AttitudeDeterminationSensor() { + nbSensorNoiseCovariance = -1; + sensorFrequency = Double.NaN; + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + checkNotNegative(sensorNumber, AttitudeDeterminationSensorKey.SENSOR_NUMBER.name()); + checkNotNull(sensorUsed, AttitudeDeterminationSensorKey.SENSOR_USED.name()); + if (nbSensorNoiseCovariance >= 0) { + final int n = sensorNoiseCovariance == null ? 0 : sensorNoiseCovariance.length; + if (nbSensorNoiseCovariance != n) { + throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_ELEMENTS, + nbSensorNoiseCovariance, n); + } + } + } + + /** Get number of the sensor. + * @return number of the sensor + */ + public int getSensorNumber() { + return sensorNumber; + } + + /** Set number of the sensor. + * @param sensorNumber number of the sensor + */ + public void setSensorNumber(final int sensorNumber) { + this.sensorNumber = sensorNumber; + } + + /** Get sensor used. + * @return sensor used + */ + public String getSensorUsed() { + return sensorUsed; + } + + /** Set sensor used. + * @param sensorUsed sensor used + */ + public void setSensorUsed(final String sensorUsed) { + this.sensorUsed = sensorUsed; + } + + /** Get number of noise elements for sensor. + * @return number of noise elements for sensor + */ + public int getNbSensorNoiseCovariance() { + return nbSensorNoiseCovariance; + } + + /** Set number of noise elements for sensor. + * @param n number of noise elements for sensor + */ + public void setNbSensorNoiseCovariance(final int n) { + nbSensorNoiseCovariance = n; + } + + /** Get standard deviation of sensor noise for sensor. + * @return standard deviation of sensor noise for sensor + */ + public double[] getSensorNoiseCovariance() { + return sensorNoiseCovariance == null ? null : sensorNoiseCovariance.clone(); + } + + /** Set standard deviation of sensor noise for sensor. + * @param stddev standard deviation of sensor noise + */ + public void setSensorNoiseCovariance(final double[] stddev) { + if (stddev.length != nbSensorNoiseCovariance) { + throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_ELEMENTS, + nbSensorNoiseCovariance, stddev.length); + } + sensorNoiseCovariance = stddev.clone(); + } + + /** Get frequency of sensor data for sensor. + * @return frequency of sensor data for sensor + */ + public double getSensorFrequency() { + return sensorFrequency; + } + + /** Set frequency of sensor data for sensor. + * @param frequency frequency of sensor data for sensor + */ + public void setSensorFrequency(final double frequency) { + sensorFrequency = frequency; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationSensorKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationSensorKey.java new file mode 100644 index 0000000000..1024bc1cc4 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationSensorKey.java @@ -0,0 +1,79 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.utils.units.Unit; + + +/** Keys for {@link AttitudeDetermination attitude determination data} sensor entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeDeterminationSensorKey { + + /** Sensor number. */ + SENSOR_NUMBER((token, context, container) -> token.processAsInteger(container::setSensorNumber)), + + /** Sensor used. */ + SENSOR_USED((token, context, container) -> token.processAsUppercaseString(container::setSensorUsed)), + + /** Number of noise elements. */ + NUMBER_SENSOR_NOISE_COVARIANCE((token, context, container) -> token.processAsInteger(container::setNbSensorNoiseCovariance)), + + /** Standard deviation of sensor noises. */ + SENSOR_NOISE_STDDEV((token, context, container) -> token.processAsDoubleArray(Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setSensorNoiseCovariance)), + + /** Frequency of sensor data. */ + SENSOR_FREQUENCY((token, context, container) -> token.processAsDouble(Unit.HERTZ, context.getParsedUnitsBehavior(), + container::setSensorFrequency)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AttitudeDeterminationSensorKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, + final AttitudeDeterminationSensor container) { + return processor.process(token, context, container); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AttitudeDeterminationSensor container); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationWriter.java new file mode 100644 index 0000000000..b02939baf2 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeDeterminationWriter.java @@ -0,0 +1,97 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; + +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.AccurateFormatter; +import org.orekit.utils.units.Unit; + +/** Writer for attitude determination data. + * @author Luc Maisonobe + * @since 12.0 + */ +class AttitudeDeterminationWriter extends AbstractWriter { + + /** Attitude determination block. */ + private final AttitudeDetermination ad; + + /** Create a writer. + * @param attitudeDetermination attitude determination to write + */ + AttitudeDeterminationWriter(final AttitudeDetermination attitudeDetermination) { + super(AcmDataSubStructureKey.ad.name(), AcmDataSubStructureKey.AD.name()); + this.ad = attitudeDetermination; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // attitude determination block + generator.writeComments(ad.getComments()); + + // identifiers + generator.writeEntry(AttitudeDeterminationKey.AD_ID.name(), ad.getId(), null, false); + generator.writeEntry(AttitudeDeterminationKey.AD_PREV_ID.name(), ad.getPrevId(), null, false); + if (ad.getMethod() != null) { + generator.writeEntry(AttitudeDeterminationKey.AD_METHOD.name(), ad.getMethod().name(), null, false); + } + + generator.writeEntry(AttitudeDeterminationKey.ATTITUDE_SOURCE.name(), ad.getSource(), null, false); + + // parameters + generator.writeEntry(AttitudeDeterminationKey.EULER_ROT_SEQ.name(), ad.getEulerRotSeq(), false); + generator.writeEntry(AttitudeDeterminationKey.NUMBER_STATES.name(), ad.getNbStates(), false); + generator.writeEntry(AttitudeDeterminationKey.ATTITUDE_STATES.name(), ad.getAttitudeStates(), true); + generator.writeEntry(AttitudeDeterminationKey.COV_TYPE.name(), ad.getCovarianceType(), false); + generator.writeEntry(AttitudeDeterminationKey.REF_FRAME_A.name(), ad.getEndpoints().getFrameA().getName(), null, false); + generator.writeEntry(AttitudeDeterminationKey.REF_FRAME_B.name(), ad.getEndpoints().getFrameB().getName(), null, false); + generator.writeEntry(AttitudeDeterminationKey.RATE_STATES.name(), ad.getRateStates(), false); + generator.writeEntry(AttitudeDeterminationKey.SIGMA_U.name(), ad.getSigmaU(), Units.DEG_PER_S_3_2, false); + generator.writeEntry(AttitudeDeterminationKey.SIGMA_V.name(), ad.getSigmaV(), Units.DEG_PER_S_1_2, false); + generator.writeEntry(AttitudeDeterminationKey.RATE_PROCESS_NOISE_STDDEV.name(), ad.getRateProcessNoiseStdDev(), Units.DEG_PER_S_3_2, false); + + // sensors + for (final AttitudeDeterminationSensor sensor : ad.getSensorsUsed()) { + generator.enterSection(AttitudeDeterminationKey.SENSOR.name()); + generator.writeEntry(AttitudeDeterminationSensorKey.SENSOR_NUMBER.name(), sensor.getSensorNumber(), true); + generator.writeEntry(AttitudeDeterminationSensorKey.SENSOR_USED.name(), sensor.getSensorUsed(), null, true); + final double[] stddevDouble = sensor.getSensorNoiseCovariance(); + if (stddevDouble != null) { + generator.writeEntry(AttitudeDeterminationSensorKey.NUMBER_SENSOR_NOISE_COVARIANCE.name(), stddevDouble.length, true); + final StringBuilder stddev = new StringBuilder(); + for (int k = 0; k < stddevDouble.length; ++k) { + if (k > 0) { + stddev.append(' '); + } + stddev.append(AccurateFormatter.format(Unit.DEGREE.fromSI(stddevDouble[k]))); + } + generator.writeEntry(AttitudeDeterminationSensorKey.SENSOR_NOISE_STDDEV.name(), + stddev.toString(), Unit.DEGREE, false); + } + generator.writeEntry(AttitudeDeterminationSensorKey.SENSOR_FREQUENCY.name(), sensor.getSensorFrequency(), Unit.HERTZ, false); + generator.exitSection(); + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeElementsType.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeElementsType.java new file mode 100644 index 0000000000..3ca1f73f4f --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeElementsType.java @@ -0,0 +1,110 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.orekit.utils.units.Unit; + +/** Attitude element set type used in CCSDS {@link Acm Attitude Comprehensive Messages}. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeElementsType { + + // CHECKSTYLE: stop MultipleStringLiterals check + + /** Quaternion. */ + QUATERNION("Quaternion", + "n/a", "n/a", "n/a", "n/a") { + /** {@inheritDoc} */ + @Override + public Rotation toRotation(final RotationOrder order, final double[] elements) { + return new Rotation(elements[3], elements[0], elements[1], elements[2], true); + } + }, + + /** Euler angles. */ + EULER_ANGLES("Euler angles", + "°", "°", "°") { + /** {@inheritDoc} */ + @Override + public Rotation toRotation(final RotationOrder order, final double[] elements) { + return new Rotation(order, RotationConvention.FRAME_TRANSFORM, + elements[0], elements[1], elements[2]); + } + }, + + /** Direction cosine matrix. */ + DCM("Direction cosine matrix", + "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a") { + /** {@inheritDoc} */ + @Override + public Rotation toRotation(final RotationOrder order, final double[] elements) { + return new Rotation(new double[][] { + { elements[0], elements[3], elements[6] }, + { elements[1], elements[4], elements[7] }, + { elements[2], elements[5], elements[8] } + }, 1.0e-10); + } + }; + + // CHECKSTYLE: resume MultipleStringLiterals check + + /** Description. */ + private final String description; + + /** Elements units. */ + private final List units; + + /** Simple constructor. + * @param description description + * @param unitsSpecifications elements units specifications + */ + AttitudeElementsType(final String description, final String... unitsSpecifications) { + this.description = description; + this.units = Stream.of(unitsSpecifications). + map(s -> Unit.parse(s)). + collect(Collectors.toList()); + } + + /** Get the elements units. + * @return elements units + */ + public List getUnits() { + return units; + } + + /** Convert to rotation. + * @param order rotation order for Euler angles + * @param elements elements values in SI units + * @return rotation + */ + public abstract Rotation toRotation(RotationOrder order, double[] elements); + + /** {@inheritDoc} */ + @Override + public String toString() { + return description; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuver.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuver.java new file mode 100644 index 0000000000..dca196a18e --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuver.java @@ -0,0 +1,247 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.section.CommentsContainer; + +/** Maneuver entry. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeManeuver extends CommentsContainer { + + /** Maneuver identification number. */ + private String id; + + /** Identification number of previous maneuver. */ + private String prevID; + + /** Purpose of the maneuver. */ + private String manPurpose; + + /** Start time of actual maneuver, relative to t₀. */ + private double beginTime; + + /** End time of actual maneuver, relative to t₀. */ + private double endTime; + + /** Duration. */ + private double duration; + + /** Actuator used. */ + private String actuatorUsed; + + /** Target momentum (if purpose is momentum desaturation). */ + private Vector3D targetMomentum; + + /** Reference frame for {@link #targetMomentum}. */ + private FrameFacade targetMomFrame; + + /** Target attitude (if purpose is attitude adjustment). */ + private Rotation targetAttitude; + + /** Target spin rate (if purpose is spin rate adjustment). */ + private double targetSpinRate; + + /** Build an uninitialized maneuver. + */ + public AttitudeManeuver() { + beginTime = Double.NaN; + endTime = Double.NaN; + duration = Double.NaN; + targetSpinRate = Double.NaN; + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + checkNotNull(manPurpose, AttitudeManeuverKey.MAN_PURPOSE.name()); + checkNotNaN(beginTime, AttitudeManeuverKey.MAN_BEGIN_TIME.name()); + if (!Double.isNaN(endTime + duration)) { + throw new OrekitException(OrekitMessages.CCSDS_INCOMPATIBLE_KEYS_BOTH_USED, + AttitudeManeuverKey.MAN_END_TIME, + AttitudeManeuverKey.MAN_DURATION); + } + if (targetMomFrame != null) { + checkNotNull(targetMomentum, AttitudeManeuverKey.TARGET_MOMENTUM.name()); + } + } + + /** Get maneuver identification number. + * @return maneuver identification number + */ + public String getID() { + return id; + } + + /** Set maneuver identification number. + * @param manId maneuver identification number + */ + public void setID(final String manId) { + refuseFurtherComments(); + this.id = manId; + } + + /** Get identification number of previous maneuver. + * @return identification number of previous maneuver + */ + public String getPrevID() { + return prevID; + } + + /** Set identification number of previous maneuver. + * @param prevID identification number of previous maneuver + */ + public void setPrevID(final String prevID) { + refuseFurtherComments(); + this.prevID = prevID; + } + + /** Get purpose of maneuver. + * @return purpose of maneuver + */ + public String getManPurpose() { + return manPurpose; + } + + /** Set purpose of maneuver. + * @param manPurpose purpose of maneuver + */ + public void setManPurpose(final String manPurpose) { + refuseFurtherComments(); + this.manPurpose = manPurpose; + } + + /** Get start time of actual maneuver, relative to t₀. + * @return start time of actual maneuver, relative to t₀ + */ + public double getBeginTime() { + return beginTime; + } + + /** Set start time of actual maneuver, relative to t₀. + * @param beginTime start time of actual maneuver, relative to t₀ + */ + public void setBeginTime(final double beginTime) { + this.beginTime = beginTime; + } + + /** Get end time of actual maneuver, relative to t₀. + * @return end time of actual maneuver, relative to t₀ + */ + public double getEndTime() { + return endTime; + } + + /** Set end time of actual maneuver, relative to t₀. + * @param endTime end time of actual maneuver, relative to t₀ + */ + public void setEndTime(final double endTime) { + this.endTime = endTime; + } + + /** Get duration. + * @return duration + */ + public double getDuration() { + return duration; + } + + /** Set duration. + * @param duration duration + */ + public void setDuration(final double duration) { + this.duration = duration; + } + + /** Get the actuator used. + * @return actuator used + */ + public String getActuatorUsed() { + return actuatorUsed; + } + + /** Set actuator used. + * @param actuatorUsed actuator used + */ + public void setActuatorUsed(final String actuatorUsed) { + this.actuatorUsed = actuatorUsed; + } + + /** Get target momentum (if purpose is momentum desaturation). + * @return target momentum + */ + public Vector3D getTargetMomentum() { + return targetMomentum; + } + + /** Set target momentum (if purpose is momentum desaturation). + * @param targetMomentum target momentum + */ + public void setTargetMomentum(final Vector3D targetMomentum) { + this.targetMomentum = targetMomentum; + } + + /** Get reference frame for {@link #getTargetMomentum()}. + * @return reference frame for {@link #getTargetMomentum()} + */ + public FrameFacade getTargetMomFrame() { + return targetMomFrame; + } + + /** Set reference frame for {@link #getTargetMomentum()}. + * @param targetMomFrame reference frame for {@link #getTargetMomentum()} + */ + public void setTargetMomFrame(final FrameFacade targetMomFrame) { + this.targetMomFrame = targetMomFrame; + } + + /** Get target attitude (if purpose is attitude adjustment). + * @return target attitude + */ + public Rotation getTargetAttitude() { + return targetAttitude; + } + + /** Set target attitude (if purpose is attitude adjustment). + * @param targetAttitude target attitude + */ + public void setTargetAttitude(final Rotation targetAttitude) { + this.targetAttitude = targetAttitude; + } + + /** Get target spin rate (if purpose is spin rate adjustment). + * @return target spin rate + */ + public double getTargetSpinRate() { + return targetSpinRate; + } + + /** Set target spin rate (if purpose is spin rate adjustment). + * @param targetSpinRate target spin rate + */ + public void setTargetSpinRate(final double targetSpinRate) { + this.targetSpinRate = targetSpinRate; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuverKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuverKey.java new file mode 100644 index 0000000000..aea35787a5 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuverKey.java @@ -0,0 +1,125 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; +import org.orekit.utils.units.Unit; + + +/** Keys for {@link AttitudeManeuver attitude maneuver} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeManeuverKey { + + /** Comment entry. */ + COMMENT((token, context, container) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), + + /** Maneuver identification number. */ + MAN_ID((token, context, container) -> token.processAsFreeTextString(container::setID)), + + /** Identification number of previous maneuver. */ + MAN_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setPrevID)), + + /** Purpose of the maneuver. */ + MAN_PURPOSE((token, context, container) -> token.processAsFreeTextString(container::setManPurpose)), + + /** Start time of actual maneuver, relative to t₀. */ + MAN_BEGIN_TIME((token, context, container) -> token.processAsDouble(Unit.SECOND, context.getParsedUnitsBehavior(), + container::setBeginTime)), + + /** End time of actual maneuver, relative to t₀. */ + MAN_END_TIME((token, context, container) -> token.processAsDouble(Unit.SECOND, context.getParsedUnitsBehavior(), + container::setEndTime)), + + /** Duration. */ + MAN_DURATION((token, context, container) -> token.processAsDouble(Unit.SECOND, context.getParsedUnitsBehavior(), + container::setDuration)), + + /** Actuator used. */ + ACTUATOR_USED((token, context, container) -> token.processAsFreeTextString(container::setActuatorUsed)), + + /** Target momentum. */ + TARGET_MOMENTUM((token, context, container) -> token.processAsVector(Units.N_M_S, context.getParsedUnitsBehavior(), + container::setTargetMomentum)), + + /** Target momentum frame. */ + TARGET_MOM_FRAME((token, context, container) -> token.processAsFrame(container::setTargetMomFrame, context, true, true, true)), + + /** Target attitude. */ + TARGET_ATTITUDE((token, context, container) -> { + try { + if (token.getType() == TokenType.ENTRY) { + final String[] fields = token.getRawContent().split("\\p{Space}+"); + if (fields.length == 4) { + container.setTargetAttitude(new Rotation(Double.parseDouble(fields[3]), + Double.parseDouble(fields[0]), + Double.parseDouble(fields[1]), + Double.parseDouble(fields[2]), + true)); + return true; + } + } else { + return true; + } + } catch (NumberFormatException nfe) { + // ignored, error handled below, together with wrong number of fields + } + throw token.generateException(null); + }), + + /** Target spin rate. */ + TARGET_SPINRATE((token, context, container) -> token.processAsDouble(Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setTargetSpinRate)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AttitudeManeuverKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param data data to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final AttitudeManeuver data) { + return processor.process(token, context, data); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param data data to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AttitudeManeuver data); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuverWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuverWriter.java new file mode 100644 index 0000000000..7077674892 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeManeuverWriter.java @@ -0,0 +1,95 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; + +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.AccurateFormatter; +import org.orekit.utils.units.Unit; + +/** Writer for attitude maneuver data. + * @author Luc Maisonobe + * @since 12.0 + */ +class AttitudeManeuverWriter extends AbstractWriter { + + /** Attitude maneuver block. */ + private final AttitudeManeuver man; + + /** Create a writer. + * @param attitudeManeuver attitude maneuver to write + */ + AttitudeManeuverWriter(final AttitudeManeuver attitudeManeuver) { + super(AcmDataSubStructureKey.man.name(), AcmDataSubStructureKey.MAN.name()); + this.man = attitudeManeuver; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // attitude maneuver block + generator.writeComments(man.getComments()); + + // identifiers + generator.writeEntry(AttitudeManeuverKey.MAN_ID.name(), man.getID(), null, false); + generator.writeEntry(AttitudeManeuverKey.MAN_PREV_ID.name(), man.getPrevID(), null, false); + generator.writeEntry(AttitudeManeuverKey.MAN_PURPOSE.name(), man.getManPurpose(), null, true); + + // time + generator.writeEntry(AttitudeManeuverKey.MAN_BEGIN_TIME.name(), man.getBeginTime(), Unit.SECOND, false); + generator.writeEntry(AttitudeManeuverKey.MAN_END_TIME.name(), man.getEndTime(), Unit.SECOND, false); + generator.writeEntry(AttitudeManeuverKey.MAN_DURATION.name(), man.getDuration(), Unit.SECOND, false); + + // actuator + generator.writeEntry(AttitudeManeuverKey.ACTUATOR_USED.name(), man.getActuatorUsed(), null, false); + + // target + if (man.getTargetMomentum() != null) { + final StringBuilder momentum = new StringBuilder(); + momentum.append(AccurateFormatter.format(Units.N_M_S.fromSI(man.getTargetMomentum().getX()))); + momentum.append(' '); + momentum.append(AccurateFormatter.format(Units.N_M_S.fromSI(man.getTargetMomentum().getY()))); + momentum.append(' '); + momentum.append(AccurateFormatter.format(Units.N_M_S.fromSI(man.getTargetMomentum().getZ()))); + generator.writeEntry(AttitudeManeuverKey.TARGET_MOMENTUM.name(), momentum.toString(), Units.N_M_S, true); + if (man.getTargetMomFrame() != null) { + generator.writeEntry(AttitudeManeuverKey.TARGET_MOM_FRAME.name(), man.getTargetMomFrame().getName(), null, false); + } + } + + if (man.getTargetAttitude() != null) { + final StringBuilder attitude = new StringBuilder(); + attitude.append(AccurateFormatter.format(man.getTargetAttitude().getQ1())); + attitude.append(' '); + attitude.append(AccurateFormatter.format(man.getTargetAttitude().getQ2())); + attitude.append(' '); + attitude.append(AccurateFormatter.format(man.getTargetAttitude().getQ3())); + attitude.append(' '); + attitude.append(AccurateFormatter.format(man.getTargetAttitude().getQ0())); + generator.writeEntry(AttitudeManeuverKey.TARGET_ATTITUDE.name(), attitude.toString(), null, true); + } + + generator.writeEntry(AttitudeManeuverKey.TARGET_SPINRATE.name(), man.getTargetSpinRate(), Units.DEG_PER_S, false); + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalProperties.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalProperties.java new file mode 100644 index 0000000000..607a8e676c --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalProperties.java @@ -0,0 +1,181 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.section.CommentsContainer; +import org.orekit.time.AbsoluteDate; + +/** Spacecraft physical properties. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudePhysicalProperties extends CommentsContainer { + + /** Drag coefficient. */ + private double dragCoefficient; + + /** Total mass at T₀. */ + private double wetMass; + + /** Mass without propellant. */ + private double dryMass; + + /** Reference frame for center of pressure. */ + private FrameFacade centerOfPressureReferenceFrame; + + /** Location of center of pressure. */ + private Vector3D centerOfPressure; + + /** Reference frame for inertia. */ + private FrameFacade inertiaReferenceFrame; + + /** Inertia matrix. */ + private RealMatrix inertiaMatrix; + + /** Simple constructor. + * @param epochT0 T0 epoch from file metadata + */ + public AttitudePhysicalProperties(final AbsoluteDate epochT0) { + dragCoefficient = Double.NaN; + wetMass = Double.NaN; + dryMass = Double.NaN; + inertiaMatrix = MatrixUtils.createRealMatrix(3, 3); + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + if (centerOfPressureReferenceFrame != null) { + checkNotNull(centerOfPressure, AttitudePhysicalPropertiesKey.CP.name()); + } + } + + /** Get the drag coefficient. + * @return the drag coefficient + */ + public double getDragCoefficient() { + return dragCoefficient; + } + + /** Set the the drag coefficient. + * @param dragCoefficient the drag coefficient + */ + public void setDragCoefficient(final double dragCoefficient) { + refuseFurtherComments(); + this.dragCoefficient = dragCoefficient; + } + + /** Get the total mass at T₀. + * @return total mass at T₀ + */ + public double getWetMass() { + return wetMass; + } + + /** Set the total mass at T₀. + * @param wetMass total mass at T₀ + */ + public void setWetMass(final double wetMass) { + refuseFurtherComments(); + this.wetMass = wetMass; + } + + /** Get the mass without propellant. + * @return mass without propellant + */ + public double getDryMass() { + return dryMass; + } + + /** Set the mass without propellant. + * @param dryMass mass without propellant + */ + public void setDryMass(final double dryMass) { + refuseFurtherComments(); + this.dryMass = dryMass; + } + + /** Get reference frame for center of pressure. + * @return reference frame for center of pressure + */ + public FrameFacade getCenterOfPressureReferenceFrame() { + return centerOfPressureReferenceFrame; + } + + /** Set reference frame for center of pressure. + * @param centerOfPressureReferenceFrame reference frame for center of pressure + */ + public void setCenterOfPressureReferenceFrame(final FrameFacade centerOfPressureReferenceFrame) { + this.centerOfPressureReferenceFrame = centerOfPressureReferenceFrame; + } + + /** Get the location of center of pressure. + * @return location of center of pressure + */ + public Vector3D getCenterOfPressure() { + return centerOfPressure; + } + + /** Set the location of center of pressure. + * @param centerOfPressure location of center of pressure + */ + public void setCenterOfPressure(final Vector3D centerOfPressure) { + this.centerOfPressure = centerOfPressure; + } + + /** Get reference frame for inertia. + * @return reference frame for inertia + */ + public FrameFacade getInertiaReferenceFrame() { + return inertiaReferenceFrame; + } + + /** Set reference frame for inertia. + * @param inertiaReferenceFrame reference frame for inertia + */ + public void setInertiaReferenceFrame(final FrameFacade inertiaReferenceFrame) { + this.inertiaReferenceFrame = inertiaReferenceFrame; + } + + /** Get the inertia matrix. + * @return the inertia matrix + */ + public RealMatrix getInertiaMatrix() { + return inertiaMatrix; + } + + /** Set an entry in the inertia matrix. + *

          + * Both I(j, k) and I(k, j) are set. + *

          + * @param j row index (must be between 0 and 3 (inclusive) + * @param k column index (must be between 0 and 3 (inclusive) + * @param entry value of the matrix entry + */ + public void setInertiaMatrixEntry(final int j, final int k, final double entry) { + refuseFurtherComments(); + inertiaMatrix.setEntry(j, k, entry); + inertiaMatrix.setEntry(k, j, entry); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalPropertiesKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalPropertiesKey.java new file mode 100644 index 0000000000..6b488d7021 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalPropertiesKey.java @@ -0,0 +1,113 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; +import org.orekit.utils.units.Unit; + + +/** Keys for {@link AttitudePhysicalProperties physical properties data} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudePhysicalPropertiesKey { + + /** Comment entry. */ + COMMENT((token, context, container) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), + + /** Drag coefficient. */ + DRAG_COEFF((token, context, container) -> token.processAsDouble(Unit.ONE, context.getParsedUnitsBehavior(), + container::setDragCoefficient)), + + /** Total mass at T₀. */ + WET_MASS((token, context, container) -> token.processAsDouble(Unit.KILOGRAM, context.getParsedUnitsBehavior(), + container::setWetMass)), + + /** Mass without propellant. */ + DRY_MASS((token, context, container) -> token.processAsDouble(Unit.KILOGRAM, context.getParsedUnitsBehavior(), + container::setDryMass)), + + /** Reference frame for center of pressure. */ + CP_REF_FRAME((token, context, container) -> token.processAsFrame(container::setCenterOfPressureReferenceFrame, context, false, false, true)), + + /** Center of pressure. */ + CP((token, context, container) -> token.processAsVector(Unit.METRE, context.getParsedUnitsBehavior(), + container::setCenterOfPressure)), + + /** Inertia reference frame. */ + INERTIA_REF_FRAME((token, context, container) -> token.processAsFrame(container::setInertiaReferenceFrame, context, false, false, true)), + + /** Moment of inertia about X-axis. */ + IXX((token, context, container) -> token.processAsDoublyIndexedDouble(0, 0, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about Y-axis. */ + IYY((token, context, container) -> token.processAsDoublyIndexedDouble(1, 1, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about Z-axis. */ + IZZ((token, context, container) -> token.processAsDoublyIndexedDouble(2, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the X and Y axes. */ + IXY((token, context, container) -> token.processAsDoublyIndexedDouble(0, 1, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the X and Z axes. */ + IXZ((token, context, container) -> token.processAsDoublyIndexedDouble(0, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the Y and Z axes. */ + IYZ((token, context, container) -> token.processAsDoublyIndexedDouble(1, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AttitudePhysicalPropertiesKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param data data to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final AttitudePhysicalProperties data) { + return processor.process(token, context, data); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param data data to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AttitudePhysicalProperties data); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalPropertiesWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalPropertiesWriter.java new file mode 100644 index 0000000000..b6b6bf5d90 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudePhysicalPropertiesWriter.java @@ -0,0 +1,90 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; + +import org.hipparchus.linear.RealMatrix; +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.AccurateFormatter; +import org.orekit.utils.units.Unit; + +/** Writer for physical properties data. + * @author Luc Maisonobe + * @since 12.0 + */ +class AttitudePhysicalPropertiesWriter extends AbstractWriter { + + /** Physical properties block. */ + private final AttitudePhysicalProperties phys; + + /** Create a writer. + * @param phys physical properties to write + */ + AttitudePhysicalPropertiesWriter(final AttitudePhysicalProperties phys) { + super(AcmDataSubStructureKey.phys.name(), AcmDataSubStructureKey.PHYS.name()); + this.phys = phys; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // physical properties block + generator.writeComments(phys.getComments()); + + // drag + generator.writeEntry(AttitudePhysicalPropertiesKey.DRAG_COEFF.name(), phys.getDragCoefficient(), Unit.ONE, false); + + // mass + generator.writeEntry(AttitudePhysicalPropertiesKey.WET_MASS.name(), phys.getWetMass(), Unit.KILOGRAM, false); + generator.writeEntry(AttitudePhysicalPropertiesKey.DRY_MASS.name(), phys.getDryMass(), Unit.KILOGRAM, false); + + // center of pressure + if (phys.getCenterOfPressureReferenceFrame() != null) { + generator.writeEntry(AttitudePhysicalPropertiesKey.CP_REF_FRAME.name(), phys.getCenterOfPressureReferenceFrame().getName(), null, false); + } + if (phys.getCenterOfPressure() != null) { + final StringBuilder cp = new StringBuilder(); + cp.append(AccurateFormatter.format(Unit.METRE.fromSI(phys.getCenterOfPressure().getX()))); + cp.append(' '); + cp.append(AccurateFormatter.format(Unit.METRE.fromSI(phys.getCenterOfPressure().getY()))); + cp.append(' '); + cp.append(AccurateFormatter.format(Unit.METRE.fromSI(phys.getCenterOfPressure().getZ()))); + generator.writeEntry(AttitudePhysicalPropertiesKey.CP.name(), cp.toString(), Unit.METRE, false); + } + + // inertia + if (phys.getInertiaReferenceFrame() != null) { + generator.writeEntry(AttitudePhysicalPropertiesKey.INERTIA_REF_FRAME.name(), phys.getInertiaReferenceFrame().getName(), null, false); + } + final RealMatrix inertia = phys.getInertiaMatrix(); + if (inertia != null) { + generator.writeEntry(AttitudePhysicalPropertiesKey.IXX.name(), inertia.getEntry(0, 0), Units.KG_M2, true); + generator.writeEntry(AttitudePhysicalPropertiesKey.IYY.name(), inertia.getEntry(1, 1), Units.KG_M2, true); + generator.writeEntry(AttitudePhysicalPropertiesKey.IZZ.name(), inertia.getEntry(2, 2), Units.KG_M2, true); + generator.writeEntry(AttitudePhysicalPropertiesKey.IXY.name(), inertia.getEntry(0, 1), Units.KG_M2, true); + generator.writeEntry(AttitudePhysicalPropertiesKey.IXZ.name(), inertia.getEntry(0, 2), Units.KG_M2, true); + generator.writeEntry(AttitudePhysicalPropertiesKey.IYZ.name(), inertia.getEntry(1, 2), Units.KG_M2, true); + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeState.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeState.java new file mode 100644 index 0000000000..1ea20b794a --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeState.java @@ -0,0 +1,120 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeStamped; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.units.Unit; + +/** Attitude state entry. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeState implements TimeStamped { + + /** Type of the elements. */ + private final AttitudeElementsType attitudeType; + + /** Type of the elements rates. */ + private final RateElementsType rateType; + + /** Entry date. */ + private final AbsoluteDate date; + + /** Attitude elements. */ + private final double[] elements; + + /** Simple constructor. + * @param attitudeType type of the elements + * @param rateType type of the elements rates + * (internally changed to {@link RateElementsType#NONE} if null) + * @param date entry date + * @param fields trajectory elements + * @param first index of first field to consider + */ + public AttitudeState(final AttitudeElementsType attitudeType, final RateElementsType rateType, + final AbsoluteDate date, final String[] fields, final int first) { + + this.attitudeType = attitudeType; + this.rateType = rateType == null ? RateElementsType.NONE : rateType; + + final List attUnits = this.attitudeType.getUnits(); + final List rateUnits = this.rateType.getUnits(); + + this.date = date; + this.elements = new double[attUnits.size() + rateUnits.size()]; + for (int i = 0; i < attUnits.size(); ++i) { + elements[i] = attUnits.get(i).toSI(Double.parseDouble(fields[first + i])); + } + for (int i = 0; i < rateUnits.size(); ++i) { + elements[attUnits.size() + i] = rateUnits.get(i).toSI(Double.parseDouble(fields[attUnits.size() + first + i])); + } + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return date; + } + + /** Get attitude elements. + * @return attitude elements + */ + public double[] getElements() { + return elements.clone(); + } + + /** Get the type of the elements. + * @return type of the elements + */ + public AttitudeElementsType getAttitudeType() { + return attitudeType; + } + + /** Get the type of the elements rates. + * @return type of the elements rates + */ + public RateElementsType getRateElementsType() { + return rateType; + } + + /** Get which derivatives of position are available in this state. + * @return a value indicating if the file contains rotation rate and/or acceleration + */ + public AngularDerivativesFilter getAvailableDerivatives() { + return rateType == RateElementsType.NONE ? + AngularDerivativesFilter.USE_R : + AngularDerivativesFilter.USE_RR; + } + + /** Convert to angular coordinates. + * @param order rotation order for Euler angles + * @return angular coordinates + */ + public TimeStampedAngularCoordinates toAngular(final RotationOrder order) { + return rateType.toAngular(getDate(), order, + attitudeType.toRotation(order, elements), + attitudeType.getUnits().size(), elements); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistory.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistory.java new file mode 100644 index 0000000000..aaf6212d7b --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistory.java @@ -0,0 +1,125 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.orekit.attitudes.BoundedAttitudeProvider; +import org.orekit.attitudes.TabulatedProvider; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.general.AttitudeEphemerisFile; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.TimeStampedAngularCoordinates; + +/** Attitude state history. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeStateHistory implements AttitudeEphemerisFile.AttitudeEphemerisSegment { + + /** Metadata. */ + private final AttitudeStateHistoryMetadata metadata; + + /** Trajectory states. */ + private final List states; + + /** Simple constructor. + * @param metadata metadata + * @param states attitude states + */ + public AttitudeStateHistory(final AttitudeStateHistoryMetadata metadata, + final List states) { + this.metadata = metadata; + this.states = states; + } + + /** Get metadata. + * @return metadata + */ + public AttitudeStateHistoryMetadata getMetadata() { + return metadata; + } + + /** Get the attitude states. + * @return attitude states + */ + public List getAttitudeStates() { + return Collections.unmodifiableList(states); + } + + /** {@inheritDoc} */ + @Override + public Frame getReferenceFrame() { + final Frame frame = metadata.getEndpoints().getFrameA().asFrame(); + if (frame == null) { + throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, + metadata.getEndpoints().getFrameA().getName()); + } + return frame; + } + + /** {@inheritDoc} */ + @Override + public int getInterpolationSamples() { + return 1; + } + + /** {@inheritDoc} */ + @Override + public String getInterpolationMethod() { + return "linear"; + } + + /** {@inheritDoc} */ + @Override + public AngularDerivativesFilter getAvailableDerivatives() { + return states.get(0).getAvailableDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public BoundedAttitudeProvider getAttitudeProvider() { + return new TabulatedProvider(getAngularCoordinates(), getInterpolationSamples(), + getAvailableDerivatives(), getStart(), getStop(), + getMetadata().getEndpoints()); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStart() { + return states.get(0).getDate(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStop() { + return states.get(states.size() - 1).getDate(); + } + + /** {@inheritDoc} */ + @Override + public List getAngularCoordinates() { + return states.stream().map(os -> os.toAngular(metadata.getEulerRotSeq())).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryMetadata.java new file mode 100644 index 0000000000..1f283b187c --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryMetadata.java @@ -0,0 +1,208 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; +import org.orekit.files.ccsds.section.CommentsContainer; + +/** Metadata for attitude state history. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AttitudeStateHistoryMetadata extends CommentsContainer { + + /** Endpoints (i.e. frames A, B and their relationship). */ + private final AttitudeEndpoints endpoints; + + /** Attitude identification number. */ + private String attID; + + /** Identification number of previous attitude. */ + private String attPrevID; + + /** Basis of this attitude state time history data. */ + private String attBasis; + + /** Identification number of the attitude determination or simulation upon which this attitude is based. */ + private String attBasisID; + + /** Rotation order for Euler angles. */ + private RotationOrder eulerRotSeq; + + /** Number of data states included (attitude components plus rates components). */ + private int nbStates; + + /** Attitude element set type. */ + private AttitudeElementsType attitudeType; + + /** Attitude rate element set type. */ + private RateElementsType rateType; + + /** Simple constructor. + */ + public AttitudeStateHistoryMetadata() { + endpoints = new AttitudeEndpoints(); + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + endpoints.checkExternalFrame(AttitudeStateHistoryMetadataKey.REF_FRAME_A, + AttitudeStateHistoryMetadataKey.REF_FRAME_B); + checkNotNull(attitudeType, AttitudeStateHistoryMetadataKey.ATT_TYPE.name()); + final int rateSize = rateType == null ? 0 : rateType.getUnits().size(); + if (nbStates != attitudeType.getUnits().size() + rateSize) { + throw new OrekitException(OrekitMessages.CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES, + attitudeType.toString(), rateType.toString(), + attitudeType.getUnits().size() + rateSize, nbStates); + } + if (attitudeType == AttitudeElementsType.EULER_ANGLES) { + checkNotNull(eulerRotSeq, AttitudeStateHistoryMetadataKey.EULER_ROT_SEQ.name()); + } + } + + /** Get the endpoints (i.e. frames A, B and their relationship). + * @return endpoints + */ + public AttitudeEndpoints getEndpoints() { + return endpoints; + } + + /** Get attitude identification number. + * @return attitude identification number + */ + public String getAttID() { + return attID; + } + + /** Set attitude identification number. + * @param attID attitude identification number + */ + public void setAttID(final String attID) { + refuseFurtherComments(); + this.attID = attID; + } + + /** Get identification number of previous attitude. + * @return identification number of previous attitude + */ + public String getAttPrevID() { + return attPrevID; + } + + /** Set identification number of previous attitude. + * @param attPrevID identification number of previous attitude + */ + public void setAttPrevID(final String attPrevID) { + refuseFurtherComments(); + this.attPrevID = attPrevID; + } + + /** Get basis of this attitude state time history data. + * @return basis of this attitude state time history data + */ + public String getAttBasis() { + return attBasis; + } + + /** Set basis of this attitude state time history data. + * @param attBasis basis of this attitude state time history data + */ + public void setAttBasis(final String attBasis) { + refuseFurtherComments(); + this.attBasis = attBasis; + } + + /** Get identification number of the orbit determination or simulation upon which this attitude is based. + * @return identification number of the orbit determination or simulation upon which this attitude is based + */ + public String getAttBasisID() { + return attBasisID; + } + + /** Set identification number of the orbit determination or simulation upon which this attitude is based. + * @param attBasisID identification number of the orbit determination or simulation upon which this attitude is based + */ + public void setAttBasisID(final String attBasisID) { + refuseFurtherComments(); + this.attBasisID = attBasisID; + } + + /** Get the rotation order for Euler angles. + * @return rotation order for Euler angles + */ + public RotationOrder getEulerRotSeq() { + return eulerRotSeq; + } + + /** Set the rotation order for Euler angles. + * @param eulerRotSeq rotation order for Euler angles + */ + public void setEulerRotSeq(final RotationOrder eulerRotSeq) { + this.eulerRotSeq = eulerRotSeq; + } + + /** Get the number of data states included (attitude components plus rates components). + * @return number of data states included (attitude components plus rates components) + */ + public int getNbStates() { + return nbStates; + } + + /** Set the number of data states included (attitude components plus rates components). + * @param nbStates number of data states included (attitude components plus rates components) + */ + public void setNbStates(final int nbStates) { + this.nbStates = nbStates; + } + + /** Get attitude element set type. + * @return attitude element set type + */ + public AttitudeElementsType getAttitudeType() { + return attitudeType; + } + + /** Set attitude element set type. + * @param attitudeType attitude element set type + */ + public void setAttitudeType(final AttitudeElementsType attitudeType) { + refuseFurtherComments(); + this.attitudeType = attitudeType; + } + + /** Get attitude rate element set type. + * @return attitude rate element set type + */ + public RateElementsType getRateType() { + return rateType; + } + + /** Set attitude rate element set type. + * @param rateType attitude rate element set type + */ + public void setRateType(final RateElementsType rateType) { + refuseFurtherComments(); + this.rateType = rateType; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryMetadataKey.java new file mode 100644 index 0000000000..e1b19cce89 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryMetadataKey.java @@ -0,0 +1,99 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; + + +/** Keys for {@link AttitudeStateHistoryMetadata attitude state history container} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum AttitudeStateHistoryMetadataKey { + + /** Comment entry. */ + COMMENT((token, context, container) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), + + /** Attitude identification number. */ + ATT_ID((token, context, container) -> token.processAsFreeTextString(container::setAttID)), + + /** Identification number of previous attitude. */ + ATT_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setAttPrevID)), + + /** Basis of this attitude state time history data. */ + ATT_BASIS((token, context, container) -> token.processAsFreeTextString(container::setAttBasis)), + + /** Identification number of the attitude determination or simulation upon which this attitude is based.*/ + ATT_BASIS_ID((token, context, container) -> token.processAsFreeTextString(container::setAttBasisID)), + + /** Reference frame defining the starting point of the transformation. */ + REF_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, false)), + + /** Reference frame defining the end point of the transformation. */ + REF_FRAME_B((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameB, context, false, false, true)), + + /** Rotation sequence entry. */ + EULER_ROT_SEQ((token, context, container) -> token.processAsRotationOrder(container::setEulerRotSeq)), + + /** Number of data states included. */ + NUMBER_STATES((token, context, container) -> token.processAsInteger(container::setNbStates)), + + /** Attitude element set type. + * @see AttitudeElementsType + */ + ATT_TYPE((token, context, container) -> token.processAsEnum(AttitudeElementsType.class, container::setAttitudeType)), + + /** Attitude rate element set type. + * @see RateElementsType + */ + RATE_TYPE((token, context, container) -> token.processAsEnum(RateElementsType.class, container::setRateType)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + AttitudeStateHistoryMetadataKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final AttitudeStateHistoryMetadata container) { + return processor.process(token, context, container); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param container container to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, AttitudeStateHistoryMetadata container); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryWriter.java new file mode 100644 index 0000000000..7d8004cf64 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/AttitudeStateHistoryWriter.java @@ -0,0 +1,108 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.orekit.files.ccsds.definitions.TimeConverter; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.AccurateFormatter; +import org.orekit.utils.units.Unit; + +/** Writer for attitude state history data. + * @author Luc Maisonobe + * @since 12.0 + */ +class AttitudeStateHistoryWriter extends AbstractWriter { + + /** Attitude state history block. */ + private final AttitudeStateHistory history; + + /** Converter for dates. */ + private final TimeConverter timeConverter; + + /** Create a writer. + * @param attitudeStateHistory trajectory state history to write + * @param timeConverter converter for dates + */ + AttitudeStateHistoryWriter(final AttitudeStateHistory attitudeStateHistory, + final TimeConverter timeConverter) { + super(AcmDataSubStructureKey.att.name(), AcmDataSubStructureKey.ATT.name()); + this.history = attitudeStateHistory; + this.timeConverter = timeConverter; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // trajectory state history block + final AttitudeStateHistoryMetadata metadata = history.getMetadata(); + generator.writeComments(metadata.getComments()); + + // identifiers + generator.writeEntry(AttitudeStateHistoryMetadataKey.ATT_ID.name(), metadata.getAttID(), null, false); + generator.writeEntry(AttitudeStateHistoryMetadataKey.ATT_PREV_ID.name(), metadata.getAttPrevID(), null, false); + generator.writeEntry(AttitudeStateHistoryMetadataKey.ATT_BASIS.name(), metadata.getAttBasis(), null, false); + generator.writeEntry(AttitudeStateHistoryMetadataKey.ATT_BASIS_ID.name(), metadata.getAttBasisID(), null, false); + + // references + generator.writeEntry(AttitudeStateHistoryMetadataKey.REF_FRAME_A.name(), metadata.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(AttitudeStateHistoryMetadataKey.REF_FRAME_B.name(), metadata.getEndpoints().getFrameB().getName(), null, true); + + // types + if (metadata.getAttitudeType() == AttitudeElementsType.EULER_ANGLES) { + generator.writeEntry(AttitudeStateHistoryMetadataKey.EULER_ROT_SEQ.name(), metadata.getEulerRotSeq(), true); + } + generator.writeEntry(AttitudeStateHistoryMetadataKey.NUMBER_STATES.name(), metadata.getNbStates(), true); + generator.writeEntry(AttitudeStateHistoryMetadataKey.ATT_TYPE.name(), metadata.getAttitudeType(), true); + if (metadata.getRateType() != null) { + generator.writeEntry(AttitudeStateHistoryMetadataKey.RATE_TYPE.name(), metadata.getRateType(), true); + } + + // data + final List attUnits = metadata.getAttitudeType().getUnits(); + final List rateUnits = metadata.getRateType() == null ? + Collections.emptyList() : metadata.getRateType().getUnits(); + for (final AttitudeState state : history.getAttitudeStates()) { + final double[] elements = state.getElements(); + final StringBuilder line = new StringBuilder(); + line.append(generator.dateToString(timeConverter, state.getDate())); + for (int i = 0; i < attUnits.size(); ++i) { + line.append(' '); + line.append(AccurateFormatter.format(attUnits.get(i).fromSI(elements[i]))); + } + for (int i = 0; i < rateUnits.size(); ++i) { + line.append(' '); + line.append(AccurateFormatter.format(rateUnits.get(i).fromSI(elements[attUnits.size() + i]))); + } + if (generator.getFormat() == FileFormat.XML) { + generator.writeEntry(Acm.ATT_LINE, line.toString(), null, true); + } else { + generator.writeRawData(line); + generator.newLine(); + } + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/RateElementsType.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/RateElementsType.java new file mode 100644 index 0000000000..4e0fcf4be0 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/RateElementsType.java @@ -0,0 +1,170 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.acm; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hipparchus.analysis.differentiation.UnivariateDerivative1; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.units.Unit; + +/** Attitude rate element set type used in CCSDS {@link Acm Attitude Comprehensive Messages}. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum RateElementsType { + + // CHECKSTYLE: stop MultipleStringLiterals check + + /** Angular velocity. */ + ANGVEL("Angular velocity", + "°/s", "°/s", "°/s") { + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates toAngular(final AbsoluteDate date, + final RotationOrder order, + final Rotation rotation, + final int first, + final double[] elements) { + return new TimeStampedAngularCoordinates(date, + rotation, + new Vector3D(elements[first], elements[first + 1], elements[first + 2]), + Vector3D.ZERO); + } + }, + + /** Quaternion derivatives. */ + Q_DOT("Quaternion derivatives", + "s⁻¹", "s⁻¹", "s⁻¹", "s⁻¹") { + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates toAngular(final AbsoluteDate date, + final RotationOrder order, + final Rotation rotation, + final int first, + final double[] elements) { + final UnivariateDerivative1 q0 = new UnivariateDerivative1(rotation.getQ0(), elements[first + 3]); + final UnivariateDerivative1 q1 = new UnivariateDerivative1(rotation.getQ1(), elements[first]); + final UnivariateDerivative1 q2 = new UnivariateDerivative1(rotation.getQ2(), elements[first + 1]); + final UnivariateDerivative1 q3 = new UnivariateDerivative1(rotation.getQ3(), elements[first + 2]); + return new TimeStampedAngularCoordinates(date, new FieldRotation<>(q0, q1, q2, q3, false)); + } + }, + + /** Euler rates. */ + EULER_RATE("Euler rates", + "°/s", "°/s", "°/s") { + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates toAngular(final AbsoluteDate date, + final RotationOrder order, + final Rotation rotation, + final int first, + final double[] elements) { + final double[] euler0 = rotation.getAngles(order, RotationConvention.FRAME_TRANSFORM); + final UnivariateDerivative1 alpha0 = new UnivariateDerivative1(euler0[0], elements[first]); + final UnivariateDerivative1 alpha1 = new UnivariateDerivative1(euler0[1], elements[first + 1]); + final UnivariateDerivative1 alpha2 = new UnivariateDerivative1(euler0[2], elements[first + 2]); + return new TimeStampedAngularCoordinates(date, new FieldRotation<>(order, RotationConvention.FRAME_TRANSFORM, + alpha0, alpha1, alpha2)); + } + }, + + /** Correction to gyro rates. */ + GYRO_BIAS("Gyro rate corrections", + "°/s", "°/s", "°/s") { + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates toAngular(final AbsoluteDate date, + final RotationOrder order, + final Rotation rotation, + final int first, + final double[] elements) { + throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE, name(), toString()); + } + }, + + /** No rates. */ + NONE("no rates") { + /** {@inheritDoc} */ + @Override + public TimeStampedAngularCoordinates toAngular(final AbsoluteDate date, + final RotationOrder order, + final Rotation rotation, + final int first, + final double[] elements) { + return new TimeStampedAngularCoordinates(date, rotation, Vector3D.ZERO, Vector3D.ZERO); + } + }; + + // CHECKSTYLE: resume MultipleStringLiterals check + + /** Description. */ + private final String description; + + /** Elements units. */ + private final List units; + + /** Simple constructor. + * @param description description + * @param unitsSpecifications elements units specifications + */ + RateElementsType(final String description, final String... unitsSpecifications) { + this.description = description; + this.units = Stream.of(unitsSpecifications). + map(s -> Unit.parse(s)). + collect(Collectors.toList()); + } + + /** Get the elements units. + * @return elements units + */ + public List getUnits() { + return units; + } + + /** Convert to angyla coordinates. + * @param date date + * @param order rotation order for Euler angles + * @param rotation rotation + * @param first index of the first element to consider + * @param elements elements values in SI units + * @return rotation + */ + public abstract TimeStampedAngularCoordinates toAngular(AbsoluteDate date, + RotationOrder order, + Rotation rotation, + int first, + double[] elements); + + /** {@inheritDoc} */ + @Override + public String toString() { + return description; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/package-info.java new file mode 100644 index 0000000000..668c4f3e1a --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/acm/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package contains class managing CCSDS Attitude Comprehensive Message. + * + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.files.ccsds.ndm.adm.acm; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/Aem.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/Aem.java index 81d5a9802e..47978836b0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/Aem.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/Aem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,11 +22,8 @@ import java.util.Map; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.NdmConstituent; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.general.AttitudeEphemerisFile; import org.orekit.utils.IERSConventions; import org.orekit.utils.TimeStampedAngularCoordinates; @@ -38,7 +35,7 @@ * @author Bryan Cazabonne * @since 10.2 */ -public class Aem extends NdmConstituent +public class Aem extends NdmConstituent implements AttitudeEphemerisFile { /** Root element for XML files. */ @@ -53,7 +50,7 @@ public class Aem extends NdmConstituent * @param conventions IERS conventions * @param dataContext used for creating frames, time scales, etc. */ - public Aem(final Header header, final List segments, + public Aem(final AdmHeader header, final List segments, final IERSConventions conventions, final DataContext dataContext) { super(header, segments, conventions, dataContext); } @@ -74,20 +71,4 @@ public Map getSatellites() { return ret; } - /** - * Check that, according to the CCSDS standard, every AEMBlock has the same time system. - */ - public void checkTimeSystems() { - TimeSystem referenceTimeSystem = null; - for (final AemSegment segment : getSegments()) { - final TimeSystem timeSystem = segment.getMetadata().getTimeSystem(); - if (referenceTimeSystem == null) { - referenceTimeSystem = timeSystem; - } else if (!referenceTimeSystem.equals(timeSystem)) { - throw new OrekitException(OrekitMessages.CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS, - referenceTimeSystem.name(), timeSystem.name()); - } - } - } - } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemData.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemData.java index 006f69102f..7febc161f1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadata.java index e8b1b9d480..79fa4c66ab 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,9 +17,12 @@ package org.orekit.files.ccsds.ndm.adm.aem; import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.definitions.FrameFacade; import org.orekit.files.ccsds.ndm.adm.AdmMetadata; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; import org.orekit.files.ccsds.ndm.adm.AttitudeType; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; import org.orekit.time.AbsoluteDate; /** This class gathers the meta-data present in the Attitude Data Message (ADM). @@ -29,7 +32,7 @@ public class AemMetadata extends AdmMetadata { /** Endpoints (i.e. frames A, B and their relationship). */ - private final AttitudeEndoints endpoints; + private final AttitudeEndpoints endpoints; /** Start of total time span covered by attitude data. */ private AbsoluteDate startTime; @@ -52,9 +55,14 @@ public class AemMetadata extends AdmMetadata { /** The rotation sequence of the Euler angles. */ private RotationOrder eulerRotSeq; - /** The frame in which rates are specified. */ + /** The frame in which rates are specified (only for ADM V1). */ private Boolean rateFrameIsA; + /** The frame in which angular velocities are specified. + * @since 12.0 + */ + private FrameFacade angvelFrame; + /** The interpolation method to be used. */ private String interpolationMethod; @@ -65,7 +73,7 @@ public class AemMetadata extends AdmMetadata { * @param defaultInterpolationDegree default interpolation degree */ public AemMetadata(final int defaultInterpolationDegree) { - endpoints = new AttitudeEndoints(); + endpoints = new AttitudeEndpoints(); interpolationDegree = defaultInterpolationDegree; } @@ -73,11 +81,18 @@ public AemMetadata(final int defaultInterpolationDegree) { @Override public void validate(final double version) { + super.validate(version); + checkMandatoryEntriesExceptDatesAndExternalFrame(version); endpoints.checkExternalFrame(AemMetadataKey.REF_FRAME_A, AemMetadataKey.REF_FRAME_B); - checkNotNull(startTime, AemMetadataKey.START_TIME); - checkNotNull(stopTime, AemMetadataKey.STOP_TIME); + checkNotNull(startTime, AemMetadataKey.START_TIME.name()); + checkNotNull(stopTime, AemMetadataKey.STOP_TIME.name()); + + if (version >= 2.0 && isFirst()) { + throw new OrekitException(OrekitMessages.CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION, + AemMetadataKey.QUATERNION_TYPE, version); + } } @@ -95,23 +110,29 @@ void checkMandatoryEntriesExceptDatesAndExternalFrame(final double version) { super.validate(version); - endpoints.checkMandatoryEntriesExceptExternalFrame(AemMetadataKey.REF_FRAME_A, + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + AemMetadataKey.REF_FRAME_A, AemMetadataKey.REF_FRAME_B, AemMetadataKey.ATTITUDE_DIR); - checkNotNull(attitudeType, AemMetadataKey.ATTITUDE_TYPE); - if (attitudeType == AttitudeType.QUATERNION || - attitudeType == AttitudeType.QUATERNION_DERIVATIVE) { - checkNotNull(isFirst, AemMetadataKey.QUATERNION_TYPE); - } - if (attitudeType == AttitudeType.QUATERNION_RATE || - attitudeType == AttitudeType.EULER_ANGLE || - attitudeType == AttitudeType.EULER_ANGLE_RATE) { - checkNotNull(eulerRotSeq, AemMetadataKey.EULER_ROT_SEQ); + checkNotNull(attitudeType, AemMetadataKey.ATTITUDE_TYPE.name()); + if (version < 2.0) { + if (attitudeType == AttitudeType.QUATERNION || + attitudeType == AttitudeType.QUATERNION_DERIVATIVE) { + checkNotNull(isFirst, AemMetadataKey.QUATERNION_TYPE.name()); + } + if (attitudeType == AttitudeType.EULER_ANGLE_DERIVATIVE) { + checkNotNull(rateFrameIsA, AemMetadataKey.RATE_FRAME.name()); + } + } else { + if (attitudeType == AttitudeType.QUATERNION_ANGVEL) { + checkNotNull(angvelFrame, AemMetadataKey.ANGVEL_FRAME.name()); + } } - if (attitudeType == AttitudeType.QUATERNION_RATE || - attitudeType == AttitudeType.EULER_ANGLE_RATE) { - checkNotNull(rateFrameIsA, AemMetadataKey.RATE_FRAME); + + if (attitudeType == AttitudeType.EULER_ANGLE || + attitudeType == AttitudeType.EULER_ANGLE_DERIVATIVE) { + checkNotNull(eulerRotSeq, AemMetadataKey.EULER_ROT_SEQ.name()); } } @@ -119,25 +140,41 @@ void checkMandatoryEntriesExceptDatesAndExternalFrame(final double version) { /** Get the endpoints (i.e. frames A, B and their relationship). * @return endpoints */ - public AttitudeEndoints getEndpoints() { + public AttitudeEndpoints getEndpoints() { return endpoints; } - /** Check if rates are specified in {@link AttitudeEndoints#getFrameA() frame A}. - * @return true if rates are specified in {@link AttitudeEndoints#getFrameA() frame A} + /** Check if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}. + * @return true if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A} */ public boolean rateFrameIsA() { return rateFrameIsA == null ? false : rateFrameIsA; } /** Set the frame in which rates are specified. - * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndoints#getFrameA() frame A} + * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndpoints#getFrameA() frame A} */ public void setRateFrameIsA(final boolean rateFrameIsA) { refuseFurtherComments(); this.rateFrameIsA = rateFrameIsA; } + /** Set frame in which angular velocities are specified. + * @param angvelFrame frame in which angular velocities are specified + * @since 12.0 + */ + public void setAngvelFrame(final FrameFacade angvelFrame) { + this.angvelFrame = angvelFrame; + } + + /** Get frame in which angular velocities are specified. + * @return frame in which angular velocities are specified + * @since 12.0 + */ + public FrameFacade getFrameAngvelFrame() { + return angvelFrame; + } + /** Check if rates are specified in spacecraft body frame. *

          * {@link #validate(double) Mandatory entries} must have been @@ -172,10 +209,10 @@ public void setAttitudeType(final AttitudeType type) { * Get the flag for the placement of the quaternion QC in the attitude data. * * @return true if QC is the first element in the attitude data, - * null if not initialized + * false if not initialized */ public Boolean isFirst() { - return isFirst == null ? Boolean.TRUE : isFirst; + return isFirst == null ? Boolean.FALSE : isFirst; } /** diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadataKey.java index f468fa77dc..cd958e9a16 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,6 @@ package org.orekit.files.ccsds.ndm.adm.aem; import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; -import org.orekit.files.ccsds.ndm.adm.AdmParser; import org.orekit.files.ccsds.ndm.adm.AttitudeType; import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; @@ -39,7 +38,7 @@ public enum AemMetadataKey { /** Second reference frame. */ REF_FRAME_B((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { - container.checkNotNull(container.getEndpoints().getFrameA(), REF_FRAME_A); + container.checkNotNull(container.getEndpoints().getFrameA(), REF_FRAME_A.name()); final boolean aIsSpaceraftBody = container.getEndpoints().getFrameA().asSpacecraftBodyFrame() != null; return token.processAsFrame(container.getEndpoints()::setFrameB, context, aIsSpaceraftBody, aIsSpaceraftBody, !aIsSpaceraftBody); @@ -71,7 +70,7 @@ public enum AemMetadataKey { ATTITUDE_TYPE((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { try { - container.setAttitudeType(AttitudeType.parseType(token.getContentAsNormalizedString())); + container.setAttitudeType(AttitudeType.parseType(token.getRawContent())); return true; } catch (IllegalArgumentException iae) { return false; @@ -89,9 +88,9 @@ public enum AemMetadataKey { }), /** Rotation order entry for Euler angles. */ - EULER_ROT_SEQ((token, context, container) -> AdmParser.processRotationOrder(token, container::setEulerRotSeq)), + EULER_ROT_SEQ((token, context, container) -> token.processAsRotationOrder(container::setEulerRotSeq)), - /** Reference frame for Euler rates. */ + /** Reference frame for Euler rates. (only for ADM V1) */ RATE_FRAME((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { final String content = token.getContentAsUppercaseString(); @@ -101,6 +100,11 @@ public enum AemMetadataKey { return true; }), + /** Reference frame for angular velocities. + * @since 12.0 + */ + ANGVEL_FRAME((token, context, container) -> token.processAsFrame(container::setAngvelFrame, context, true, true, true)), + /** Interpolation method in ephemeris. */ INTERPOLATION_METHOD((token, context, container) -> token.processAsUppercaseString(container::setInterpolationMethod)), diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemParser.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemParser.java index 2f781be547..25ac8f4ac5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import java.util.regex.Pattern; import org.orekit.data.DataContext; @@ -25,9 +26,10 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.adm.AdmCommonMetadataKey; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; import org.orekit.files.ccsds.ndm.adm.AdmParser; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.KvnStructureProcessingState; import org.orekit.files.ccsds.section.MetadataKey; @@ -61,7 +63,7 @@ public class AemParser extends AdmParser implements AttitudeEphe private static final Pattern SPLIT_AT_BLANKS = Pattern.compile("\\s+"); /** File header. */ - private Header header; + private AdmHeader header; /** File segments. */ private List segments; @@ -97,12 +99,15 @@ public class AemParser extends AdmParser implements AttitudeEphe * (may be null if time system is absolute) * @param defaultInterpolationDegree default interpolation degree * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ public AemParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, final AbsoluteDate missionReferenceDate, - final int defaultInterpolationDegree, final ParsedUnitsBehavior parsedUnitsBehavior) { + final int defaultInterpolationDegree, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { super(Aem.ROOT, Aem.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, - missionReferenceDate, parsedUnitsBehavior); + missionReferenceDate, parsedUnitsBehavior, filters); this.defaultInterpolationDegree = defaultInterpolationDegree; } @@ -114,14 +119,14 @@ public Aem parse(final DataSource source) { /** {@inheritDoc} */ @Override - public Header getHeader() { + public AdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(2.0); + header = new AdmHeader(); segments = new ArrayList<>(); metadata = null; context = null; @@ -214,9 +219,7 @@ public boolean finalizeData() { /** {@inheritDoc} */ @Override public Aem build() { - final Aem file = new Aem(header, segments, getConventions(), getDataContext()); - file.checkTimeSystems(); - return file; + return new Aem(header, segments, getConventions(), getDataContext()); } /** Manage attitude state section in a XML message. @@ -259,10 +262,14 @@ private boolean processMetadataToken(final ParseToken token) { return AdmMetadataKey.valueOf(token.getName()).process(token, context, metadata); } catch (IllegalArgumentException iaeD) { try { - return AemMetadataKey.valueOf(token.getName()).process(token, context, metadata); - } catch (IllegalArgumentException iaeE) { - // token has not been recognized - return false; + return AdmCommonMetadataKey.valueOf(token.getName()).process(token, context, metadata); + } catch (IllegalArgumentException iaeC) { + try { + return AemMetadataKey.valueOf(token.getName()).process(token, context, metadata); + } catch (IllegalArgumentException iaeE) { + // token has not been recognized + return false; + } } } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSatelliteEphemeris.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSatelliteEphemeris.java index 2c0aec47c7..7126aa7a8e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSatelliteEphemeris.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSatelliteEphemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSegment.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSegment.java index 77d3b7d259..2a163d7053 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSegment.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemSegment.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemWriter.java index 233f45e59f..93634507df 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AemWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,12 +21,12 @@ import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; -import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.definitions.Units; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.adm.AdmCommonMetadataKey; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; import org.orekit.files.ccsds.ndm.adm.AttitudeType; import org.orekit.files.ccsds.section.Header; @@ -52,7 +52,7 @@ *

          The AEM header and metadata used by this writer are described in the following tables. * Many metadata items are optional or have default values so they do not need to be specified. * At a minimum the user must supply those values that are required and for which no - * default exits: {@link AdmMetadataKey#OBJECT_NAME}, {@link AdmMetadataKey#OBJECT_ID}, + * default exits: {@link AdmMetadataKey#OBJECT_NAME}, {@link AdmCommonMetadataKey#OBJECT_ID}, * {@link AemMetadataKey#START_TIME} and {@link AemMetadataKey#STOP_TIME}. * The usage column in the table indicates where the metadata item is used, either in the AEM header * or in the metadata section at the start of an AEM attitude segment. @@ -118,7 +118,7 @@ * * * - * {@link AdmMetadataKey#OBJECT_ID} + * {@link AdmCommonMetadataKey#OBJECT_ID} * Yes * * @@ -170,7 +170,7 @@ * * {@link AemMetadataKey#ATTITUDE_TYPE} * Yes - * {@link AttitudeType#QUATERNION_RATE QUATERNION/RATE} + * {@link AttitudeType#QUATERNION_DERIVATIVE QUATERNION/DERIVATIVE} * * * {@link AemMetadataKey#QUATERNION_TYPE} @@ -214,10 +214,10 @@ * @author Bryan Cazabonne * @since 10.2 */ -public class AemWriter extends AbstractMessageWriter { +public class AemWriter extends AbstractMessageWriter { /** Version number implemented. **/ - public static final double CCSDS_AEM_VERS = 1.0; + public static final double CCSDS_AEM_VERS = 2.0; /** Padding width for aligning the '=' sign. */ public static final int KVN_PADDING_WIDTH = 20; @@ -228,7 +228,7 @@ public class AemWriter extends AbstractMessageWriter { /** Constant for frame B to frame A attitude. */ private static final String B_TO_A = "B2A"; - /** Constant for quaternions with scalar component in first position. */ + /** Constant for quaternions with scalar component in position. */ private static final String FIRST = "FIRST"; /** Constant for quaternions with scalar component in last position. */ @@ -258,17 +258,6 @@ public class AemWriter extends AbstractMessageWriter { /** * Constructor used to create a new AEM writer configured with the necessary parameters * to successfully fill in all required fields that aren't part of a standard object. - *

          - * If the mandatory header entries are not present (or if header is null), - * built-in defaults will be used - *

          - *

          - * The writer is built from the complete header and partial metadata. The template - * metadata is used to initialize and independent local copy, that will be updated - * as new segments are written (with at least the segment start and stop will change, - * but some other parts may change too). The {@code template} argument itself is not - * changed. - *

          * @param conventions IERS Conventions * @param dataContext used to retrieve frames, time scales, etc. * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems @@ -286,18 +275,18 @@ public AemWriter(final IERSConventions conventions, final DataContext dataContex /** {@inheritDoc} */ @Override - public void writeSegmentContent(final Generator generator, final double formatVersion, - final AemSegment segment) + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final AemSegment segment) throws IOException { final AemMetadata metadata = segment.getMetadata(); - writeMetadata(generator, metadata); + writeMetadata(generator, formatVersion, metadata); // Loop on attitude data startAttitudeBlock(generator); generator.writeComments(((AemSegment) segment).getData().getComments()); for (final TimeStampedAngularCoordinates coordinates : segment.getAngularCoordinates()) { - writeAttitudeEphemerisLine(generator, metadata, coordinates); + writeAttitudeEphemerisLine(generator, formatVersion, metadata, coordinates); } endAttitudeBlock(generator); @@ -305,10 +294,12 @@ public void writeSegmentContent(final Generator generator, final double formatVe /** Write an ephemeris segment metadata. * @param generator generator to use for producing output + * @param formatVersion format version * @param metadata metadata to write * @throws IOException if the output stream throws one while writing. */ - void writeMetadata(final Generator generator, final AemMetadata metadata) throws IOException { + void writeMetadata(final Generator generator, final double formatVersion, final AemMetadata metadata) + throws IOException { final ContextBinding oldContext = getContext(); setContext(new ContextBinding(oldContext::getConventions, @@ -328,8 +319,8 @@ void writeMetadata(final Generator generator, final AemMetadata metadata) throws generator.writeComments(metadata.getComments()); // objects - generator.writeEntry(AdmMetadataKey.OBJECT_NAME.name(), metadata.getObjectName(), null, true); - generator.writeEntry(AdmMetadataKey.OBJECT_ID.name(), metadata.getObjectID(), null, true); + generator.writeEntry(AdmMetadataKey.OBJECT_NAME.name(), metadata.getObjectName(), null, true); + generator.writeEntry(AdmCommonMetadataKey.OBJECT_ID.name(), metadata.getObjectID(), null, true); if (metadata.getCenter() != null) { generator.writeEntry(AdmMetadataKey.CENTER_NAME.name(), metadata.getCenter().getName(), null, false); } @@ -337,55 +328,69 @@ void writeMetadata(final Generator generator, final AemMetadata metadata) throws // frames generator.writeEntry(AemMetadataKey.REF_FRAME_A.name(), metadata.getEndpoints().getFrameA().getName(), null, true); generator.writeEntry(AemMetadataKey.REF_FRAME_B.name(), metadata.getEndpoints().getFrameB().getName(), null, true); - generator.writeEntry(AemMetadataKey.ATTITUDE_DIR.name(), metadata.getEndpoints().isA2b() ? A_TO_B : B_TO_A, null, true); + if (formatVersion < 2.0) { + generator.writeEntry(AemMetadataKey.ATTITUDE_DIR.name(), metadata.getEndpoints().isA2b() ? A_TO_B : B_TO_A, null, true); + } // time generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), metadata.getTimeSystem(), true); - generator.writeEntry(AemMetadataKey.START_TIME.name(), getTimeConverter(), metadata.getStartTime(), true); + generator.writeEntry(AemMetadataKey.START_TIME.name(), getTimeConverter(), metadata.getStartTime(), false, true); if (metadata.getUseableStartTime() != null) { - generator.writeEntry(AemMetadataKey.USEABLE_START_TIME.name(), getTimeConverter(), metadata.getUseableStartTime(), false); + generator.writeEntry(AemMetadataKey.USEABLE_START_TIME.name(), getTimeConverter(), metadata.getUseableStartTime(), false, false); } if (metadata.getUseableStopTime() != null) { - generator.writeEntry(AemMetadataKey.USEABLE_STOP_TIME.name(), getTimeConverter(), metadata.getUseableStopTime(), false); + generator.writeEntry(AemMetadataKey.USEABLE_STOP_TIME.name(), getTimeConverter(), metadata.getUseableStopTime(), false, false); } - generator.writeEntry(AemMetadataKey.STOP_TIME.name(), getTimeConverter(), metadata.getStopTime(), true); + generator.writeEntry(AemMetadataKey.STOP_TIME.name(), getTimeConverter(), metadata.getStopTime(), false, true); // types final AttitudeType attitudeType = metadata.getAttitudeType(); - generator.writeEntry(AemMetadataKey.ATTITUDE_TYPE.name(), attitudeType.toString(), null, true); - if (attitudeType == AttitudeType.QUATERNION || - attitudeType == AttitudeType.QUATERNION_DERIVATIVE || - attitudeType == AttitudeType.QUATERNION_RATE) { - generator.writeEntry(AemMetadataKey.QUATERNION_TYPE.name(), metadata.isFirst() ? FIRST : LAST, null, false); + generator.writeEntry(AemMetadataKey.ATTITUDE_TYPE.name(), attitudeType.getName(formatVersion), null, true); + if (formatVersion < 2.0) { + if (attitudeType == AttitudeType.QUATERNION || + attitudeType == AttitudeType.QUATERNION_DERIVATIVE || + attitudeType == AttitudeType.QUATERNION_ANGVEL) { + generator.writeEntry(AemMetadataKey.QUATERNION_TYPE.name(), metadata.isFirst() ? FIRST : LAST, null, false); + } } - if (attitudeType == AttitudeType.QUATERNION_RATE || - attitudeType == AttitudeType.EULER_ANGLE || - attitudeType == AttitudeType.EULER_ANGLE_RATE) { - if (metadata.getEulerRotSeq() == null) { - // the keyword *will* be missing because we cannot set it - throw new OrekitException(OrekitMessages.CCSDS_MISSING_KEYWORD, - AemMetadataKey.EULER_ROT_SEQ.name(), generator.getOutputName()); + if (attitudeType == AttitudeType.QUATERNION_EULER_RATES || + attitudeType == AttitudeType.EULER_ANGLE || + attitudeType == AttitudeType.EULER_ANGLE_DERIVATIVE || + attitudeType == AttitudeType.EULER_ANGLE_ANGVEL) { + if (formatVersion < 2.0) { + generator.writeEntry(AemMetadataKey.EULER_ROT_SEQ.name(), + metadata.getEulerRotSeq().name().replace('X', '1').replace('Y', '2').replace('Z', '3'), + null, false); + } else { + generator.writeEntry(AemMetadataKey.EULER_ROT_SEQ.name(), + metadata.getEulerRotSeq().name(), + null, false); } - generator.writeEntry(AemMetadataKey.EULER_ROT_SEQ.name(), - metadata.getEulerRotSeq().name().replace('X', '1').replace('Y', '2').replace('Z', '3'), - null, false); } - if (attitudeType == AttitudeType.QUATERNION_RATE || - attitudeType == AttitudeType.EULER_ANGLE_RATE) { + if (formatVersion < 2 && attitudeType == AttitudeType.EULER_ANGLE_DERIVATIVE) { generator.writeEntry(AemMetadataKey.RATE_FRAME.name(), metadata.rateFrameIsA() ? REF_FRAME_A : REF_FRAME_B, - null, false); + null, false); + } + + if (attitudeType == AttitudeType.QUATERNION_ANGVEL || + attitudeType == AttitudeType.EULER_ANGLE_ANGVEL) { + generator.writeEntry(AemMetadataKey.ANGVEL_FRAME.name(), + metadata.getFrameAngvelFrame().getName(), + null, true); } // interpolation - generator.writeEntry(AemMetadataKey.INTERPOLATION_METHOD.name(), - metadata.getInterpolationMethod(), - null, false); - generator.writeEntry(AemMetadataKey.INTERPOLATION_DEGREE.name(), - Integer.toString(metadata.getInterpolationDegree()), - null, false); + if (metadata.getInterpolationMethod() != null) { + generator.writeEntry(AemMetadataKey.INTERPOLATION_METHOD.name(), + metadata.getInterpolationMethod(), + null, true); + generator.writeEntry(AemMetadataKey.INTERPOLATION_DEGREE.name(), + Integer.toString(metadata.getInterpolationDegree()), + null, true); + } // Stop metadata generator.exitSection(); @@ -395,11 +400,13 @@ void writeMetadata(final Generator generator, final AemMetadata metadata) throws /** * Write a single attitude ephemeris line according to section 4.2.4 and Table 4-4. * @param generator generator to use for producing output + * @param formatVersion format version to use * @param metadata metadata to use for interpreting data * @param attitude the attitude information for a given date * @throws IOException if the output stream throws one while writing. */ - void writeAttitudeEphemerisLine(final Generator generator, final AemMetadata metadata, + void writeAttitudeEphemerisLine(final Generator generator, final double formatVersion, + final AemMetadata metadata, final TimeStampedAngularCoordinates attitude) throws IOException { @@ -416,8 +423,7 @@ void writeAttitudeEphemerisLine(final Generator generator, final AemMetadata met generator.writeRawData(generator.dateToString(getTimeConverter(), attitude.getDate())); // data - final int size = data.length; - for (int index = 0; index < size; index++) { + for (int index = 0; index < data.length; index++) { generator.writeRawData(' '); generator.writeRawData(data[index]); } @@ -430,26 +436,35 @@ void writeAttitudeEphemerisLine(final Generator generator, final AemMetadata met xmlGenerator.enterSection(XmlSubStructureKey.attitudeState.name()); switch (metadata.getAttitudeType()) { case QUATERNION : - writeQuaternion(xmlGenerator, metadata.isFirst(), attitude.getDate(), data); + writeQuaternion(xmlGenerator, formatVersion, metadata.isFirst(), attitude.getDate(), data); break; case QUATERNION_DERIVATIVE : - writeQuaternionDerivative(xmlGenerator, metadata.isFirst(), attitude.getDate(), data); + writeQuaternionDerivative(xmlGenerator, formatVersion, metadata.isFirst(), attitude.getDate(), data); break; - case QUATERNION_RATE : - writeQuaternionRate(xmlGenerator, metadata.isFirst(), metadata.getEulerRotSeq(), attitude.getDate(), data); + case QUATERNION_EULER_RATES : + writeQuaternionEulerRates(xmlGenerator, metadata.isFirst(), metadata.getEulerRotSeq(), attitude.getDate(), data); + break; + case QUATERNION_ANGVEL : + writeQuaternionAngularVelocity(xmlGenerator, attitude.getDate(), data); break; case EULER_ANGLE : - writeEulerAngle(xmlGenerator, metadata.getEulerRotSeq(), attitude.getDate(), data); + writeEulerAngle(xmlGenerator, formatVersion, metadata.getEulerRotSeq(), attitude.getDate(), data); + break; + case EULER_ANGLE_DERIVATIVE : + writeEulerAngleDerivative(xmlGenerator, formatVersion, metadata.getEulerRotSeq(), attitude.getDate(), data); break; - case EULER_ANGLE_RATE : - writeEulerAngleRate(xmlGenerator, metadata.getEulerRotSeq(), attitude.getDate(), data); + case EULER_ANGLE_ANGVEL : + writeEulerAngleAngularVelocity(xmlGenerator, formatVersion, metadata.getEulerRotSeq(), attitude.getDate(), data); break; case SPIN : writeSpin(xmlGenerator, attitude.getDate(), data); break; -// case SPIN_NUTATION : -// writeSpinNutation(xmlGenerator, attitude.getDate(), data); -// break; + case SPIN_NUTATION : + writeSpinNutation(xmlGenerator, attitude.getDate(), data); + break; + case SPIN_NUTATION_MOMENTUM : + writeSpinNutationMomentum(xmlGenerator, attitude.getDate(), data); + break; default : // this should never happen throw new OrekitInternalError(null); @@ -461,75 +476,86 @@ void writeAttitudeEphemerisLine(final Generator generator, final AemMetadata met /** Write a quaternion entry in XML. * @param xmlGenerator generator to use for producing output - * @param first flag for scalar component to appear first + * @param formatVersion format version to use + * @param first flag for scalar component to appear first (only relevant in ADM V1) * @param epoch of the entry * @param data entry data * @throws IOException if the output stream throws one while writing. */ - void writeQuaternion(final XmlGenerator xmlGenerator, final boolean first, final AbsoluteDate epoch, final String[] data) + void writeQuaternion(final XmlGenerator xmlGenerator, final double formatVersion, + final boolean first, final AbsoluteDate epoch, final String[] data) throws IOException { - // wrapping element - xmlGenerator.enterSection(AttitudeEntryKey.quaternion.name()); + xmlGenerator.enterSection(formatVersion < 2.0 ? + AttitudeEntryKey.quaternionState.name() : + AttitudeEntryKey.quaternionEphemeris.name()); // data part - xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); + + // wrapping element + xmlGenerator.enterSection(AttitudeEntryKey.quaternion.name()); // quaternion part int i = 0; - if (first) { + if (formatVersion < 2.0 && first) { xmlGenerator.writeEntry(AttitudeEntryKey.QC.name(), data[i++], Unit.ONE, false); } xmlGenerator.writeEntry(AttitudeEntryKey.Q1.name(), data[i++], Unit.ONE, false); xmlGenerator.writeEntry(AttitudeEntryKey.Q2.name(), data[i++], Unit.ONE, false); xmlGenerator.writeEntry(AttitudeEntryKey.Q3.name(), data[i++], Unit.ONE, false); - if (!first) { + if (!(formatVersion < 2.0 && first)) { xmlGenerator.writeEntry(AttitudeEntryKey.QC.name(), data[i++], Unit.ONE, false); } xmlGenerator.exitSection(); + xmlGenerator.exitSection(); } /** Write a quaternion/derivative entry in XML. * @param xmlGenerator generator to use for producing output - * @param first flag for scalar component to appear first + * @param formatVersion format version to use + * @param first flag for scalar component to appear first (only relevant in ADM V1) * @param epoch of the entry * @param data entry data * @throws IOException if the output stream throws one while writing. */ - void writeQuaternionDerivative(final XmlGenerator xmlGenerator, final boolean first, final AbsoluteDate epoch, final String[] data) + void writeQuaternionDerivative(final XmlGenerator xmlGenerator, final double formatVersion, + final boolean first, final AbsoluteDate epoch, final String[] data) throws IOException { // wrapping element xmlGenerator.enterSection(AttitudeEntryKey.quaternionDerivative.name()); // data part - xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); int i = 0; // quaternion part xmlGenerator.enterSection(AttitudeEntryKey.quaternion.name()); - if (first) { + if (formatVersion < 2.0 && first) { xmlGenerator.writeEntry(AttitudeEntryKey.QC.name(), data[i++], Unit.ONE, true); } xmlGenerator.writeEntry(AttitudeEntryKey.Q1.name(), data[i++], Unit.ONE, true); xmlGenerator.writeEntry(AttitudeEntryKey.Q2.name(), data[i++], Unit.ONE, true); xmlGenerator.writeEntry(AttitudeEntryKey.Q3.name(), data[i++], Unit.ONE, true); - if (!first) { + if (!(formatVersion < 2.0 && first)) { xmlGenerator.writeEntry(AttitudeEntryKey.QC.name(), data[i++], Unit.ONE, true); } xmlGenerator.exitSection(); // derivative part - xmlGenerator.enterSection(AttitudeEntryKey.quaternionRate.name()); - if (first) { + xmlGenerator.enterSection(formatVersion < 2.0 ? + AttitudeEntryKey.quaternionRate.name() : + AttitudeEntryKey.quaternionDot.name()); + if (formatVersion < 2.0 && first) { xmlGenerator.writeEntry(AttitudeEntryKey.QC_DOT.name(), data[i++], Units.ONE_PER_S, true); } xmlGenerator.writeEntry(AttitudeEntryKey.Q1_DOT.name(), data[i++], Units.ONE_PER_S, true); xmlGenerator.writeEntry(AttitudeEntryKey.Q2_DOT.name(), data[i++], Units.ONE_PER_S, true); xmlGenerator.writeEntry(AttitudeEntryKey.Q3_DOT.name(), data[i++], Units.ONE_PER_S, true); - if (!first) { + if (!(formatVersion < 2.0 && first)) { xmlGenerator.writeEntry(AttitudeEntryKey.QC_DOT.name(), data[i++], Units.ONE_PER_S, true); } xmlGenerator.exitSection(); @@ -538,23 +564,23 @@ void writeQuaternionDerivative(final XmlGenerator xmlGenerator, final boolean fi } - /** Write a quaternion/rate entry in XML. + /** Write a quaternion/Euler rates entry in XML. * @param xmlGenerator generator to use for producing output - * @param first flag for scalar component to appear first + * @param first flag for scalar component to appear first (only relevant in ADM V1) * @param order Euler rotation order * @param epoch of the entry * @param data entry data * @throws IOException if the output stream throws one while writing. */ - void writeQuaternionRate(final XmlGenerator xmlGenerator, final boolean first, final RotationOrder order, - final AbsoluteDate epoch, final String[] data) + void writeQuaternionEulerRates(final XmlGenerator xmlGenerator, final boolean first, final RotationOrder order, + final AbsoluteDate epoch, final String[] data) throws IOException { // wrapping element xmlGenerator.enterSection(AttitudeEntryKey.quaternionEulerRate.name()); // data part - xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); int i = 0; // quaternion part @@ -572,9 +598,45 @@ void writeQuaternionRate(final XmlGenerator xmlGenerator, final boolean first, f // derivative part xmlGenerator.enterSection(AttitudeEntryKey.rotationRates.name()); - writeEulerRate(xmlGenerator, 0, order.name(), data[i++]); - writeEulerRate(xmlGenerator, 1, order.name(), data[i++]); - writeEulerRate(xmlGenerator, 2, order.name(), data[i++]); + writeLabeledEulerRate(xmlGenerator, 0, order.name(), data[i++]); + writeLabeledEulerRate(xmlGenerator, 1, order.name(), data[i++]); + writeLabeledEulerRate(xmlGenerator, 2, order.name(), data[i++]); + xmlGenerator.exitSection(); + + xmlGenerator.exitSection(); + + } + + /** Write a quaternion/rate entry in XML. + * @param xmlGenerator generator to use for producing output + * @param epoch of the entry + * @param data entry data + * @throws IOException if the output stream throws one while writing. + */ + void writeQuaternionAngularVelocity(final XmlGenerator xmlGenerator, + final AbsoluteDate epoch, final String[] data) + throws IOException { + + // wrapping element + xmlGenerator.enterSection(AttitudeEntryKey.quaternionAngVel.name()); + + // data part + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); + int i = 0; + + // quaternion part + xmlGenerator.enterSection(AttitudeEntryKey.quaternion.name()); + xmlGenerator.writeEntry(AttitudeEntryKey.Q1.name(), data[i++], Unit.ONE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.Q2.name(), data[i++], Unit.ONE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.Q3.name(), data[i++], Unit.ONE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.QC.name(), data[i++], Unit.ONE, true); + xmlGenerator.exitSection(); + + // angular velocity part + xmlGenerator.enterSection(AttitudeEntryKey.angVel.name()); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGVEL_X.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGVEL_Y.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGVEL_Z.name(), data[i++], Units.DEG_PER_S, true); xmlGenerator.exitSection(); xmlGenerator.exitSection(); @@ -583,65 +645,115 @@ void writeQuaternionRate(final XmlGenerator xmlGenerator, final boolean first, f /** Write a Euler angles entry in XML. * @param xmlGenerator generator to use for producing output + * @param formatVersion format version to use * @param order Euler rotation order * @param epoch of the entry * @param data entry data * @throws IOException if the output stream throws one while writing. */ - void writeEulerAngle(final XmlGenerator xmlGenerator, final RotationOrder order, - final AbsoluteDate epoch, final String[] data) + void writeEulerAngle(final XmlGenerator xmlGenerator, final double formatVersion, + final RotationOrder order, final AbsoluteDate epoch, final String[] data) throws IOException { // wrapping element xmlGenerator.enterSection(AttitudeEntryKey.eulerAngle.name()); // data part - xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); int i = 0; // angle part - xmlGenerator.enterSection(AttitudeEntryKey.rotationAngles.name()); - writeEulerAngle(xmlGenerator, 0, order.name(), data[i++]); - writeEulerAngle(xmlGenerator, 1, order.name(), data[i++]); - writeEulerAngle(xmlGenerator, 2, order.name(), data[i++]); - xmlGenerator.exitSection(); + if (formatVersion < 2.0) { + xmlGenerator.enterSection(AttitudeEntryKey.rotationAngles.name()); + writeLabeledEulerAngle(xmlGenerator, 0, order.name(), data[i++]); + writeLabeledEulerAngle(xmlGenerator, 1, order.name(), data[i++]); + writeLabeledEulerAngle(xmlGenerator, 2, order.name(), data[i++]); + xmlGenerator.exitSection(); + } else { + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_1.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_2.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_3.name(), data[i++], Unit.DEGREE, true); + } xmlGenerator.exitSection(); } - /** Write a Euler angles/rates entry in XML. + /** Write a Euler angles entry in XML. * @param xmlGenerator generator to use for producing output + * @param formatVersion format version to use * @param order Euler rotation order * @param epoch of the entry * @param data entry data * @throws IOException if the output stream throws one while writing. */ - void writeEulerAngleRate(final XmlGenerator xmlGenerator, final RotationOrder order, - final AbsoluteDate epoch, final String[] data) + void writeEulerAngleDerivative(final XmlGenerator xmlGenerator, final double formatVersion, + final RotationOrder order, final AbsoluteDate epoch, final String[] data) throws IOException { // wrapping element - xmlGenerator.enterSection(AttitudeEntryKey.eulerAngle.name()); + xmlGenerator.enterSection(formatVersion < 2.0 ? + AttitudeEntryKey.eulerAngleRate.name() : + AttitudeEntryKey.eulerAngleDerivative.name()); // data part - xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); int i = 0; // angle part - xmlGenerator.enterSection(AttitudeEntryKey.rotationAngles.name()); - writeEulerAngle(xmlGenerator, 0, order.name(), data[i++]); - writeEulerAngle(xmlGenerator, 1, order.name(), data[i++]); - writeEulerAngle(xmlGenerator, 2, order.name(), data[i++]); - xmlGenerator.exitSection(); + if (formatVersion < 2.0) { + xmlGenerator.enterSection(AttitudeEntryKey.rotationAngles.name()); + writeLabeledEulerAngle(xmlGenerator, 0, order.name(), data[i++]); + writeLabeledEulerAngle(xmlGenerator, 1, order.name(), data[i++]); + writeLabeledEulerAngle(xmlGenerator, 2, order.name(), data[i++]); + xmlGenerator.exitSection(); + xmlGenerator.enterSection(AttitudeEntryKey.rotationRates.name()); + writeLabeledEulerRate(xmlGenerator, 0, order.name(), data[i++]); + writeLabeledEulerRate(xmlGenerator, 1, order.name(), data[i++]); + writeLabeledEulerRate(xmlGenerator, 2, order.name(), data[i++]); + xmlGenerator.exitSection(); + } else { + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_1.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_2.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_3.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_1_DOT.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_2_DOT.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_3_DOT.name(), data[i++], Units.DEG_PER_S, true); + } - // rates part - xmlGenerator.enterSection(AttitudeEntryKey.rotationRates.name()); - writeEulerRate(xmlGenerator, 0, order.name(), data[i++]); - writeEulerRate(xmlGenerator, 1, order.name(), data[i++]); - writeEulerRate(xmlGenerator, 2, order.name(), data[i++]); xmlGenerator.exitSection(); + } + + /** Write a Euler angles/angular velocity entry in XML. + * @param xmlGenerator generator to use for producing output + * @param formatVersion format version to use + * @param order Euler rotation order + * @param epoch of the entry + * @param data entry data + * @throws IOException if the output stream throws one while writing. + */ + void writeEulerAngleAngularVelocity(final XmlGenerator xmlGenerator, final double formatVersion, final RotationOrder order, + final AbsoluteDate epoch, final String[] data) + throws IOException { + + // wrapping element + xmlGenerator.enterSection(AttitudeEntryKey.eulerAngleAngVel.name()); + + // data part + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); + int i = 0; + + // angle part + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_1.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_2.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGLE_3.name(), data[i++], Unit.DEGREE, true); + + // angular velocity part + xmlGenerator.writeEntry(AttitudeEntryKey.ANGVEL_X.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGVEL_Y.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.ANGVEL_Z.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.exitSection(); } @@ -659,43 +771,70 @@ void writeSpin(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final xmlGenerator.enterSection(AttitudeEntryKey.spin.name()); // data part - xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); + int i = 0; + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ALPHA.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_DELTA.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE_VEL.name(), data[i++], Units.DEG_PER_S, true); + + xmlGenerator.exitSection(); + + } + + /** Write a spin/nutation entry in XML. + * @param xmlGenerator generator to use for producing output + * @param epoch of the entry + * @param data entry data + * @throws IOException if the output stream throws one while writing. + */ + void writeSpinNutation(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final String[] data) + throws IOException { + + // wrapping element + xmlGenerator.enterSection(AttitudeEntryKey.spinNutation.name()); + + // data part + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); int i = 0; xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ALPHA.name(), data[i++], Unit.DEGREE, true); xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_DELTA.name(), data[i++], Unit.DEGREE, true); xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE.name(), data[i++], Unit.DEGREE, true); xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE_VEL.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION_PER.name(), data[i++], Unit.SECOND, true); + xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION_PHASE.name(), data[i++], Unit.DEGREE, true); xmlGenerator.exitSection(); } -// /** Write a spin/nutation entry in XML. -// * @param xmlGenerator generator to use for producing output -// * @param epoch of the entry -// * @param data entry data -// * @throws IOException if the output stream throws one while writing. -// */ -// void writeSpinNutation(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final String[] data) -// throws IOException { -// -// // wrapping element -// xmlGenerator.enterSection(AttitudeEntryKey.spin.name()); -// -// // data part -// xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true); -// int i = 0; -// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ALPHA.name(), data[i++], Unit.DEGREE, true); -// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_DELTA.name(), data[i++], Unit.DEGREE, true); -// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE.name(), data[i++], Unit.DEGREE, true); -// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE_VEL.name(), data[i++], Units.DEG_PER_S, true); -// xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION.name(), data[i++], Unit.DEGREE, true); -// xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION_PER.name(), data[i++], Unit.SECOND, true); -// xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION_PHASE.name(), data[i++], Unit.DEGREE, true); -// -// xmlGenerator.exitSection(); -// -// } + /** Write a spin/nutation/momentum entry in XML. + * @param xmlGenerator generator to use for producing output + * @param epoch of the entry + * @param data entry data + * @throws IOException if the output stream throws one while writing. + */ + void writeSpinNutationMomentum(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final String[] data) + throws IOException { + + // wrapping element + xmlGenerator.enterSection(AttitudeEntryKey.spinNutationMom.name()); + + // data part + xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, false, true); + int i = 0; + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ALPHA.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_DELTA.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE_VEL.name(), data[i++], Units.DEG_PER_S, true); + xmlGenerator.writeEntry(AttitudeEntryKey.MOMENTUM_ALPHA.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.MOMENTUM_DELTA.name(), data[i++], Unit.DEGREE, true); + xmlGenerator.writeEntry(AttitudeEntryKey.NUTATION_VEL.name(), data[i++], Units.DEG_PER_S, true); + + xmlGenerator.exitSection(); + + } /** Write an angle from an Euler sequence. * @param xmlGenerator generator to use @@ -704,7 +843,8 @@ void writeSpin(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final * @param angle angle value * @throws IOException if the output stream throws one while writing. */ - private void writeEulerAngle(final XmlGenerator xmlGenerator, final int index, final String seq, final String angle) + private void writeLabeledEulerAngle(final XmlGenerator xmlGenerator, final int index, + final String seq, final String angle) throws IOException { if (xmlGenerator.writeUnits(Unit.DEGREE)) { xmlGenerator.writeTwoAttributesElement(ROTATION + (index + 1), angle, @@ -724,7 +864,7 @@ private void writeEulerAngle(final XmlGenerator xmlGenerator, final int index, f * @param rate rate value * @throws IOException if the output stream throws one while writing. */ - private void writeEulerRate(final XmlGenerator xmlGenerator, final int index, final String seq, final String rate) + private void writeLabeledEulerRate(final XmlGenerator xmlGenerator, final int index, final String seq, final String rate) throws IOException { if (xmlGenerator.writeUnits(Units.DEG_PER_S)) { xmlGenerator.writeTwoAttributesElement(ROTATION + (index + 1), rate, diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntry.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntry.java index 18d319c9e5..1df7bbd6b5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntry.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntry.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -70,35 +70,33 @@ public void setComponent(final int i, final double value) { /** Set one angle. * @param axis axis label - * @param value value of the angle + * @param angle value of the angle (rad) */ - public void setAngle(final char axis, final double value) { + public void setLabeledAngle(final char axis, final double angle) { if (metadata.getEulerRotSeq() != null) { - final String seq = metadata.getEulerRotSeq().name(); - if (seq.charAt(0) == axis && Double.isNaN(components[0])) { - components[0] = value; - } else if (seq.charAt(1) == axis && Double.isNaN(components[1])) { - components[1] = value; - } else if (seq.charAt(2) == axis && Double.isNaN(components[2])) { - components[2] = value; + for (int i = 0; i < components.length; ++i) { + if (metadata.getEulerRotSeq().name().charAt(i) == axis && Double.isNaN(components[i])) { + setComponent(i, angle); + return; + } } } } /** Set one rate. * @param axis axis label - * @param value value of the rate + * @param rate value of the rate (rad/s) */ - public void setRate(final char axis, final double value) { + public void setLabeledRate(final char axis, final double rate) { if (metadata.getEulerRotSeq() != null) { - final String seq = metadata.getEulerRotSeq().name(); - final int first = metadata.getAttitudeType() == AttitudeType.QUATERNION_RATE ? 4 : 3; - if (seq.charAt(0) == axis && Double.isNaN(components[first])) { - components[first] = value; - } else if (seq.charAt(1) == axis && Double.isNaN(components[first + 1])) { - components[first + 1] = value; - } else if (seq.charAt(2) == axis && Double.isNaN(components[first + 2])) { - components[first + 2] = value; + final int first = (metadata.getAttitudeType() == AttitudeType.QUATERNION_ANGVEL || + metadata.getAttitudeType() == AttitudeType.QUATERNION_EULER_RATES) ? + 4 : 3; + for (int i = 0; i < 3; ++i) { + if (metadata.getEulerRotSeq().name().charAt(i) == axis && Double.isNaN(components[first + i])) { + setComponent(first + i, rate); + return; + } } } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntryKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntryKey.java index 6d1b78f29c..6b8059e2e6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntryKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeEntryKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.files.ccsds.ndm.adm.aem; import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.ndm.adm.AttitudeType; import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.utils.units.Unit; @@ -28,39 +29,74 @@ */ public enum AttitudeEntryKey { - /** Quaternion state sub-section. */ + /** Quaternion state sub-section (only for ADM V1). */ quaternionState((token, context, container) -> true), + /** Quaternion state sub-section. + * @since 12.0 + */ + quaternionEphemeris((token, context, container) -> true), + /** Quaternion/derivative sub-section. */ quaternionDerivative((token, context, container) -> true), - /** Quaternion/rate sub-section. */ + /** Quaternion/rate sub-section (only for ADM V1). */ quaternionEulerRate((token, context, container) -> true), + /** Quaternion/angular velocity sub-section. + * @since 12.0 + */ + quaternionAngVel((token, context, container) -> true), + /** Euler angle sub-section. */ eulerAngle((token, context, container) -> true), - /** Euler angle/rate sub-section. */ + /** Euler angle/rate sub-section (only for ADM V1). */ eulerAngleRate((token, context, container) -> true), + /** Euler angle/derivative sub-section. + * @since 12.0 + */ + eulerAngleDerivative((token, context, container) -> true), + + /** Euler angle/angular velocity sub-section. + * @since 12.0 + */ + eulerAngleAngVel((token, context, container) -> true), + /** Spin sub-section. */ spin((token, context, container) -> true), /** Spin/nutation sub-section. */ spinNutation((token, context, container) -> true), + /** Spin/nutation/momentum sub-section. + * @since 12.0 + */ + spinNutationMom((token, context, container) -> true), + /** Quaternion sub-sub-section. */ quaternion((token, context, container) -> true), - /** Quaternion rate sub-sub-section. */ + /** Quaternion rate sub-sub-section(only for ADM V1). */ quaternionRate((token, context, container) -> true), - /** Rotation angles sub-sub-section. */ + /** Quaternion rate sub-sub-section. + * @since 12.0 + */ + quaternionDot((token, context, container) -> true), + + /** Rotation angles sub-sub-section (only for ADM V1). */ rotationAngles((token, context, container) -> true), - /** Rotation rates sub-sub-section. */ + /** Rotation rates sub-sub-section (only for ADM V1). */ rotationRates((token, context, container) -> true), + /** Angular velocity sub-sub-section. + * @since 12.0 + */ + angVel((token, context, container) -> true), + /** Entry epoch. */ EPOCH((token, context, container) -> token.processAsDate(container::setEpoch, context)), @@ -104,32 +140,95 @@ public enum AttitudeEntryKey { Units.ONE_PER_S, context.getParsedUnitsBehavior(), container::setComponent)), - /** Rotation about X axis. */ + /** Angular velocity about X axis. + * @since 12.0 + */ + ANGVEL_X((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().getAttitudeType() == AttitudeType.QUATERNION_ANGVEL ? 4 : 3, + Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Angular velocity about Y axis. + * @since 12.0 + */ + ANGVEL_Y((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().getAttitudeType() == AttitudeType.QUATERNION_ANGVEL ? 5 : 4, + Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Angular velocity about Z axis. + * @since 12.0 + */ + ANGVEL_Z((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().getAttitudeType() == AttitudeType.QUATERNION_ANGVEL ? 6 : 5, + Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about first axis. + * @since 12.0 + */ + ANGLE_1((token, context, container) -> token.processAsIndexedDouble(0, + Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about second axis. + * @since 12.0 + */ + ANGLE_2((token, context, container) -> token.processAsIndexedDouble(1, + Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about third axis. + * @since 12.0 + */ + ANGLE_3((token, context, container) -> token.processAsIndexedDouble(2, + Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about first axis. + * @since 12.0 + */ + ANGLE_1_DOT((token, context, container) -> token.processAsIndexedDouble(3, + Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about second axis. + * @since 12.0 + */ + ANGLE_2_DOT((token, context, container) -> token.processAsIndexedDouble(4, + Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about third axis. + * @since 12.0 + */ + ANGLE_3_DOT((token, context, container) -> token.processAsIndexedDouble(5, + Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Rotation about X axis (only for ADM V1). */ X_ANGLE((token, context, container) -> token.processAsLabeledDouble('X', Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setAngle)), + container::setLabeledAngle)), - /** Rotation about Y axis. */ + /** Rotation about Y axis (only for ADM V1). */ Y_ANGLE((token, context, container) -> token.processAsLabeledDouble('Y', Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setAngle)), + container::setLabeledAngle)), - /** Rotation about Z axis. */ + /** Rotation about Z axis (only for ADM V1). */ Z_ANGLE((token, context, container) -> token.processAsLabeledDouble('Z', Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setAngle)), + container::setLabeledAngle)), - /** Rotation about X axis. */ + /** Rotation about X axis (only for ADM V1). */ X_RATE((token, context, container) -> token.processAsLabeledDouble('X', Units.DEG_PER_S, context.getParsedUnitsBehavior(), - container::setRate)), + container::setLabeledRate)), - /** Rotation about Y axis. */ + /** Rotation about Y axis (only for ADM V1). */ Y_RATE((token, context, container) -> token.processAsLabeledDouble('Y', Units.DEG_PER_S, context.getParsedUnitsBehavior(), - container::setRate)), + container::setLabeledRate)), - /** Rotation about Z axis. */ + /** Rotation about Z axis (only for ADM V1). */ Z_RATE((token, context, container) -> token.processAsLabeledDouble('Z', Units.DEG_PER_S, context.getParsedUnitsBehavior(), - container::setRate)), + container::setLabeledRate)), /** Right ascension of spin axis vector. */ SPIN_ALPHA((token, context, container) -> token.processAsIndexedDouble(0, Unit.DEGREE, context.getParsedUnitsBehavior(), @@ -148,7 +247,7 @@ public enum AttitudeEntryKey { container::setComponent)), /** Nutation angle entry. */ - NUTATION((token, context, container) -> token.processAsIndexedDouble(4, Units.DEG_PER_S, context.getParsedUnitsBehavior(), + NUTATION((token, context, container) -> token.processAsIndexedDouble(4, Unit.DEGREE, context.getParsedUnitsBehavior(), container::setComponent)), /** Nutation period entry. */ @@ -157,6 +256,24 @@ public enum AttitudeEntryKey { /** Nutation phase entry. */ NUTATION_PHASE((token, context, container) -> token.processAsIndexedDouble(6, Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Right ascension of angular momentum vector. + * @since 12.0 + */ + MOMENTUM_ALPHA((token, context, container) -> token.processAsIndexedDouble(4, Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Declination of angular momentum vector. + * @since 12.0 + */ + MOMENTUM_DELTA((token, context, container) -> token.processAsIndexedDouble(5, Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setComponent)), + + /** Nutation velocity entry. + * @since 12.0 + */ + NUTATION_VEL((token, context, container) -> token.processAsIndexedDouble(6, Units.DEG_PER_S, context.getParsedUnitsBehavior(), container::setComponent)); /** Processing method. */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeWriter.java index b150c876be..dd25b0b8fc 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/AttitudeWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,7 +22,7 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.FrameFacade; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.utils.FileFormat; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.files.ccsds.utils.generation.KvnGenerator; @@ -42,7 +42,7 @@ public class AttitudeWriter implements AttitudeEphemerisFileWriter { private final AemWriter writer; /** Header. */ - private final Header header; + private final AdmHeader header; /** Current metadata. */ private final AemMetadata metadata; @@ -53,6 +53,11 @@ public class AttitudeWriter implements AttitudeEphemerisFileWriter { /** Output name for error messages. */ private final String outputName; + /** Maximum offset for relative dates. + * @since 12.0 + */ + private final double maxRelativeOffset; + /** Column number for aligning units. */ private final int unitsColumn; @@ -79,19 +84,22 @@ public class AttitudeWriter implements AttitudeEphemerisFileWriter { * @param template template for metadata * @param fileFormat file format to use * @param outputName output name for error messages + * @param maxRelativeOffset maximum offset in seconds to use relative dates + * (if a date is too far from reference, it will be displayed as calendar elements) * @param unitsColumn columns number for aligning units (if negative or zero, units are not output) - * @since 11.0 + * @since 12.0 */ public AttitudeWriter(final AemWriter writer, - final Header header, final AemMetadata template, + final AdmHeader header, final AemMetadata template, final FileFormat fileFormat, final String outputName, - final int unitsColumn) { - this.writer = writer; - this.header = header; - this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); - this.fileFormat = fileFormat; - this.outputName = outputName; - this.unitsColumn = unitsColumn; + final double maxRelativeOffset, final int unitsColumn) { + this.writer = writer; + this.header = header; + this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); + this.fileFormat = fileFormat; + this.outputName = outputName; + this.maxRelativeOffset = maxRelativeOffset; + this.unitsColumn = unitsColumn; } /** {@inheritDoc} @@ -101,7 +109,7 @@ public AttitudeWriter(final AemWriter writer, * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation * method and interpolation degree. The missing values (like object name, local spacecraft * body frame, attitude type...) will be inherited from the template metadata set at writer - * {@link #AttitudeWriter(AemWriter, Header, AemMetadata, FileFormat, String, int) construction}. + * {@link #AttitudeWriter(AemWriter, AdmHeader, AemMetadata, FileFormat, String, double, int) construction}. *

          */ @Override @@ -132,8 +140,10 @@ void write(final Appendable appendable, final AttitudeEphemerisFile epheme } try (Generator generator = fileFormat == FileFormat.KVN ? - new KvnGenerator(appendable, AemWriter.KVN_PADDING_WIDTH, outputName, unitsColumn) : - new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, unitsColumn > 0)) { + new KvnGenerator(appendable, AemWriter.KVN_PADDING_WIDTH, outputName, + maxRelativeOffset, unitsColumn) : + new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, + maxRelativeOffset, unitsColumn > 0, null)) { writer.writeHeader(generator, header); @@ -171,7 +181,10 @@ void writeSegment(final Generator generator, final S segment) throws IOException } metadata.setInterpolationMethod(segment.getInterpolationMethod()); metadata.setInterpolationDegree(segment.getInterpolationSamples() - 1); - writer.writeMetadata(generator, metadata); + metadata.validate(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); + writer.writeMetadata(generator, + header == null ? writer.getDefaultVersion() : header.getFormatVersion(), + metadata); // Loop on attitude data writer.startAttitudeBlock(generator); @@ -179,7 +192,9 @@ void writeSegment(final Generator generator, final S segment) throws IOException generator.writeComments(((AemSegment) segment).getData().getComments()); } for (final TimeStampedAngularCoordinates coordinates : segment.getAngularCoordinates()) { - writer.writeAttitudeEphemerisLine(generator, metadata, coordinates); + writer.writeAttitudeEphemerisLine(generator, + header == null ? writer.getDefaultVersion() : header.getFormatVersion(), + metadata, coordinates); } writer.endAttitudeBlock(generator); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/StreamingAemWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/StreamingAemWriter.java index d96736b7b7..454a5bbb9f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/StreamingAemWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/StreamingAemWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,6 +22,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.propagation.Propagator; @@ -70,7 +71,7 @@ public class StreamingAemWriter implements AutoCloseable { private final AemWriter writer; /** Header. */ - private final Header header; + private final AdmHeader header; /** Current metadata. */ private final AemMetadata metadata; @@ -86,7 +87,7 @@ public class StreamingAemWriter implements AutoCloseable { * @since 11.0 */ public StreamingAemWriter(final Generator generator, final AemWriter writer, - final Header header, final AemMetadata template) { + final AdmHeader header, final AemMetadata template) { this.generator = generator; this.writer = writer; this.header = header; @@ -113,12 +114,23 @@ public void close() throws IOException { /** A writer for a segment of an AEM. */ public class SegmentWriter implements OrekitFixedStepHandler { + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public SegmentWriter() { + // nothing to do + } + /** * {@inheritDoc} * *

          Sets the {@link AemMetadataKey#START_TIME} and {@link AemMetadataKey#STOP_TIME} in this * segment's metadata if not already set by the user. Then calls {@link AemWriter#writeHeader(Generator, Header) - * writeHeader} if it is the first segment) and {@link AemWriter#writeMetadata(Generator, AemMetadata)} to start the segment. + * writeHeader} if it is the first segment) and {@link AemWriter#writeMetadata(Generator, double, AemMetadata)} to start the segment. */ @Override public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) { @@ -147,7 +159,9 @@ public void init(final SpacecraftState s0, final AbsoluteDate t, final double st // the external frame must be frame B metadata.getEndpoints().setFrameB(FrameFacade.map(s0.getAttitude().getReferenceFrame())); } - writer.writeMetadata(generator, metadata); + writer.writeMetadata(generator, + header == null ? writer.getDefaultVersion() : header.getFormatVersion(), + metadata); writer.startAttitudeBlock(generator); } catch (IOException e) { throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage()); @@ -158,7 +172,9 @@ public void init(final SpacecraftState s0, final AbsoluteDate t, final double st @Override public void handleStep(final SpacecraftState currentState) { try { - writer.writeAttitudeEphemerisLine(generator, metadata, currentState.getAttitude().getOrientation()); + writer.writeAttitudeEphemerisLine(generator, + header == null ? writer.getDefaultVersion() : header.getFormatVersion(), + metadata, currentState.getAttitude().getOrientation()); } catch (IOException e) { throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage()); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/XmlSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/XmlSubStructureKey.java index ce5eab80ef..a828353e63 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/XmlSubStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/XmlSubStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/package-info.java index 78680b91dd..dbcace233d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/aem/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocity.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocity.java new file mode 100644 index 0000000000..00411532c8 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocity.java @@ -0,0 +1,136 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.apm; + +import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; +import org.orekit.files.ccsds.section.CommentsContainer; + +/** + * Container for Attitude Parameter Message data lines. + * @author Luc Maisonobe + * @since 12.0 + */ +public class AngularVelocity extends CommentsContainer { + + /** Endpoints (i.e. frames A, B and their relationship). */ + private final AttitudeEndpoints endpoints; + + /** The frame in which angular velocities are specified. */ + private FrameFacade frame; + + /** Angular velocity around X axis (rad/s). */ + private double angVelX; + + /** Angular velocity around Y axis (rad/s). */ + private double angVelY; + + /** Angular velocity around Z axis (rad/s). */ + private double angVelZ; + + /** Simple constructor. + */ + public AngularVelocity() { + endpoints = new AttitudeEndpoints(); + frame = null; + angVelX = Double.NaN; + angVelY = Double.NaN; + angVelZ = Double.NaN; + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + AngularVelocityKey.REF_FRAME_A, + AngularVelocityKey.REF_FRAME_B, + null); + endpoints.checkExternalFrame(AngularVelocityKey.REF_FRAME_A, AngularVelocityKey.REF_FRAME_B); + checkNotNull(frame, AngularVelocityKey.ANGVEL_FRAME.name()); + checkNotNaN(angVelX, AngularVelocityKey.ANGVEL_X.name()); + checkNotNaN(angVelY, AngularVelocityKey.ANGVEL_Y.name()); + checkNotNaN(angVelZ, AngularVelocityKey.ANGVEL_Z.name()); + } + + /** Get the endpoints (i.e. frames A, B and their relationship). + * @return endpoints + */ + public AttitudeEndpoints getEndpoints() { + return endpoints; + } + + /** Set frame in which angular velocities are specified. + * @param frame frame in which angular velocities are specified + */ + public void setFrame(final FrameFacade frame) { + this.frame = frame; + } + + /** Get frame in which angular velocities are specified. + * @return frame in which angular velocities are specified + */ + public FrameFacade getFrame() { + return frame; + } + + /** Get the angular velocity around X axis (rad/s). + * @return angular velocity around X axis (rad/s) + */ + public double getAngVelX() { + return angVelX; + } + + /** Set the angular velocity around X axis (rad/s). + * @param angVelX angular velocity around X axis (rad/s) + */ + public void setAngVelX(final double angVelX) { + refuseFurtherComments(); + this.angVelX = angVelX; + } + + /** Get the angular velocity around Y axis (rad/s). + * @return angular velocity around Y axis (rad/s) + */ + public double getAngVelY() { + return angVelY; + } + + /** Set the angular velocity around Z axis (rad/s). + * @param angVelZ angular velocity around Z axis (rad/s) + */ + public void setAngVelZ(final double angVelZ) { + refuseFurtherComments(); + this.angVelZ = angVelZ; + } + + /** Get the angular velocity around Z axis (rad/s). + * @return angular velocity around Z axis (rad/s) + */ + public double getAngVelZ() { + return angVelZ; + } + + /** Set the angular velocity around Y axis (rad/s). + * @param angVelY angular velocity around Y axis (rad/s) + */ + public void setAngVelY(final double angVelY) { + refuseFurtherComments(); + this.angVelY = angVelY; + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParametersKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocityKey.java similarity index 52% rename from src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParametersKey.java rename to src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocityKey.java index a051c7081e..20276a7f8d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParametersKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocityKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2023 Luc Maisonobe * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,43 +21,44 @@ import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.lexical.TokenType; -/** Keys for {@link SpacecraftParameters APM spacecraft parameters} entries. - * @author Bryan Cazabonne - * @since 10.2 +/** Keys for {@link AngularVelocity APM angular velocity} entries. + * @author Luc Maisonobe + * @since 12.0 */ -public enum SpacecraftParametersKey { +public enum AngularVelocityKey { /** Comment entry. */ COMMENT((token, context, container) -> token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), - /** Inertia reference frame entry. */ - INERTIA_REF_FRAME((token, context, container) -> token.processAsFrame(container::setInertiaReferenceFrame, - context, false, false, true)), + /** First reference frame entry. */ + REF_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), - /** 1-axis moment of inertia entry. */ - I11((token, context, container) -> token.processAsDouble(Units.KG_M2, context.getParsedUnitsBehavior(), - container::setI11)), + /** Second reference frame entry. */ + REF_FRAME_B((token, context, container) -> { + if (token.getType() == TokenType.ENTRY) { + container.checkNotNull(container.getEndpoints().getFrameA(), REF_FRAME_A.name()); + final boolean aIsSpaceraftBody = container.getEndpoints().getFrameA().asSpacecraftBodyFrame() != null; + return token.processAsFrame(container.getEndpoints()::setFrameB, context, + aIsSpaceraftBody, aIsSpaceraftBody, !aIsSpaceraftBody); + } + return true; + }), - /** 2-axis moment of inertia entry. */ - I22((token, context, container) -> token.processAsDouble(Units.KG_M2, context.getParsedUnitsBehavior(), - container::setI22)), + /** Frame in which angular velocity is defined. */ + ANGVEL_FRAME((token, context, container) -> token.processAsFrame(container::setFrame, context, true, true, true)), - /** 3-axis moment of inertia entry. */ - I33((token, context, container) -> token.processAsDouble(Units.KG_M2, context.getParsedUnitsBehavior(), - container::setI33)), + /** Angular velocity around X axis. */ + ANGVEL_X((token, context, container) -> token.processAsDouble(Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setAngVelX)), - /** 1-axis / 2-axis inertia cross product entry. */ - I12((token, context, container) -> token.processAsDouble(Units.KG_M2, context.getParsedUnitsBehavior(), - container::setI12)), + /** Angular velocity around Y axis. */ + ANGVEL_Y((token, context, container) -> token.processAsDouble(Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setAngVelY)), - /** 1-axis / 3-axis inertia cross product entry. */ - I13((token, context, container) -> token.processAsDouble(Units.KG_M2, context.getParsedUnitsBehavior(), - container::setI13)), - - /** 2-axis / 3-axis inertia cross product entry. */ - I23((token, context, container) -> token.processAsDouble(Units.KG_M2, context.getParsedUnitsBehavior(), - container::setI23)); + /** Angular velocity around Z axis. */ + ANGVEL_Z((token, context, container) -> token.processAsDouble(Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setAngVelZ)); /** Processing method. */ private final TokenProcessor processor; @@ -65,7 +66,7 @@ public enum SpacecraftParametersKey { /** Simple constructor. * @param processor processing method */ - SpacecraftParametersKey(final TokenProcessor processor) { + AngularVelocityKey(final TokenProcessor processor) { this.processor = processor; } @@ -75,7 +76,7 @@ public enum SpacecraftParametersKey { * @param container container to fill * @return true of token was accepted */ - public boolean process(final ParseToken token, final ContextBinding context, final SpacecraftParameters container) { + public boolean process(final ParseToken token, final ContextBinding context, final AngularVelocity container) { return processor.process(token, context, container); } @@ -87,7 +88,7 @@ interface TokenProcessor { * @param container container to fill * @return true of token was accepted */ - boolean process(ParseToken token, ContextBinding context, SpacecraftParameters container); + boolean process(ParseToken token, ContextBinding context, AngularVelocity container); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocityWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocityWriter.java new file mode 100644 index 0000000000..2ae9672a0e --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/AngularVelocityWriter.java @@ -0,0 +1,66 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.apm; + +import java.io.IOException; + +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.generation.Generator; + +/** Writer for angular velocity data. + * @author Luc Maisonobe + * @since 12.0 + */ +class AngularVelocityWriter extends AbstractWriter { + + /** Angular velocity block. */ + private final AngularVelocity angularVelocity; + + /** Create a writer. + * @param xmlTag name of the XML tag surrounding the section + * @param kvnTag name of the KVN tag surrounding the section (may be null) + * @param angularVelocity angular velocity block + */ + AngularVelocityWriter(final String xmlTag, final String kvnTag, + final AngularVelocity angularVelocity) { + super(xmlTag, kvnTag); + this.angularVelocity = angularVelocity; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + generator.writeComments(angularVelocity.getComments()); + + // endpoints + generator.writeEntry(AngularVelocityKey.REF_FRAME_A.name(), angularVelocity.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(AngularVelocityKey.REF_FRAME_B.name(), angularVelocity.getEndpoints().getFrameB().getName(), null, true); + + // frame + generator.writeEntry(AngularVelocityKey.ANGVEL_FRAME.name(), angularVelocity.getFrame().getName(), null, true); + + // velocity + generator.writeEntry(AngularVelocityKey.ANGVEL_X.name(), angularVelocity.getAngVelX(), Units.DEG_PER_S, true); + generator.writeEntry(AngularVelocityKey.ANGVEL_Y.name(), angularVelocity.getAngVelY(), Units.DEG_PER_S, true); + generator.writeEntry(AngularVelocityKey.ANGVEL_Z.name(), angularVelocity.getAngVelZ(), Units.DEG_PER_S, true); + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Apm.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Apm.java index eb4c4ae23c..f44989e5ea 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Apm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Apm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,20 +18,15 @@ import java.util.List; -import org.hipparchus.complex.Quaternion; import org.orekit.attitudes.Attitude; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.ndm.NdmConstituent; import org.orekit.files.ccsds.ndm.adm.AdmMetadata; -import org.orekit.files.ccsds.ndm.adm.AttitudeType; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.section.Segment; import org.orekit.frames.Frame; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinatesProvider; -import org.orekit.utils.TimeStampedAngularCoordinates; /** * This class stores all the information of the Attitude Parameter Message (APM) File parsed @@ -39,7 +34,7 @@ * @author Bryan Cazabonne * @since 10.2 */ -public class Apm extends NdmConstituent> { +public class Apm extends NdmConstituent> { /** Root element for XML files. */ public static final String ROOT = "apm"; @@ -53,7 +48,7 @@ public class Apm extends NdmConstituent> { * @param conventions IERS conventions * @param dataContext used for creating frames, time scales, etc. */ - public Apm(final Header header, final List> segments, + public Apm(final AdmHeader header, final List> segments, final IERSConventions conventions, final DataContext dataContext) { super(header, segments, conventions, dataContext); } @@ -73,13 +68,6 @@ public ApmData getData() { } /** Get the attitude. - *

          - * The orientation part of the attitude is always extracted from the file mandatory - * {@link ApmQuaternion quaternion logical block}. The rotation rate part of - * the attitude is extracted from the {@link ApmQuaternion quaternion logical block} - * if rate is available there, or from the {@link Euler Euler logical block} if rate - * is missing from quaternion logical block but available in Euler logical block. - *

          * @param frame reference frame with respect to which attitude must be defined, * (may be null if attitude is not orbit-relative and one wants * attitude in the same frame as used in the attitude message) @@ -88,51 +76,7 @@ public ApmData getData() { * @return attitude */ public Attitude getAttitude(final Frame frame, final PVCoordinatesProvider pvProvider) { - - final ApmData data = getSegments().get(0).getData(); - final ApmQuaternion qBlock = data.getQuaternionBlock(); - final Euler eBlock = data.getEulerBlock(); - - final TimeStampedAngularCoordinates tac; - if (qBlock.hasRates()) { - // quaternion logical block includes everything we need - final Quaternion q = qBlock.getQuaternion(); - final Quaternion qDot = qBlock.getQuaternionDot(); - tac = AttitudeType.QUATERNION_DERIVATIVE.build(true, qBlock.getEndpoints().isExternal2SpacecraftBody(), - null, true, qBlock.getEpoch(), - q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(), - qDot.getQ0(), qDot.getQ1(), qDot.getQ2(), qDot.getQ3()); - } else if (eBlock != null && eBlock.hasRates()) { - // we have to rely on the Euler logical block to take rates into account - - if (!qBlock.getEndpoints().isCompatibleWith(eBlock.getEndpoints())) { - // nothing really prevents having two logical blocks with different settings - // but it is a nightmare to process and probably not worse the trouble. - // For now, we just throw an exception, we may reconsider this if some use case arises - throw new OrekitException(OrekitMessages.INCOMPATIBLE_FRAMES, - qBlock.getEndpoints().toString(), - eBlock.getEndpoints().toString()); - } - - final Quaternion q = qBlock.getQuaternion(); - final double[] rates = eBlock.getRotationRates(); - tac = AttitudeType.QUATERNION_RATE.build(true, - qBlock.getEndpoints().isExternal2SpacecraftBody(), - eBlock.getEulerRotSeq(), eBlock.isSpacecraftBodyRate(), qBlock.getEpoch(), - q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(), - rates[0], rates[1], rates[2]); - - } else { - // we rely only on the quaternion logical block, despite it doesn't include rates - final Quaternion q = qBlock.getQuaternion(); - tac = AttitudeType.QUATERNION.build(true, qBlock.getEndpoints().isExternal2SpacecraftBody(), - null, true, qBlock.getEpoch(), - q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3()); - } - - // build the attitude - return qBlock.getEndpoints().build(frame, pvProvider, tac); - + return getData().getAttitude(frame, pvProvider); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmData.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmData.java index 8fdcbb2105..a073501160 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,8 +20,17 @@ import java.util.Collections; import java.util.List; +import org.hipparchus.complex.Quaternion; +import org.orekit.attitudes.Attitude; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.ndm.adm.AttitudeType; import org.orekit.files.ccsds.section.CommentsContainer; import org.orekit.files.ccsds.section.Data; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedAngularCoordinates; /** * Container for Attitude Parameter Message data. @@ -33,57 +42,92 @@ public class ApmData implements Data { /** General comments block. */ private final CommentsContainer commentsBlock; + /** Epoch of the data. */ + private final AbsoluteDate epoch; + /** Quaternion block. */ private final ApmQuaternion quaternionBlock; /** Euler angles block. */ private final Euler eulerBlock; + /** Angular velocity block. + * @since 12.0 + */ + private final AngularVelocity angularVelocityBlock; + /** Spin-stabilized block. */ private final SpinStabilized spinStabilizedBlock; - /** Spacecraft parameters block. */ - private final SpacecraftParameters spacecraftParameters; + /** Inertia block. */ + private final Inertia inertia; /** Maneuvers. */ private final List maneuvers; /** Simple constructor. * @param commentsBlock general comments block - * @param quaternionBlock quaternion logical block + * @param epoch epoch of the data + * @param quaternionBlock quaternion logical block (may be null in ADM V2 or later) * @param eulerBlock Euler angles logicial block (may be null) + * @param angularVelocityBlock angular velocity block (may be null) * @param spinStabilizedBlock spin-stabilized logical block (may be null) - * @param spacecraftParameters spacecraft parameters logical block (may be null) + * @param inertia inertia logical block (may be null) */ public ApmData(final CommentsContainer commentsBlock, + final AbsoluteDate epoch, final ApmQuaternion quaternionBlock, final Euler eulerBlock, + final AngularVelocity angularVelocityBlock, final SpinStabilized spinStabilizedBlock, - final SpacecraftParameters spacecraftParameters) { + final Inertia inertia) { this.commentsBlock = commentsBlock; + this.epoch = epoch; this.quaternionBlock = quaternionBlock; this.eulerBlock = eulerBlock; + this.angularVelocityBlock = angularVelocityBlock; this.spinStabilizedBlock = spinStabilizedBlock; - this.spacecraftParameters = spacecraftParameters; + this.inertia = inertia; this.maneuvers = new ArrayList<>(); } /** {@inheritDoc} */ @Override public void validate(final double version) { - quaternionBlock.validate(version); + + if (version < 2.0) { + // quaternion block is mandatory in ADM V1 + if (quaternionBlock == null) { + // generate a dummy entry just for triggering the exception + new ApmQuaternion().validate(version); + } + } else { + // at least one logical block is mandatory in ADM V2 + if (quaternionBlock == null && eulerBlock == null && angularVelocityBlock == null && + spinStabilizedBlock == null && inertia == null) { + throw new OrekitException(OrekitMessages.CCSDS_INCOMPLETE_DATA); + } + } + + if (quaternionBlock != null) { + quaternionBlock.validate(version); + } if (eulerBlock != null) { eulerBlock.validate(version); } + if (angularVelocityBlock != null) { + angularVelocityBlock.validate(version); + } if (spinStabilizedBlock != null) { spinStabilizedBlock.validate(version); } - if (spacecraftParameters != null) { - spacecraftParameters.validate(version); + if (inertia != null) { + inertia.validate(version); } for (final Maneuver maneuver : maneuvers) { maneuver.validate(version); } + } /** Get the comments. @@ -93,6 +137,15 @@ public List getComments() { return commentsBlock.getComments(); } + /** + * Get the epoch of the data. + * @return epoch the epoch + * @since 12.0 + */ + public AbsoluteDate getEpoch() { + return epoch; + } + /** Get the quaternion logical block. * @return quaternion block */ @@ -107,6 +160,14 @@ public Euler getEulerBlock() { return eulerBlock; } + /** Get the angular velocity logical block. + * @return angular velocity block (may be null) + * @since 12.0 + */ + public AngularVelocity getAngularVelocityBlock() { + return angularVelocityBlock; + } + /** Get the spin-stabilized logical block. * @return spin-stabilized block (may be null) */ @@ -114,11 +175,11 @@ public SpinStabilized getSpinStabilizedBlock() { return spinStabilizedBlock; } - /** Get the spacecraft parameters logical block. - * @return spacecraft parameters block (may be null) + /** Get the inertia logical block. + * @return inertia block (may be null) */ - public SpacecraftParameters getSpacecraftParametersBlock() { - return spacecraftParameters; + public Inertia getInertiaBlock() { + return inertia; } /** @@ -163,4 +224,142 @@ public boolean hasManeuvers() { return !maneuvers.isEmpty(); } + /** Get the attitude. + * @param frame reference frame with respect to which attitude must be defined, + * (may be null if attitude is not orbit-relative and one wants + * attitude in the same frame as used in the attitude message) + * @param pvProvider provider for spacecraft position and velocity + * (may be null if attitude is not orbit-relative) + * @return attitude + * @since 12.0 + */ + public Attitude getAttitude(final Frame frame, final PVCoordinatesProvider pvProvider) { + + if (quaternionBlock != null) { + // we have a quaternion + final Quaternion q = quaternionBlock.getQuaternion(); + + final TimeStampedAngularCoordinates tac; + if (quaternionBlock.hasRates()) { + // quaternion logical block includes everything we need + final Quaternion qDot = quaternionBlock.getQuaternionDot(); + tac = AttitudeType.QUATERNION_DERIVATIVE.build(true, quaternionBlock.getEndpoints().isExternal2SpacecraftBody(), + null, true, epoch, + q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(), + qDot.getQ0(), qDot.getQ1(), qDot.getQ2(), qDot.getQ3()); + } else if (angularVelocityBlock != null) { + // get derivatives from the angular velocity logical block + tac = AttitudeType.QUATERNION_ANGVEL.build(true, + quaternionBlock.getEndpoints().isExternal2SpacecraftBody(), + null, true, epoch, + q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(), + angularVelocityBlock.getAngVelX(), + angularVelocityBlock.getAngVelY(), + angularVelocityBlock.getAngVelZ()); + } else if (eulerBlock != null && eulerBlock.hasRates()) { + // get derivatives from the Euler logical block + final double[] rates = eulerBlock.getRotationRates(); + if (eulerBlock.hasAngles()) { + // the Euler block has everything we need + final double[] angles = eulerBlock.getRotationAngles(); + tac = AttitudeType.EULER_ANGLE_DERIVATIVE.build(true, + eulerBlock.getEndpoints().isExternal2SpacecraftBody(), + eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch, + angles[0], angles[1], angles[2], + rates[0], rates[1], rates[2]); + } else { + // the Euler block has only the rates (we are certainly using an ADM V1 message) + // we need to rebuild the rotation from the quaternion + tac = AttitudeType.QUATERNION_EULER_RATES.build(true, + eulerBlock.getEndpoints().isExternal2SpacecraftBody(), + eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch, + q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(), + rates[0], rates[1], rates[2]); + } + + } else { + // we rely only on the quaternion logical block, despite it doesn't include rates + tac = AttitudeType.QUATERNION.build(true, quaternionBlock.getEndpoints().isExternal2SpacecraftBody(), + null, true, epoch, + q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3()); + } + + // build the attitude + return quaternionBlock.getEndpoints().build(frame, pvProvider, tac); + + } else if (eulerBlock != null) { + // we have Euler angles + final double[] angles = eulerBlock.getRotationAngles(); + + final TimeStampedAngularCoordinates tac; + if (eulerBlock.hasRates()) { + // the Euler block has everything we need + final double[] rates = eulerBlock.getRotationRates(); + tac = AttitudeType.EULER_ANGLE_DERIVATIVE.build(true, + eulerBlock.getEndpoints().isExternal2SpacecraftBody(), + eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch, + angles[0], angles[1], angles[2], + rates[0], rates[1], rates[2]); + } else if (angularVelocityBlock != null) { + // get derivatives from the angular velocity logical block + tac = AttitudeType.EULER_ANGLE_ANGVEL.build(true, + eulerBlock.getEndpoints().isExternal2SpacecraftBody(), + eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch, + angles[0], angles[1], angles[2], + angularVelocityBlock.getAngVelX(), + angularVelocityBlock.getAngVelY(), + angularVelocityBlock.getAngVelZ()); + } else { + // we rely only on the Euler logical block, despite it doesn't include rates + tac = AttitudeType.EULER_ANGLE.build(true, + eulerBlock.getEndpoints().isExternal2SpacecraftBody(), + eulerBlock.getEulerRotSeq(), eulerBlock.isSpacecraftBodyRate(), epoch, + angles[0], angles[1], angles[2]); + } + + // build the attitude + return eulerBlock.getEndpoints().build(frame, pvProvider, tac); + + } else if (spinStabilizedBlock != null) { + // we have a spin block + + final TimeStampedAngularCoordinates tac; + if (spinStabilizedBlock.hasNutation()) { + // we rely only on nutation + tac = AttitudeType.SPIN_NUTATION.build(true, true, null, true, epoch, + spinStabilizedBlock.getSpinAlpha(), + spinStabilizedBlock.getSpinDelta(), + spinStabilizedBlock.getSpinAngle(), + spinStabilizedBlock.getSpinAngleVel(), + spinStabilizedBlock.getNutation(), + spinStabilizedBlock.getNutationPeriod(), + spinStabilizedBlock.getNutationPhase()); + } else if (spinStabilizedBlock.hasMomentum()) { + // we rely only on momentum + tac = AttitudeType.SPIN_NUTATION_MOMENTUM.build(true, true, null, true, epoch, + spinStabilizedBlock.getSpinAlpha(), + spinStabilizedBlock.getSpinDelta(), + spinStabilizedBlock.getSpinAngle(), + spinStabilizedBlock.getSpinAngleVel(), + spinStabilizedBlock.getMomentumAlpha(), + spinStabilizedBlock.getMomentumDelta(), + spinStabilizedBlock.getNutationVel()); + } else { + // we rely only on the spin logical block, despite it doesn't include rates + tac = AttitudeType.SPIN.build(true, true, null, true, epoch, + spinStabilizedBlock.getSpinAlpha(), + spinStabilizedBlock.getSpinDelta(), + spinStabilizedBlock.getSpinAngle(), + spinStabilizedBlock.getSpinAngleVel()); + } + + // build the attitude + return spinStabilizedBlock.getEndpoints().build(frame, pvProvider, tac); + + } else { + throw new OrekitException(OrekitMessages.CCSDS_INCOMPLETE_DATA); + } + + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmDataSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmDataSubStructureKey.java new file mode 100644 index 0000000000..c07cd19023 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmDataSubStructureKey.java @@ -0,0 +1,111 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.apm; + +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; + +/** Keywords for APM data sub-structure. + * @author Luc Maisonobe + * @since 11.0 + */ +enum ApmDataSubStructureKey { + + /** General comment. */ + COMMENT((token, context, parser) -> token.getType() == TokenType.ENTRY ? parser.addGeneralComment(token.getContentAsNormalizedString()) : true), + + /** Epoch. */ + EPOCH((token, context, parser) -> token.processAsDate(parser::setEpoch, context)), + + /** Quaternion block. */ + QUAT((token, context, parser) -> parser.manageQuaternionSection(token.getType() == TokenType.START)), + + /** Quaternion section. */ + quaternionState((token, context, parser) -> parser.manageQuaternionSection(token.getType() == TokenType.START)), + + /** Euler elements. */ + EULER((token, context, parser) -> parser.manageEulerElementsSection(token.getType() == TokenType.START)), + + /** Euler elements. */ + eulerAngleState((token, context, parser) -> parser.manageEulerElementsSection(token.getType() == TokenType.START)), + + /** Angular velocity elements. */ + ANGVEL((token, context, parser) -> parser.manageAngularVelocitylementsSection(token.getType() == TokenType.START)), + + /** Angular velocity elements. */ + angularVelocity((token, context, parser) -> parser.manageAngularVelocitylementsSection(token.getType() == TokenType.START)), + + /** Euler elements / three axis stabilized section (ADM V1 only). */ + eulerElementsThree((token, context, parser) -> parser.manageEulerElementsSection(token.getType() == TokenType.START)), + + /** Euler elements /spin stabilized section (ADM V1 only). */ + eulerElementsSpin((token, context, parser) -> parser.manageSpinElementsSection(token.getType() == TokenType.START)), + + /** Spin elements. */ + SPIN((token, context, parser) -> parser.manageSpinElementsSection(token.getType() == TokenType.START)), + + /** Spin elements. */ + spin((token, context, parser) -> parser.manageSpinElementsSection(token.getType() == TokenType.START)), + + /** Spacecraft parameters section (ADM V1 only). */ + spacecraftParameters((token, context, parser) -> parser.manageInertiaSection(token.getType() == TokenType.START)), + + /** Inertia elements. */ + INERTIA((token, context, parser) -> parser.manageInertiaSection(token.getType() == TokenType.START)), + + /** Inertia elements. */ + inertia((token, context, parser) -> parser.manageInertiaSection(token.getType() == TokenType.START)), + + /** Maneuver parameters section. */ + MAN((token, context, parser) -> parser.manageManeuverParametersSection(token.getType() == TokenType.START)), + + /** Maneuver parameters section. */ + maneuverParameters((token, context, parser) -> parser.manageManeuverParametersSection(token.getType() == TokenType.START)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + ApmDataSubStructureKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process one token. + * @param token token to process + * @param context context binding + * @param parser APM file parser + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final ApmParser parser) { + return processor.process(token, context, parser); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param parser APM file parser + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, ApmParser parser); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmParser.java index cc750ea4ef..71e62759dd 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,14 +18,16 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.adm.AdmCommonMetadataKey; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.ndm.adm.AdmMetadata; import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey; import org.orekit.files.ccsds.ndm.adm.AdmParser; import org.orekit.files.ccsds.section.CommentsContainer; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.MetadataKey; import org.orekit.files.ccsds.section.Segment; @@ -56,7 +58,7 @@ public class ApmParser extends AdmParser { /** File header. */ - private Header header; + private AdmHeader header; /** File segments. */ private List> segments; @@ -67,6 +69,11 @@ public class ApmParser extends AdmParser { /** Context binding valid for current metadata. */ private ContextBinding context; + /** APM epoch. + * @since 12.0 + */ + private AbsoluteDate epoch; + /** APM general comments block being read. */ private CommentsContainer commentsBlock; @@ -76,11 +83,18 @@ public class ApmParser extends AdmParser { /** APM Euler angles logical block being read. */ private Euler eulerBlock; + /** APM angular velocity logical block being read. + * @since 12.0 + */ + private AngularVelocity angularVelocityBlock; + /** APM spin-stabilized logical block being read. */ private SpinStabilized spinStabilizedBlock; - /** APM spacecraft parameters logical block being read. */ - private SpacecraftParameters spacecraftParametersBlock; + /** APM inertia block being read. + * @since 12.0 + */ + private Inertia inertiaBlock; /** Current maneuver. */ private Maneuver currentManeuver; @@ -103,32 +117,35 @@ public class ApmParser extends AdmParser { * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems * (may be null if time system is absolute) * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ public ApmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, - final AbsoluteDate missionReferenceDate, final ParsedUnitsBehavior parsedUnitsBehavior) { + final AbsoluteDate missionReferenceDate, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { super(Apm.ROOT, Apm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, - missionReferenceDate, parsedUnitsBehavior); + missionReferenceDate, parsedUnitsBehavior, filters); } /** {@inheritDoc} */ @Override - public Header getHeader() { + public AdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(2.0); - segments = new ArrayList<>(); - metadata = null; - context = null; - quaternionBlock = null; - eulerBlock = null; - spinStabilizedBlock = null; - spacecraftParametersBlock = null; - currentManeuver = null; - maneuvers = new ArrayList<>(); + header = new AdmHeader(); + segments = new ArrayList<>(); + metadata = null; + context = null; + quaternionBlock = null; + eulerBlock = null; + spinStabilizedBlock = null; + inertiaBlock = null; + currentManeuver = null; + maneuvers = new ArrayList<>(); if (fileFormat == FileFormat.XML) { structureProcessor = new XmlStructureProcessingState(Apm.ROOT, this); reset(fileFormat, structureProcessor); @@ -177,7 +194,7 @@ public boolean prepareMetadata() { /** {@inheritDoc} */ @Override public boolean inMetadata() { - anticipateNext(getFileFormat() == FileFormat.XML ? structureProcessor : this::processGeneralCommentToken); + anticipateNext(getFileFormat() == FileFormat.XML ? structureProcessor : this::processDataToken); return true; } @@ -192,7 +209,8 @@ public boolean finalizeMetadata() { @Override public boolean prepareData() { commentsBlock = new CommentsContainer(); - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : this::processGeneralCommentToken); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processDataToken : this::processDataSubStructureToken); return true; } @@ -206,21 +224,27 @@ public boolean inData() { @Override public boolean finalizeData() { if (metadata != null) { - final ApmData data = new ApmData(commentsBlock, quaternionBlock, eulerBlock, - spinStabilizedBlock, spacecraftParametersBlock); + final ApmData data = new ApmData(commentsBlock, epoch, quaternionBlock, eulerBlock, + angularVelocityBlock, spinStabilizedBlock, inertiaBlock); + if (currentManeuver != null) { + // current maneuver is completed + maneuvers.add(currentManeuver); + currentManeuver = null; + } for (final Maneuver maneuver : maneuvers) { data.addManeuver(maneuver); } data.validate(header.getFormatVersion()); segments.add(new Segment<>(metadata, data)); } - metadata = null; - context = null; - quaternionBlock = null; - eulerBlock = null; - spinStabilizedBlock = null; - spacecraftParametersBlock = null; - currentManeuver = null; + metadata = null; + context = null; + quaternionBlock = null; + eulerBlock = null; + angularVelocityBlock = null; + spinStabilizedBlock = null; + inertiaBlock = null; + currentManeuver = null; return true; } @@ -242,6 +266,14 @@ boolean addGeneralComment(final String comment) { return commentsBlock.addComment(comment); } + /** Set current epoch. + * @param epoch epoch to set + * @since 12.0 + */ + void setEpoch(final AbsoluteDate epoch) { + this.epoch = epoch; + } + /** Manage quaternion section. * @param starting if true, parser is entering the section * otherwise it is leaving the section @@ -257,28 +289,40 @@ boolean manageQuaternionSection(final boolean starting) { * otherwise it is leaving the section * @return always return true */ - boolean manageEulerElementsThreeSection(final boolean starting) { + boolean manageEulerElementsSection(final boolean starting) { anticipateNext(starting ? this::processEulerToken : structureProcessor); return true; } + /** Manage angular velocity section. + * @param starting if true, parser is entering the section + * otherwise it is leaving the section + * @return always return true + * @since 12.0 + */ + boolean manageAngularVelocitylementsSection(final boolean starting) { + anticipateNext(starting ? this::processAngularVelocityToken : structureProcessor); + return true; + } + /** Manage Euler elements /spin stabilized section. * @param starting if true, parser is entering the section * otherwise it is leaving the section * @return always return true */ - boolean manageEulerElementsSpinSection(final boolean starting) { + boolean manageSpinElementsSection(final boolean starting) { anticipateNext(starting ? this::processSpinStabilizedToken : structureProcessor); return true; } - /** Manage spacecraft parameters section. + /** Manage inertia section. * @param starting if true, parser is entering the section * otherwise it is leaving the section * @return always return true + * @since 12.0 */ - boolean manageSpacecraftParametersSection(final boolean starting) { - anticipateNext(starting ? this::processSpacecraftParametersToken : structureProcessor); + boolean manageInertiaSection(final boolean starting) { + anticipateNext(starting ? this::processInertiaToken : structureProcessor); return true; } @@ -310,31 +354,35 @@ private boolean processMetadataToken(final ParseToken token) { try { return AdmMetadataKey.valueOf(token.getName()).process(token, context, metadata); } catch (IllegalArgumentException iaeD) { - // token has not been recognized - return false; + try { + return AdmCommonMetadataKey.valueOf(token.getName()).process(token, context, metadata); + } catch (IllegalArgumentException iaeC) { + // token has not been recognized + return false; + } } } } - /** Process one XML data substructure token. + /** Process one data substructure token. * @param token token to process * @return true if token was processed, false otherwise */ - private boolean processXmlSubStructureToken(final ParseToken token) { + private boolean processDataSubStructureToken(final ParseToken token) { try { return token.getName() != null && - XmlSubStructureKey.valueOf(token.getName()).process(token, this); + ApmDataSubStructureKey.valueOf(token.getName()).process(token, context, this); } catch (IllegalArgumentException iae) { // token has not been recognized return false; } } - /** Process one comment token. + /** Process one data token. * @param token token to process * @return true if token was processed, false otherwise */ - private boolean processGeneralCommentToken(final ParseToken token) { + private boolean processDataToken(final ParseToken token) { if (commentsBlock == null) { // APM KVN file lack a META_STOP keyword, hence we can't call finalizeMetadata() // automatically before the first data token arrives @@ -343,12 +391,18 @@ private boolean processGeneralCommentToken(final ParseToken token) { // automatically before the first data token arrives prepareData(); } - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : this::processQuaternionToken); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processQuaternionToken : this::processDataSubStructureToken); if ("COMMENT".equals(token.getName())) { if (token.getType() == TokenType.ENTRY) { commentsBlock.addComment(token.getContentAsNormalizedString()); } return true; + } else if ("EPOCH".equals(token.getName())) { + if (token.getType() == TokenType.ENTRY) { + token.processAsDate(date -> epoch = date, context); + } + return true; } else { return false; } @@ -363,10 +417,11 @@ private boolean processQuaternionToken(final ParseToken token) { if (quaternionBlock == null) { quaternionBlock = new ApmQuaternion(); } - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : this::processEulerToken); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processEulerToken : this::processDataSubStructureToken); try { return token.getName() != null && - ApmQuaternionKey.valueOf(token.getName()).process(token, context, quaternionBlock); + ApmQuaternionKey.valueOf(token.getName()).process(token, context, quaternionBlock, this::setEpoch); } catch (IllegalArgumentException iae) { // token has not been recognized return false; @@ -378,10 +433,16 @@ private boolean processQuaternionToken(final ParseToken token) { * @return true if token was processed, false otherwise */ private boolean processEulerToken(final ParseToken token) { + commentsBlock.refuseFurtherComments(); if (eulerBlock == null) { eulerBlock = new Euler(); + if (moveCommentsIfEmpty(quaternionBlock, eulerBlock)) { + // get rid of the empty logical block + quaternionBlock = null; + } } - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : this::processSpinStabilizedToken); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processAngularVelocityToken : this::processDataSubStructureToken); try { return token.getName() != null && EulerKey.valueOf(token.getName()).process(token, context, eulerBlock); @@ -391,19 +452,46 @@ private boolean processEulerToken(final ParseToken token) { } } + /** Process one angular velocity data token. + * @param token token to process + * @return true if token was processed, false otherwise + * @since 12.0 + */ + private boolean processAngularVelocityToken(final ParseToken token) { + commentsBlock.refuseFurtherComments(); + if (angularVelocityBlock == null) { + angularVelocityBlock = new AngularVelocity(); + if (moveCommentsIfEmpty(eulerBlock, angularVelocityBlock)) { + // get rid of the empty logical block + eulerBlock = null; + } + } + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processSpinStabilizedToken : this::processDataSubStructureToken); + try { + return token.getName() != null && + AngularVelocityKey.valueOf(token.getName()).process(token, context, angularVelocityBlock); + } catch (IllegalArgumentException iae) { + // token has not been recognized + return false; + } + } + /** Process one spin-stabilized data token. * @param token token to process * @return true if token was processed, false otherwise */ private boolean processSpinStabilizedToken(final ParseToken token) { + commentsBlock.refuseFurtherComments(); if (spinStabilizedBlock == null) { spinStabilizedBlock = new SpinStabilized(); - if (moveCommentsIfEmpty(eulerBlock, spinStabilizedBlock)) { + if (moveCommentsIfEmpty(angularVelocityBlock, spinStabilizedBlock)) { // get rid of the empty logical block - eulerBlock = null; + angularVelocityBlock = null; } } - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : this::processSpacecraftParametersToken); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processInertiaToken : this::processDataSubStructureToken); try { return token.getName() != null && SpinStabilizedKey.valueOf(token.getName()).process(token, context, spinStabilizedBlock); @@ -417,18 +505,20 @@ private boolean processSpinStabilizedToken(final ParseToken token) { * @param token token to process * @return true if token was processed, false otherwise */ - private boolean processSpacecraftParametersToken(final ParseToken token) { - if (spacecraftParametersBlock == null) { - spacecraftParametersBlock = new SpacecraftParameters(); - if (moveCommentsIfEmpty(spinStabilizedBlock, spacecraftParametersBlock)) { + private boolean processInertiaToken(final ParseToken token) { + commentsBlock.refuseFurtherComments(); + if (inertiaBlock == null) { + inertiaBlock = new Inertia(); + if (moveCommentsIfEmpty(spinStabilizedBlock, inertiaBlock)) { // get rid of the empty logical block spinStabilizedBlock = null; } } - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : this::processManeuverToken); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + this::processManeuverToken : this::processDataSubStructureToken); try { return token.getName() != null && - SpacecraftParametersKey.valueOf(token.getName()).process(token, context, spacecraftParametersBlock); + InertiaKey.valueOf(token.getName()).process(token, context, inertiaBlock); } catch (IllegalArgumentException iae) { // token has not been recognized return false; @@ -440,30 +530,25 @@ private boolean processSpacecraftParametersToken(final ParseToken token) { * @return true if token was processed, false otherwise */ private boolean processManeuverToken(final ParseToken token) { + commentsBlock.refuseFurtherComments(); if (currentManeuver == null) { currentManeuver = new Maneuver(); - if (moveCommentsIfEmpty(spacecraftParametersBlock, currentManeuver)) { + if (moveCommentsIfEmpty(inertiaBlock, currentManeuver)) { // get rid of the empty logical block - spacecraftParametersBlock = null; + inertiaBlock = null; } } - anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlSubStructureToken : new ErrorState()); + anticipateNext(getFileFormat() == FileFormat.KVN && header.getFormatVersion() < 2.0 ? + new ErrorState() : this::processDataSubStructureToken); try { - if (token.getName() != null && - ManeuverKey.valueOf(token.getName()).process(token, context, currentManeuver)) { - // the token was processed properly - if (currentManeuver.completed()) { - // current maneuver is completed - maneuvers.add(currentManeuver); - currentManeuver = null; - } - return true; - } + return token.getName() != null && + ManeuverKey.valueOf(token.getName()).process(token, context, currentManeuver); } catch (IllegalArgumentException iae) { - // ignored, delegate to next state below + // token has not been recognized + maneuvers.add(currentManeuver); + currentManeuver = null; + return false; } - // the token was not processed - return false; } /** Move comments from one empty logical block to another logical block. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternion.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternion.java index b590ae2a80..38b4a14aac 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternion.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternion.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,33 +18,21 @@ import java.util.Arrays; -import org.hipparchus.analysis.differentiation.UnivariateDerivative1; import org.hipparchus.complex.Quaternion; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; -import org.hipparchus.geometry.euclidean.threed.Rotation; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.attitudes.Attitude; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; -import org.orekit.files.ccsds.section.Section; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.utils.PVCoordinatesProvider; -import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; +import org.orekit.files.ccsds.section.CommentsContainer; /** * Container for Attitude Parameter Message quaternion logical block. * @author Bryan Cazabonne * @since 10.2 */ -public class ApmQuaternion implements Section { - - /** Epoch of the data. */ - private AbsoluteDate epoch; +public class ApmQuaternion extends CommentsContainer { /** Endpoints (i.e. frames A, B and their relationship). */ - private final AttitudeEndoints endpoints; + private final AttitudeEndpoints endpoints; /** Quaternion. */ private double[] q; @@ -55,7 +43,7 @@ public class ApmQuaternion implements Section { /** Simple constructor. */ public ApmQuaternion() { - endpoints = new AttitudeEndoints(); + endpoints = new AttitudeEndpoints(); q = new double[4]; qDot = new double[4]; Arrays.fill(q, Double.NaN); @@ -65,35 +53,29 @@ public ApmQuaternion() { /** {@inheritDoc} */ @Override public void validate(final double version) { - endpoints.checkMandatoryEntriesExceptExternalFrame(ApmQuaternionKey.Q_FRAME_A, - ApmQuaternionKey.Q_FRAME_B, - ApmQuaternionKey.Q_DIR); - endpoints.checkExternalFrame(ApmQuaternionKey.Q_FRAME_A, ApmQuaternionKey.Q_FRAME_B); + super.validate(version); + if (version < 2.0) { + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + ApmQuaternionKey.Q_FRAME_A, + ApmQuaternionKey.Q_FRAME_B, + ApmQuaternionKey.Q_DIR); + endpoints.checkExternalFrame(ApmQuaternionKey.Q_FRAME_A, ApmQuaternionKey.Q_FRAME_B); + } else { + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + ApmQuaternionKey.REF_FRAME_A, + ApmQuaternionKey.REF_FRAME_B, + ApmQuaternionKey.Q_DIR); + endpoints.checkExternalFrame(ApmQuaternionKey.REF_FRAME_A, ApmQuaternionKey.REF_FRAME_B); + } if (Double.isNaN(q[0] + q[1] + q[2] + q[3])) { throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "Q{C|1|2|3}"); } } - /** - * Get the epoch of the data. - * @return epoch the epoch - */ - public AbsoluteDate getEpoch() { - return epoch; - } - - /** - * Set the epoch of the data. - * @param epoch the epoch to be set - */ - public void setEpoch(final AbsoluteDate epoch) { - this.epoch = epoch; - } - /** Get the endpoints (i.e. frames A, B and their relationship). * @return endpoints */ - public AttitudeEndoints getEndpoints() { + public AttitudeEndpoints getEndpoints() { return endpoints; } @@ -111,6 +93,7 @@ public Quaternion getQuaternion() { * @param value quaternion component */ public void setQ(final int index, final double value) { + refuseFurtherComments(); this.q[index] = value; } @@ -128,6 +111,7 @@ public Quaternion getQuaternionDot() { * @param derivative quaternion derivative component */ public void setQDot(final int index, final double derivative) { + refuseFurtherComments(); this.qDot[index] = derivative; } @@ -138,36 +122,4 @@ public boolean hasRates() { return !Double.isNaN(qDot[0] + qDot[1] + qDot[2] + qDot[3]); } - /** Get the attitude. - * @param frame reference frame with respect to which attitude must be defined - * (may be null if attitude is not orbit-relative and one wants - * attitude in the same frame as used in the attitude message) - * @param pvProvider provider for spacecraft position and velocity - * (may be null if attitude is not orbit-relative) - * @return attitude - */ - public Attitude getAttitude(final Frame frame, final PVCoordinatesProvider pvProvider) { - - if (Double.isNaN(qDot[0])) { - // rotation as it is stored in the APM - final Rotation r = new Rotation(q[0], q[1], q[2], q[3], true); - - // attitude without rotation rate - return endpoints.build(frame, pvProvider, - new TimeStampedAngularCoordinates(epoch, r, Vector3D.ZERO, Vector3D.ZERO)); - - } else { - // attitude as it is stored in the APM - final UnivariateDerivative1 q0 = new UnivariateDerivative1(q[0], qDot[0]); - final UnivariateDerivative1 q1 = new UnivariateDerivative1(q[1], qDot[1]); - final UnivariateDerivative1 q2 = new UnivariateDerivative1(q[2], qDot[2]); - final UnivariateDerivative1 q3 = new UnivariateDerivative1(q[3], qDot[3]); - final FieldRotation rd = new FieldRotation<>(q0, q1, q2, q3, true); - - // attitude converted to Orekit conventions - return endpoints.build(frame, pvProvider, new TimeStampedAngularCoordinates(epoch, rd)); - } - - } - } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionKey.java index 9fd6ee3eb4..d17835508e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,22 +29,41 @@ public enum ApmQuaternionKey { /** Quaternion wrapping element in XML files. */ - quaternion((token, context, container) -> true), + quaternion((token, context, container, epochSetter) -> true), - /** Quaternion wrapping element in XML files. */ - quaternionRate((token, context, container) -> true), + /** Quaternion derivative wrapping element in XML files (ADM V1 only). */ + quaternionRate((token, context, container, epochSetter) -> true), + + /** Quaternion derivative wrapping element in XML files. + * @since 12.0 + */ + quaternionDot((token, context, container, epochSetter) -> true), + + /** Comment entry. */ + COMMENT((token, context, container, epochSetter) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), - /** Epoch entry. */ - EPOCH((token, context, container) -> token.processAsDate(container::setEpoch, context)), + /** Epoch entry (only for ADM V1). */ + EPOCH((token, context, container, epochSetter) -> token.processAsDate(epochSetter, context)), - /** First reference frame entry. */ - Q_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), + /** First reference frame entry (ADM V1 only). */ + Q_FRAME_A((token, context, container, epochSetter) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), - /** Second reference frame entry. */ - Q_FRAME_B((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameB, context, true, true, true)), + /** First reference frame entry. + * @since 12.0 + */ + REF_FRAME_A((token, context, container, epochSetter) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), + + /** Second reference frame entry (ADM V1 only). */ + Q_FRAME_B((token, context, container, epochSetter) -> token.processAsFrame(container.getEndpoints()::setFrameB, context, true, true, true)), + + /** Second reference frame entry. + * @since 12.0 + */ + REF_FRAME_B((token, context, container, epochSetter) -> token.processAsFrame(container.getEndpoints()::setFrameB, context, true, true, true)), /** Rotation direction entry. */ - Q_DIR((token, context, container) -> { + Q_DIR((token, context, container, epochSetter) -> { if (token.getType() == TokenType.ENTRY) { container.getEndpoints().setA2b(token.getContentAsUppercaseCharacter() == 'A'); } @@ -52,36 +71,36 @@ public enum ApmQuaternionKey { }), /** Scalar part of the quaternion entry. */ - QC((token, context, container) -> token.processAsIndexedDouble(0, Unit.ONE, context.getParsedUnitsBehavior(), - container::setQ)), + QC((token, context, container, epochSetter) -> token.processAsIndexedDouble(0, Unit.ONE, context.getParsedUnitsBehavior(), + container::setQ)), /** First component of the vector part of the quaternion entry. */ - Q1((token, context, container) -> token.processAsIndexedDouble(1, Unit.ONE, context.getParsedUnitsBehavior(), - container::setQ)), + Q1((token, context, container, epochSetter) -> token.processAsIndexedDouble(1, Unit.ONE, context.getParsedUnitsBehavior(), + container::setQ)), /** Second component of the vector part of the quaternion entry. */ - Q2((token, context, container) -> token.processAsIndexedDouble(2, Unit.ONE, context.getParsedUnitsBehavior(), - container::setQ)), + Q2((token, context, container, epochSetter) -> token.processAsIndexedDouble(2, Unit.ONE, context.getParsedUnitsBehavior(), + container::setQ)), /** Third component of the vector part of the quaternion entry. */ - Q3((token, context, container) -> token.processAsIndexedDouble(3, Unit.ONE, context.getParsedUnitsBehavior(), - container::setQ)), + Q3((token, context, container, epochSetter) -> token.processAsIndexedDouble(3, Unit.ONE, context.getParsedUnitsBehavior(), + container::setQ)), /** Scalar part of the quaternion derivative entry. */ - QC_DOT((token, context, container) -> token.processAsIndexedDouble(0, Units.ONE_PER_S, context.getParsedUnitsBehavior(), - container::setQDot)), + QC_DOT((token, context, container, epochSetter) -> token.processAsIndexedDouble(0, Units.ONE_PER_S, context.getParsedUnitsBehavior(), + container::setQDot)), /** First component of the vector part of the quaternion derivative entry. */ - Q1_DOT((token, context, container) -> token.processAsIndexedDouble(1, Units.ONE_PER_S, context.getParsedUnitsBehavior(), - container::setQDot)), + Q1_DOT((token, context, container, epochSetter) -> token.processAsIndexedDouble(1, Units.ONE_PER_S, context.getParsedUnitsBehavior(), + container::setQDot)), /** Second component of the vector part of the quaternion derivative entry. */ - Q2_DOT((token, context, container) -> token.processAsIndexedDouble(2, Units.ONE_PER_S, context.getParsedUnitsBehavior(), - container::setQDot)), + Q2_DOT((token, context, container, epochSetter) -> token.processAsIndexedDouble(2, Units.ONE_PER_S, context.getParsedUnitsBehavior(), + container::setQDot)), /** Third component of the vector part of the quaternion derivative entry. */ - Q3_DOT((token, context, container) -> token.processAsIndexedDouble(3, Units.ONE_PER_S, context.getParsedUnitsBehavior(), - container::setQDot)); + Q3_DOT((token, context, container, epochSetter) -> token.processAsIndexedDouble(3, Units.ONE_PER_S, context.getParsedUnitsBehavior(), + container::setQDot)); /** Processing method. */ private final TokenProcessor processor; @@ -97,10 +116,12 @@ public enum ApmQuaternionKey { * @param token token to process * @param context context binding * @param container container to fill + * @param epochSetter setter for the epoch (used only in ADM V1 XML files) * @return true of token was accepted */ - public boolean process(final ParseToken token, final ContextBinding context, final ApmQuaternion container) { - return processor.process(token, context, container); + public boolean process(final ParseToken token, final ContextBinding context, + final ApmQuaternion container, final ParseToken.DateConsumer epochSetter) { + return processor.process(token, context, container, epochSetter); } /** Interface for processing one token. */ @@ -109,9 +130,11 @@ interface TokenProcessor { * @param token token to process * @param context context binding * @param container container to fill + * @param epochSetter setter for the epoch (used only in ADM V1 XML files) * @return true of token was accepted */ - boolean process(ParseToken token, ContextBinding context, ApmQuaternion container); + boolean process(ParseToken token, ContextBinding context, + ApmQuaternion container, ParseToken.DateConsumer epochSetter); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionWriter.java index 7d1577ed90..362793dc72 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmQuaternionWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,11 @@ import org.hipparchus.complex.Quaternion; import org.orekit.files.ccsds.definitions.TimeConverter; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.FileFormat; import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.units.Unit; /** Writer for quaternion data. @@ -32,22 +34,35 @@ */ class ApmQuaternionWriter extends AbstractWriter { + /** Format version. + * @since 12.0 + */ + private final double formatVersion; + /** Quaternion block. */ private final ApmQuaternion quaternion; + /** Quaternion epoch. */ + private final AbsoluteDate epoch; + /** Converter for dates. */ private final TimeConverter timeConverter; /** Create a writer. + * @param formatVersion format version * @param xmlTag name of the XML tag surrounding the section * @param kvnTag name of the KVN tag surrounding the section (may be null) * @param quaternion quaternion to write + * @param epoch quaternion epoch (only for ADM V1) * @param timeConverter converter for dates */ - ApmQuaternionWriter(final String xmlTag, final String kvnTag, - final ApmQuaternion quaternion, final TimeConverter timeConverter) { + ApmQuaternionWriter(final double formatVersion, final String xmlTag, final String kvnTag, + final ApmQuaternion quaternion, + final AbsoluteDate epoch, final TimeConverter timeConverter) { super(xmlTag, kvnTag); + this.formatVersion = formatVersion; this.quaternion = quaternion; + this.epoch = epoch; this.timeConverter = timeConverter; } @@ -55,29 +70,53 @@ class ApmQuaternionWriter extends AbstractWriter { @Override protected void writeContent(final Generator generator) throws IOException { - generator.writeEntry(ApmQuaternionKey.EPOCH.name(), timeConverter, quaternion.getEpoch(), true); + generator.writeComments(quaternion.getComments()); + + if (epoch != null) { + // epoch is in the quaternion block only in ADM V1 + generator.writeEntry(ApmQuaternionKey.EPOCH.name(), timeConverter, epoch, false, true); + } // endpoints - generator.writeEntry(ApmQuaternionKey.Q_FRAME_A.name(), quaternion.getEndpoints().getFrameA().getName(), null, true); - generator.writeEntry(ApmQuaternionKey.Q_FRAME_B.name(), quaternion.getEndpoints().getFrameB().getName(), null, true); - generator.writeEntry(ApmQuaternionKey.Q_DIR.name(), - quaternion.getEndpoints().isA2b() ? AttitudeEndoints.A2B : AttitudeEndoints.B2A, - null, true); + if (formatVersion < 2.0) { + generator.writeEntry(ApmQuaternionKey.Q_FRAME_A.name(), quaternion.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(ApmQuaternionKey.Q_FRAME_B.name(), quaternion.getEndpoints().getFrameB().getName(), null, true); + generator.writeEntry(ApmQuaternionKey.Q_DIR.name(), + quaternion.getEndpoints().isA2b() ? AttitudeEndpoints.A2B : AttitudeEndpoints.B2A, + null, true); + } else { + generator.writeEntry(ApmQuaternionKey.REF_FRAME_A.name(), quaternion.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(ApmQuaternionKey.REF_FRAME_B.name(), quaternion.getEndpoints().getFrameB().getName(), null, true); + } // quaternion + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(ApmQuaternionKey.quaternion.name()); + } final Quaternion q = quaternion.getQuaternion(); generator.writeEntry(ApmQuaternionKey.Q1.name(), q.getQ1(), Unit.ONE, true); generator.writeEntry(ApmQuaternionKey.Q2.name(), q.getQ2(), Unit.ONE, true); generator.writeEntry(ApmQuaternionKey.Q3.name(), q.getQ3(), Unit.ONE, true); generator.writeEntry(ApmQuaternionKey.QC.name(), q.getQ0(), Unit.ONE, true); + if (generator.getFormat() == FileFormat.XML) { + generator.exitSection(); + } // quaternion derivative if (quaternion.hasRates()) { + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(formatVersion < 2.0 ? + ApmQuaternionKey.quaternionRate.name() : + ApmQuaternionKey.quaternionDot.name()); + } final Quaternion qDot = quaternion.getQuaternionDot(); generator.writeEntry(ApmQuaternionKey.Q1_DOT.name(), qDot.getQ1(), Unit.ONE, true); generator.writeEntry(ApmQuaternionKey.Q2_DOT.name(), qDot.getQ2(), Unit.ONE, true); generator.writeEntry(ApmQuaternionKey.Q3_DOT.name(), qDot.getQ3(), Unit.ONE, true); generator.writeEntry(ApmQuaternionKey.QC_DOT.name(), qDot.getQ0(), Unit.ONE, true); + if (generator.getFormat() == FileFormat.XML) { + generator.exitSection(); + } } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmWriter.java index 7d74307ab3..9015ed0c74 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ApmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,9 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.adm.AdmCommonMetadataWriter; +import org.orekit.files.ccsds.ndm.adm.AdmHeader; import org.orekit.files.ccsds.ndm.adm.AdmMetadata; -import org.orekit.files.ccsds.ndm.adm.AdmMetadataWriter; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.Segment; import org.orekit.files.ccsds.section.XmlStructureKey; import org.orekit.files.ccsds.utils.ContextBinding; @@ -40,7 +40,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class ApmWriter extends AbstractMessageWriter, Apm> { +public class ApmWriter extends AbstractMessageWriter, Apm> { /** Version number implemented. **/ public static final double CCSDS_APM_VERS = 1.0; @@ -70,8 +70,8 @@ public ApmWriter(final IERSConventions conventions, final DataContext dataContex /** {@inheritDoc} */ @Override - public void writeSegmentContent(final Generator generator, final double formatVersion, - final Segment segment) + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final Segment segment) throws IOException { // write the metadata @@ -85,7 +85,7 @@ public void writeSegmentContent(final Generator generator, final double formatVe metadata::getTimeSystem, oldContext::getClockCount, oldContext::getClockRate)); - new AdmMetadataWriter(metadata).write(generator); + new AdmCommonMetadataWriter(metadata).write(generator); // start data block if (generator.getFormat() == FileFormat.XML) { @@ -93,37 +93,75 @@ public void writeSegmentContent(final Generator generator, final double formatVe } generator.writeComments(segment.getData().getComments()); + if (formatVersion >= 2.0) { + // starting with version 2, epoch is outside of other blocks + generator.writeEntry("EPOCH", getTimeConverter(), segment.getData().getEpoch(), false, true); + } - // write mandatory quaternion block - new ApmQuaternionWriter(XmlSubStructureKey.quaternionState.name(), null, - segment.getData().getQuaternionBlock(), getTimeConverter()). - write(generator); + if (segment.getData().getQuaternionBlock() != null) { + // write quaternion block + final String xmlTag = ApmDataSubStructureKey.quaternionState.name(); + final String kvnTag = formatVersion < 2.0 ? null : ApmDataSubStructureKey.QUAT.name(); + new ApmQuaternionWriter(formatVersion, xmlTag, kvnTag, + segment.getData().getQuaternionBlock(), + formatVersion >= 2.0 ? null : segment.getData().getEpoch(), + getTimeConverter()). + write(generator); + } if (segment.getData().getEulerBlock() != null) { // write optional Euler block for three axis stabilized satellites - new EulerWriter(XmlSubStructureKey.eulerElementsThree.name(), null, + final String xmlTag = formatVersion < 2.0 ? + ApmDataSubStructureKey.eulerElementsThree.name() : + ApmDataSubStructureKey.eulerAngleState.name(); + final String kvnTag = formatVersion < 2.0 ? null : ApmDataSubStructureKey.EULER.name(); + new EulerWriter(formatVersion, xmlTag, kvnTag, segment.getData().getEulerBlock()). write(generator); } + if (segment.getData().getAngularVelocityBlock() != null) { + // write optional angular velocity block + final String xmlTag = ApmDataSubStructureKey.angularVelocity.name(); + final String kvnTag = ApmDataSubStructureKey.ANGVEL.name(); + new AngularVelocityWriter(xmlTag, kvnTag, + segment.getData().getAngularVelocityBlock()). + write(generator); + } + if (segment.getData().getSpinStabilizedBlock() != null) { - // write optional Euler block for spin stabilized satellites - new SpinStabilizedWriter(XmlSubStructureKey.eulerElementsSpin.name(), null, + // write optional block for spin stabilized satellites + final String xmlTag; + final String kvnTag; + if (formatVersion < 2.0) { + xmlTag = ApmDataSubStructureKey.eulerElementsSpin.name(); + kvnTag = null; + } else { + xmlTag = ApmDataSubStructureKey.spin.name(); + kvnTag = ApmDataSubStructureKey.SPIN.name(); + } + new SpinStabilizedWriter(formatVersion, xmlTag, kvnTag, segment.getData().getSpinStabilizedBlock()). write(generator); } - if (segment.getData().getSpacecraftParametersBlock() != null) { + if (segment.getData().getInertiaBlock() != null) { // write optional spacecraft parameters block - new SpacecraftParametersWriter(XmlSubStructureKey.spacecraftParameters.name(), null, - segment.getData().getSpacecraftParametersBlock()). + final String xmlTag = formatVersion < 2.0 ? + ApmDataSubStructureKey.spacecraftParameters.name() : + ApmDataSubStructureKey.inertia.name(); + final String kvnTag = formatVersion < 2.0 ? null : ApmDataSubStructureKey.INERTIA.name(); + new InertiaWriter(formatVersion, xmlTag, kvnTag, + segment.getData().getInertiaBlock()). write(generator); } if (!segment.getData().getManeuvers().isEmpty()) { for (final Maneuver maneuver : segment.getData().getManeuvers()) { // write optional maneuver block - new ManeuverWriter(XmlSubStructureKey.maneuverParameters.name(), null, + final String xmlTag = ApmDataSubStructureKey.maneuverParameters.name(); + final String kvnTag = formatVersion < 2.0 ? null : ApmDataSubStructureKey.MAN.name(); + new ManeuverWriter(formatVersion, xmlTag, kvnTag, maneuver, getTimeConverter()).write(generator); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Euler.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Euler.java index f062c6d91e..a173aa9f21 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Euler.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Euler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; import org.orekit.files.ccsds.section.CommentsContainer; /** @@ -31,8 +31,28 @@ */ public class Euler extends CommentsContainer { + /** Key for angles in ADM V1. + * @since 12.0 + */ + private static final String KEY_ANGLES_V1 = "{X|Y|Z}_ANGLE"; + + /** Key for angles in ADM V2. + * @since 12.0 + */ + private static final String KEY_ANGLES_V2 = "ANGLE_{1|2|3}"; + + /** Key for rates in ADM V1. + * @since 12.0 + */ + private static final String KEY_RATES_V1 = "{X|Y|Z}_RATE"; + + /** Key for rates in ADM V2. + * @since 12.0 + */ + private static final String KEY_RATES_V2 = "ANGLE_{1|2|3}_DOT"; + /** Endpoints (i.e. frames A, B and their relationship). */ - private final AttitudeEndoints endpoints; + private final AttitudeEndpoints endpoints; /** Rotation order of the Euler angles. */ private RotationOrder eulerRotSeq; @@ -52,7 +72,7 @@ public class Euler extends CommentsContainer { /** Simple constructor. */ public Euler() { - this.endpoints = new AttitudeEndoints(); + this.endpoints = new AttitudeEndpoints(); this.rotationAngles = new double[3]; this.rotationRates = new double[3]; this.inRotationAngles = false; @@ -65,36 +85,52 @@ public Euler() { public void validate(final double version) { super.validate(version); - endpoints.checkMandatoryEntriesExceptExternalFrame(EulerKey.EULER_FRAME_A, - EulerKey.EULER_FRAME_B, - EulerKey.EULER_DIR); - endpoints.checkExternalFrame(EulerKey.EULER_FRAME_A, EulerKey.EULER_FRAME_B); - checkNotNull(eulerRotSeq, EulerKey.EULER_ROT_SEQ); - - final boolean missingAngle = Double.isNaN(rotationAngles[0] + rotationAngles[1] + rotationAngles[2]); - if (missingAngle) { - // if at least one is NaN, all must be NaN (i.e. not initialized) + if (version < 2.0) { + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + EulerKey.EULER_FRAME_A, + EulerKey.EULER_FRAME_B, + EulerKey.EULER_DIR); + endpoints.checkExternalFrame(EulerKey.EULER_FRAME_A, EulerKey.EULER_FRAME_B); + } else { + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + EulerKey.REF_FRAME_A, + EulerKey.REF_FRAME_B, + EulerKey.EULER_DIR); + endpoints.checkExternalFrame(EulerKey.REF_FRAME_A, EulerKey.REF_FRAME_B); + } + checkNotNull(eulerRotSeq, EulerKey.EULER_ROT_SEQ.name()); + + if (!hasAngles()) { + // if at least one angle is missing, all must be NaN (i.e. not initialized) for (final double ra : rotationAngles) { if (!Double.isNaN(ra)) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}_ANGLE"); + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, + version < 2.0 ? KEY_ANGLES_V1 : KEY_ANGLES_V2); } } } - final boolean missingRate = Double.isNaN(rotationRates[0] + rotationRates[1] + rotationRates[2]); - if (missingRate) { - // if at least one is NaN, all must be NaN (i.e. not initialized) + if (!hasRates()) { + // if at least one rate is missing, all must be NaN (i.e. not initialized) for (final double rr : rotationRates) { if (!Double.isNaN(rr)) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}_RATE"); + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, + version < 2.0 ? KEY_RATES_V1 : KEY_RATES_V2); } } } - // either angles or rates must be specified - // (angles may be missing in the quaternion/Euler rate case) - if (missingAngle && missingRate) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}_{ANGLE|RATE}"); + if (version < 2.0) { + // in ADM V1, either angles or rates must be specified + // (angles may be missing in the quaternion/Euler rate case) + if (!hasAngles() && !hasRates()) { + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, KEY_ANGLES_V1 + "/" + KEY_RATES_V1); + } + } else { + // in ADM V2, angles are mandatory + if (!hasAngles()) { + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, KEY_ANGLES_V2); + } } } @@ -102,7 +138,7 @@ public void validate(final double version) { /** Get the endpoints (i.e. frames A, B and their relationship). * @return endpoints */ - public AttitudeEndoints getEndpoints() { + public AttitudeEndpoints getEndpoints() { return endpoints; } @@ -123,15 +159,15 @@ public void setEulerRotSeq(final RotationOrder eulerRotSeq) { this.eulerRotSeq = eulerRotSeq; } - /** Check if rates are specified in {@link AttitudeEndoints#getFrameA() frame A}. - * @return true if rates are specified in {@link AttitudeEndoints#getFrameA() frame A} + /** Check if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}. + * @return true if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A} */ public boolean rateFrameIsA() { return rateFrameIsA == null ? false : rateFrameIsA; } /** Set the frame in which rates are specified. - * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndoints#getFrameA() frame A} + * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndpoints#getFrameA() frame A} */ public void setRateFrameIsA(final boolean rateFrameIsA) { refuseFurtherComments(); @@ -147,43 +183,77 @@ public void setRateFrameIsA(final boolean rateFrameIsA) { * @return true if rates are specified in spacecraft body frame */ public boolean isSpacecraftBodyRate() { - return rateFrameIsA ^ endpoints.getFrameA().asSpacecraftBodyFrame() == null; + return rateFrameIsA() ^ endpoints.getFrameA().asSpacecraftBodyFrame() == null; } /** - * Get the coordinates of the Euler angles (rad). - * @return rotation angles + * Get the coordinates of the Euler angles. + * @return rotation angles (rad) */ public double[] getRotationAngles() { return rotationAngles.clone(); } /** - * Set the Euler angle about (rad). + * Set the Euler angle about axis. + * @param axis rotation axis + * @param angle angle to set (rad) + */ + public void setLabeledRotationAngle(final char axis, final double angle) { + if (eulerRotSeq != null) { + for (int i = 0; i < rotationAngles.length; ++i) { + if (eulerRotSeq.name().charAt(i) == axis && Double.isNaN(rotationAngles[i])) { + setIndexedRotationAngle(i, angle); + return; + } + } + } + } + + /** + * Set the Euler angle about axis. * @param axis rotation axis - * @param angle angle to set + * @param angle angle to set (rad) + * @since 12.0 */ - public void setRotationAngle(final char axis, final double angle) { + public void setIndexedRotationAngle(final int axis, final double angle) { refuseFurtherComments(); - setAngleOrRate(rotationAngles, axis, angle); + rotationAngles[axis] = angle; } /** - * Get the rates of the Euler angles (rad/s). - * @return rotation rates + * Get the rates of the Euler angles. + * @return rotation rates (rad/s) */ public double[] getRotationRates() { return rotationRates.clone(); } /** - * Set the rate of Euler angle (rad/s). + * Set the rate of Euler angle about axis. * @param axis rotation axis - * @param rate angle rate to set + * @param rate angle rate to set (rad/s) */ - public void setRotationRate(final char axis, final double rate) { + public void setLabeledRotationRate(final char axis, final double rate) { + if (eulerRotSeq != null) { + for (int i = 0; i < rotationRates.length; ++i) { + if (eulerRotSeq.name().charAt(i) == axis && Double.isNaN(rotationRates[i])) { + setIndexedRotationRate(i, rate); + return; + } + } + } + } + + /** + * Set the rate of Euler angle about axis. + * @param axis rotation axis + * @param rate angle rate to set (rad/s) + * @since 12.0 + */ + public void setIndexedRotationRate(final int axis, final double rate) { refuseFurtherComments(); - setAngleOrRate(rotationRates, axis, rate); + rotationRates[axis] = rate; } /** Check if we are in the rotationAngles part of XML files. @@ -201,6 +271,17 @@ public void setInRotationAngles(final boolean inRotationAngles) { this.inRotationAngles = inRotationAngles; } + /** Check if the logical block includes angles. + *

          + * This can be false only for ADM V1, as angles are mandatory since ADM V2. + *

          + * @return true if logical block includes angles + * @since 12.0 + */ + public boolean hasAngles() { + return !Double.isNaN(rotationAngles[0] + rotationAngles[1] + rotationAngles[2]); + } + /** Check if the logical block includes rates. * @return true if logical block includes rates */ @@ -208,21 +289,4 @@ public boolean hasRates() { return !Double.isNaN(rotationRates[0] + rotationRates[1] + rotationRates[2]); } - /** Set an angle or rate in an array. - * @param array angle or rate array - * @param axis axis name - * @param value angle or rate to set - */ - private void setAngleOrRate(final double[] array, final char axis, final double value) { - refuseFurtherComments(); - if (eulerRotSeq != null) { - if (eulerRotSeq.name().charAt(0) == axis && Double.isNaN(array[0])) { - array[0] = value; - } else if (eulerRotSeq.name().charAt(1) == axis && Double.isNaN(array[1])) { - array[1] = value; - } else if (eulerRotSeq.name().charAt(2) == axis && Double.isNaN(array[2])) { - array[2] = value; - } - } - } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerKey.java index e825c0ac51..59fd584aeb 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,6 @@ package org.orekit.files.ccsds.ndm.adm.apm; import org.orekit.files.ccsds.definitions.Units; -import org.orekit.files.ccsds.ndm.adm.AdmParser; import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.lexical.TokenType; @@ -29,23 +28,41 @@ */ public enum EulerKey { - /** Rotation angles wrapping element in XML files. */ + /** Rotation angles wrapping element in XML files (ADM V1 only). */ rotationAngles((token, context, container) -> true), - /** Rotation rates wrapping element in XML files. */ + /** Rotation rates wrapping element in XML files (ADM V1 only). */ rotationRates((token, context, container) -> true), /** Comment entry. */ COMMENT((token, context, container) -> token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), - /** First reference frame entry. */ + /** First reference frame entry (only for ADM V1). */ EULER_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), - /** Second reference frame entry. */ + /** First reference frame entry. + * @since 12.0 + */ + REF_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), + + /** Second reference frame entry (only for ADM V1). */ EULER_FRAME_B((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { - container.checkNotNull(container.getEndpoints().getFrameA(), EULER_FRAME_A); + container.checkNotNull(container.getEndpoints().getFrameA(), EULER_FRAME_A.name()); + final boolean aIsSpaceraftBody = container.getEndpoints().getFrameA().asSpacecraftBodyFrame() != null; + return token.processAsFrame(container.getEndpoints()::setFrameB, context, + aIsSpaceraftBody, aIsSpaceraftBody, !aIsSpaceraftBody); + } + return true; + }), + + /** Second reference frame entry. + * @since 12.0 + */ + REF_FRAME_B((token, context, container) -> { + if (token.getType() == TokenType.ENTRY) { + container.checkNotNull(container.getEndpoints().getFrameA(), EULER_FRAME_A.name()); final boolean aIsSpaceraftBody = container.getEndpoints().getFrameA().asSpacecraftBodyFrame() != null; return token.processAsFrame(container.getEndpoints()::setFrameB, context, aIsSpaceraftBody, aIsSpaceraftBody, !aIsSpaceraftBody); @@ -53,7 +70,7 @@ public enum EulerKey { return true; }), - /** Rotation direction entry. */ + /** Rotation direction entry (ADM V1 only). */ EULER_DIR((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { container.getEndpoints().setA2b(token.getContentAsUppercaseCharacter() == 'A'); @@ -62,9 +79,9 @@ public enum EulerKey { }), /** Rotation sequence entry. */ - EULER_ROT_SEQ((token, context, container) -> AdmParser.processRotationOrder(token, container::setEulerRotSeq)), + EULER_ROT_SEQ((token, context, container) -> token.processAsRotationOrder(container::setEulerRotSeq)), - /** Reference frame for rate entry. */ + /** Reference frame for rate entry (ADM V1 only). */ RATE_FRAME((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { final String content = token.getContentAsUppercaseString(); @@ -74,29 +91,65 @@ public enum EulerKey { return true; }), - /** X body rotation angle entry. */ + /** X body rotation angle entry (ADM V1 only). */ X_ANGLE((token, context, container) -> token.processAsLabeledDouble('X', Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setRotationAngle)), + container::setLabeledRotationAngle)), - /** Y body rotation angle entry. */ + /** Y body rotation angle entry (ADM V1 only). */ Y_ANGLE((token, context, container) -> token.processAsLabeledDouble('Y', Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setRotationAngle)), + container::setLabeledRotationAngle)), - /** Z body rotation angle entry. */ + /** Z body rotation angle entry (ADM V1 only). */ Z_ANGLE((token, context, container) -> token.processAsLabeledDouble('Z', Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setRotationAngle)), + container::setLabeledRotationAngle)), - /** X body rotation rate entry. */ + /** X body rotation rate entry (ADM V1 only). */ X_RATE((token, context, container) -> token.processAsLabeledDouble('X', Units.DEG_PER_S, context.getParsedUnitsBehavior(), - container::setRotationRate)), + container::setLabeledRotationRate)), - /** Y body rotation rate entry. */ + /** Y body rotation rate entry (ADM V1 only). */ Y_RATE((token, context, container) -> token.processAsLabeledDouble('Y', Units.DEG_PER_S, context.getParsedUnitsBehavior(), - container::setRotationRate)), + container::setLabeledRotationRate)), - /** Z body rotation rate entry. */ + /** Z body rotation rate entry (ADM V1 only). */ Z_RATE((token, context, container) -> token.processAsLabeledDouble('Z', Units.DEG_PER_S, context.getParsedUnitsBehavior(), - container::setRotationRate)); + container::setLabeledRotationRate)), + + /** First body rotation angle entry. + * @since 12.0 + */ + ANGLE_1((token, context, container) -> token.processAsIndexedDouble(0, Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setIndexedRotationAngle)), + + /** Second body rotation angle entry. + * @since 12.0 + */ + ANGLE_2((token, context, container) -> token.processAsIndexedDouble(1, Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setIndexedRotationAngle)), + + /** Third body rotation angle entry. + * @since 12.0 + */ + ANGLE_3((token, context, container) -> token.processAsIndexedDouble(2, Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setIndexedRotationAngle)), + + /** First body rotation rate entry. + * @since 12.0 + */ + ANGLE_1_DOT((token, context, container) -> token.processAsIndexedDouble(0, Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setIndexedRotationRate)), + + /** Second body rotation rate entry. + * @since 12.0 + */ + ANGLE_2_DOT((token, context, container) -> token.processAsIndexedDouble(1, Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setIndexedRotationRate)), + + /** Third body rotation rate entry. + * @since 12.0 + */ + ANGLE_3_DOT((token, context, container) -> token.processAsIndexedDouble(2, Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setIndexedRotationRate)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerWriter.java index 7f12b7e797..cacc7e00a6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/EulerWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.io.IOException; import org.orekit.files.ccsds.definitions.Units; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; import org.orekit.files.ccsds.section.AbstractWriter; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.utils.units.Unit; @@ -37,18 +37,25 @@ class EulerWriter extends AbstractWriter { /** Suffix for rates. */ private static String RATE = "_RATE"; + /** Format version. + * @since 12.0 + */ + private final double formatVersion; + /** Euler block. */ private final Euler euler; /** Create a writer. + * @param formatVersion format version * @param xmlTag name of the XML tag surrounding the section * @param kvnTag name of the KVN tag surrounding the section (may be null) * @param euler Euler data to write */ - EulerWriter(final String xmlTag, final String kvnTag, - final Euler euler) { + EulerWriter(final double formatVersion, final String xmlTag, final String kvnTag, + final Euler euler) { super(xmlTag, kvnTag); - this.euler = euler; + this.formatVersion = formatVersion; + this.euler = euler; } /** {@inheritDoc} */ @@ -58,35 +65,56 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeComments(euler.getComments()); // endpoints - generator.writeEntry(EulerKey.EULER_FRAME_A.name(), euler.getEndpoints().getFrameA().getName(), null, true); - generator.writeEntry(EulerKey.EULER_FRAME_B.name(), euler.getEndpoints().getFrameB().getName(), null, true); - generator.writeEntry(EulerKey.EULER_DIR.name(), - euler.getEndpoints().isA2b() ? AttitudeEndoints.A2B : AttitudeEndoints.B2A, - null, true); + if (formatVersion < 2.0) { + generator.writeEntry(EulerKey.EULER_FRAME_A.name(), euler.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(EulerKey.EULER_FRAME_B.name(), euler.getEndpoints().getFrameB().getName(), null, true); + generator.writeEntry(EulerKey.EULER_DIR.name(), + euler.getEndpoints().isA2b() ? AttitudeEndpoints.A2B : AttitudeEndpoints.B2A, + null, true); + } else { + generator.writeEntry(EulerKey.REF_FRAME_A.name(), euler.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(EulerKey.REF_FRAME_B.name(), euler.getEndpoints().getFrameB().getName(), null, true); + } // angles final String seq = euler.getEulerRotSeq().name(); final double[] angles = euler.getRotationAngles(); - generator.writeEntry(EulerKey.EULER_ROT_SEQ.name(), - seq.replace('X', '1').replace('Y', '2').replace('Z', '3'), - null, true); - generator.writeEntry(EulerKey.RATE_FRAME.name(), - euler.rateFrameIsA() ? EulerKey.EULER_FRAME_A.name() : EulerKey.EULER_FRAME_B.name(), - null, true); + if (formatVersion < 2.0) { + generator.writeEntry(EulerKey.EULER_ROT_SEQ.name(), + seq.replace('X', '1').replace('Y', '2').replace('Z', '3'), + null, true); + generator.writeEntry(EulerKey.RATE_FRAME.name(), + euler.rateFrameIsA() ? EulerKey.EULER_FRAME_A.name() : EulerKey.EULER_FRAME_B.name(), + null, euler.hasRates()); + } else { + generator.writeEntry(EulerKey.EULER_ROT_SEQ.name(), seq, null, true); + } // if we don't have rates, at least we need angles // (we may have only rates, as orientation is already given by mandatory quaternion) final boolean needsAngles = !euler.hasRates(); - generator.writeEntry(seq.charAt(0) + ANGLE, angles[0], Unit.DEGREE, needsAngles); - generator.writeEntry(seq.charAt(1) + ANGLE, angles[1], Unit.DEGREE, needsAngles); - generator.writeEntry(seq.charAt(2) + ANGLE, angles[2], Unit.DEGREE, needsAngles); + if (formatVersion < 2.0) { + generator.writeEntry(seq.charAt(0) + ANGLE, angles[0], Unit.DEGREE, needsAngles); + generator.writeEntry(seq.charAt(1) + ANGLE, angles[1], Unit.DEGREE, needsAngles); + generator.writeEntry(seq.charAt(2) + ANGLE, angles[2], Unit.DEGREE, needsAngles); + } else { + generator.writeEntry(EulerKey.ANGLE_1.name(), angles[0], Unit.DEGREE, needsAngles); + generator.writeEntry(EulerKey.ANGLE_2.name(), angles[1], Unit.DEGREE, needsAngles); + generator.writeEntry(EulerKey.ANGLE_3.name(), angles[2], Unit.DEGREE, needsAngles); + } // rates if (euler.hasRates()) { final double[] rates = euler.getRotationRates(); - generator.writeEntry(seq.charAt(0) + RATE, rates[0], Units.DEG_PER_S, true); - generator.writeEntry(seq.charAt(1) + RATE, rates[1], Units.DEG_PER_S, true); - generator.writeEntry(seq.charAt(2) + RATE, rates[2], Units.DEG_PER_S, true); + if (formatVersion < 2.0) { + generator.writeEntry(seq.charAt(0) + RATE, rates[0], Units.DEG_PER_S, true); + generator.writeEntry(seq.charAt(1) + RATE, rates[1], Units.DEG_PER_S, true); + generator.writeEntry(seq.charAt(2) + RATE, rates[2], Units.DEG_PER_S, true); + } else { + generator.writeEntry(EulerKey.ANGLE_1_DOT.name(), rates[0], Units.DEG_PER_S, true); + generator.writeEntry(EulerKey.ANGLE_2_DOT.name(), rates[1], Units.DEG_PER_S, true); + generator.writeEntry(EulerKey.ANGLE_3_DOT.name(), rates[2], Units.DEG_PER_S, true); + } } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Inertia.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Inertia.java new file mode 100644 index 0000000000..81b76cf1cc --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Inertia.java @@ -0,0 +1,97 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.apm; + +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.ndm.CommonPhysicalProperties; + +/** Inertia. + * @author Luc Maisonobe + * @since 12.0 + */ +public class Inertia extends CommonPhysicalProperties { + + /** Inertia reference frame. */ + private FrameFacade frame; + + /** Inertia matrix. */ + private RealMatrix inertiaMatrix; + + /** Simple constructor. + */ + public Inertia() { + inertiaMatrix = MatrixUtils.createRealMatrix(new double[][] { + { Double.NaN, Double.NaN, Double.NaN }, + { Double.NaN, Double.NaN, Double.NaN }, + { Double.NaN, Double.NaN, Double.NaN } + }); + } + + /** {@inheritDoc} */ + @Override + public void validate(final double version) { + super.validate(version); + if (version >= 2.0) { + checkNotNull(frame, InertiaKey.INERTIA_REF_FRAME.name()); + } + checkNotNaN(inertiaMatrix.getEntry(0, 0), InertiaKey.IXX.name()); + checkNotNaN(inertiaMatrix.getEntry(1, 1), InertiaKey.IYY.name()); + checkNotNaN(inertiaMatrix.getEntry(2, 2), InertiaKey.IZZ.name()); + checkNotNaN(inertiaMatrix.getEntry(0, 1), InertiaKey.IXY.name()); + checkNotNaN(inertiaMatrix.getEntry(0, 2), InertiaKey.IXZ.name()); + checkNotNaN(inertiaMatrix.getEntry(1, 2), InertiaKey.IYZ.name()); + } + + /** Set frame in which inertia is specified. + * @param frame frame in which inertia is specified + */ + public void setFrame(final FrameFacade frame) { + this.frame = frame; + } + + /** Get frame in which inertia is specified. + * @return frame in which inertia is specified + */ + public FrameFacade getFrame() { + return frame; + } + + /** Get the inertia matrix. + * @return the inertia matrix + */ + public RealMatrix getInertiaMatrix() { + return inertiaMatrix; + } + + /** Set an entry in the inertia matrix. + *

          + * Both I(j, k) and I(k, j) are set. + *

          + * @param j row index (must be between 0 and 3 (inclusive) + * @param k column index (must be between 0 and 3 (inclusive) + * @param entry value of the matrix entry + */ + public void setInertiaMatrixEntry(final int j, final int k, final double entry) { + refuseFurtherComments(); + inertiaMatrix.setEntry(j, k, entry); + inertiaMatrix.setEntry(k, j, entry); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/InertiaKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/InertiaKey.java new file mode 100644 index 0000000000..ff3012e413 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/InertiaKey.java @@ -0,0 +1,117 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.adm.apm; + +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.utils.ContextBinding; +import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; + + +/** Keys for {@link Inertia inertia} entries. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum InertiaKey { + + /** Comment entry. */ + COMMENT((token, context, container) -> + token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), + + /** Frame in which inertia is defined. */ + INERTIA_REF_FRAME((token, context, container) -> token.processAsFrame(container::setFrame, context, false, false, true)), + + /** Moment of inertia about X-axis (ADM V1 only). */ + I11((token, context, container) -> token.processAsDoublyIndexedDouble(0, 0, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about X-axis. */ + IXX((token, context, container) -> token.processAsDoublyIndexedDouble(0, 0, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about Y-axis (ADM V1 only). */ + I22((token, context, container) -> token.processAsDoublyIndexedDouble(1, 1, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about Y-axis. */ + IYY((token, context, container) -> token.processAsDoublyIndexedDouble(1, 1, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about Z-axis (ADM V1 only). */ + I33((token, context, container) -> token.processAsDoublyIndexedDouble(2, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Moment of inertia about Z-axis. */ + IZZ((token, context, container) -> token.processAsDoublyIndexedDouble(2, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the X and Y axes (ADM V1 only). */ + I12((token, context, container) -> token.processAsDoublyIndexedDouble(0, 1, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the X and Y axes. */ + IXY((token, context, container) -> token.processAsDoublyIndexedDouble(0, 1, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the X and Z axes (ADM V1 only). */ + I13((token, context, container) -> token.processAsDoublyIndexedDouble(0, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the X and Z axes. */ + IXZ((token, context, container) -> token.processAsDoublyIndexedDouble(0, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the Y and Z axes (ADM V1 only). */ + I23((token, context, container) -> token.processAsDoublyIndexedDouble(1, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)), + + /** Inertia cross product of the Y and Z axes. */ + IYZ((token, context, container) -> token.processAsDoublyIndexedDouble(1, 2, Units.KG_M2, context.getParsedUnitsBehavior(), + container::setInertiaMatrixEntry)); + + /** Processing method. */ + private final TokenProcessor processor; + + /** Simple constructor. + * @param processor processing method + */ + InertiaKey(final TokenProcessor processor) { + this.processor = processor; + } + + /** Process an token. + * @param token token to process + * @param context context binding + * @param data data to fill + * @return true of token was accepted + */ + public boolean process(final ParseToken token, final ContextBinding context, final Inertia data) { + return processor.process(token, context, data); + } + + /** Interface for processing one token. */ + interface TokenProcessor { + /** Process one token. + * @param token token to process + * @param context context binding + * @param data data to fill + * @return true of token was accepted + */ + boolean process(ParseToken token, ContextBinding context, Inertia data); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/InertiaWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/InertiaWriter.java new file mode 100644 index 0000000000..b2ff16d55d --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/InertiaWriter.java @@ -0,0 +1,85 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.adm.apm; + +import java.io.IOException; + +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.generation.Generator; + +/** Writer for inertia data. + * @author Luc Maisonobe + * @since 11.0 + */ +class InertiaWriter extends AbstractWriter { + + /** Format version. + * @since 12.0 + */ + private final double formatVersion; + + /** Inertia block. */ + private final Inertia inertia; + + /** Create a writer. + * @param formatVersion format version + * @param xmlTag name of the XML tag surrounding the section + * @param kvnTag name of the KVN tag surrounding the section (may be null) + * @param spacecraftParameters spacecraft parameters data to write + */ + InertiaWriter(final double formatVersion, final String xmlTag, final String kvnTag, + final Inertia spacecraftParameters) { + super(xmlTag, kvnTag); + this.formatVersion = formatVersion; + this.inertia = spacecraftParameters; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + generator.writeComments(inertia.getComments()); + + // frame + if (inertia.getFrame() != null) { + generator.writeEntry(InertiaKey.INERTIA_REF_FRAME.name(), + inertia.getFrame().getName(), + null, false); + } + + // inertia matrix + if (formatVersion < 2.0) { + generator.writeEntry(InertiaKey.I11.name(), inertia.getInertiaMatrix().getEntry(0, 0), Units.KG_M2, true); + generator.writeEntry(InertiaKey.I22.name(), inertia.getInertiaMatrix().getEntry(1, 1), Units.KG_M2, true); + generator.writeEntry(InertiaKey.I33.name(), inertia.getInertiaMatrix().getEntry(2, 2), Units.KG_M2, true); + generator.writeEntry(InertiaKey.I12.name(), inertia.getInertiaMatrix().getEntry(0, 1), Units.KG_M2, true); + generator.writeEntry(InertiaKey.I13.name(), inertia.getInertiaMatrix().getEntry(0, 2), Units.KG_M2, true); + generator.writeEntry(InertiaKey.I23.name(), inertia.getInertiaMatrix().getEntry(1, 2), Units.KG_M2, true); + } else { + generator.writeEntry(InertiaKey.IXX.name(), inertia.getInertiaMatrix().getEntry(0, 0), Units.KG_M2, true); + generator.writeEntry(InertiaKey.IYY.name(), inertia.getInertiaMatrix().getEntry(1, 1), Units.KG_M2, true); + generator.writeEntry(InertiaKey.IZZ.name(), inertia.getInertiaMatrix().getEntry(2, 2), Units.KG_M2, true); + generator.writeEntry(InertiaKey.IXY.name(), inertia.getInertiaMatrix().getEntry(0, 1), Units.KG_M2, true); + generator.writeEntry(InertiaKey.IXZ.name(), inertia.getInertiaMatrix().getEntry(0, 2), Units.KG_M2, true); + generator.writeEntry(InertiaKey.IYZ.name(), inertia.getInertiaMatrix().getEntry(1, 2), Units.KG_M2, true); + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Maneuver.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Maneuver.java index d2093238bc..1e2f1e902c 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Maneuver.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/Maneuver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.util.Arrays; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.files.ccsds.definitions.FrameFacade; import org.orekit.files.ccsds.section.CommentsContainer; import org.orekit.time.AbsoluteDate; @@ -32,8 +33,8 @@ public class Maneuver extends CommentsContainer { /** Epoch of start of maneuver . */ private AbsoluteDate epochStart; - /** Coordinate system for the torque vector, for absolute frames. */ - private String refFrameString; + /** Coordinate system for the torque vector. */ + private FrameFacade frame; /** Duration (value is 0 for impulsive maneuver). */ private double duration; @@ -41,12 +42,18 @@ public class Maneuver extends CommentsContainer { /** Torque vector (N.m). */ private double[] torque; + /** Mass change during maneuver (kg). + * @since 12.0 + */ + private double deltaMass; + /** * Simple constructor. */ public Maneuver() { - duration = Double.NaN; - torque = new double[3]; + duration = Double.NaN; + torque = new double[3]; + deltaMass = Double.NaN; Arrays.fill(torque, Double.NaN); } @@ -54,12 +61,12 @@ public Maneuver() { @Override public void validate(final double version) { super.validate(version); - checkNotNull(epochStart, ManeuverKey.MAN_EPOCH_START); - checkNotNaN(duration, ManeuverKey.MAN_DURATION); - checkNotNull(refFrameString, ManeuverKey.MAN_REF_FRAME); - checkNotNaN(torque[0], ManeuverKey.MAN_TOR_1); - checkNotNaN(torque[1], ManeuverKey.MAN_TOR_2); - checkNotNaN(torque[2], ManeuverKey.MAN_TOR_3); + checkNotNull(epochStart, ManeuverKey.MAN_EPOCH_START.name()); + checkNotNaN(duration, ManeuverKey.MAN_DURATION.name()); + checkNotNull(frame, ManeuverKey.MAN_REF_FRAME.name()); + checkNotNaN(torque[0], ManeuverKey.MAN_TOR_1.name()); + checkNotNaN(torque[1], ManeuverKey.MAN_TOR_2.name()); + checkNotNaN(torque[2], ManeuverKey.MAN_TOR_3.name()); } /** @@ -80,20 +87,20 @@ public void setEpochStart(final AbsoluteDate epochStart) { } /** - * Get Coordinate system for the torque vector, for absolute frames. - * @return coordinate system for the torque vector, for absolute frames + * Get Coordinate system for the torque vector. + * @return coordinate system for the torque vector */ - public String getRefFrameString() { - return refFrameString; + public FrameFacade getFrame() { + return frame; } /** - * Set Coordinate system for the torque vector, for absolute frames. - * @param refFrameString coordinate system for the torque vector, for absolute frames + * Set Coordinate system for the torque vector. + * @param frame coordinate system for the torque vector */ - public void setRefFrameString(final String refFrameString) { + public void setFrame(final FrameFacade frame) { refuseFurtherComments(); - this.refFrameString = refFrameString; + this.frame = frame; } /** @@ -113,15 +120,6 @@ public void setDuration(final double duration) { this.duration = duration; } - /** Check if maneuver has been completed. - * @return true if maneuver has been completed - */ - public boolean completed() { - return !(epochStart == null || - refFrameString == null || - Double.isNaN(duration + torque[0] + torque[1] + torque[2])); - } - /** * Get the torque vector (N.m). * @return torque vector @@ -140,4 +138,23 @@ public void setTorque(final int index, final double value) { this.torque[index] = value; } + /** + * Get mass change during maneuver. + * @return mass change during maneuver (kg, negative) + * @since 12.0 + */ + public double getDeltaMass() { + return deltaMass; + } + + /** + * Set mass change during maneuver. + * @param deltaMass mass change during maneuver (kg) + * @since 12.0 + */ + public void setDeltaMass(final double deltaMass) { + refuseFurtherComments(); + this.deltaMass = deltaMass; + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverKey.java index f629d67fa1..775e3be20b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -41,19 +41,43 @@ public enum ManeuverKey { container::setDuration)), /** Reference frame entry. */ - MAN_REF_FRAME((token, context, container) -> token.processAsUppercaseString(container::setRefFrameString)), + MAN_REF_FRAME((token, context, container) -> token.processAsFrame(container::setFrame, context, true, true, true)), - /** First torque vector component entry. */ + /** First torque vector component entry (ADM V1 only). */ MAN_TOR_1((token, context, container) -> token.processAsIndexedDouble(0, Units.N_M, context.getParsedUnitsBehavior(), container::setTorque)), - /** Second torque vector component entry. */ + /** First torque vector component entry. + * @since 12.0 + */ + MAN_TOR_X((token, context, container) -> token.processAsIndexedDouble(0, Units.N_M, context.getParsedUnitsBehavior(), + container::setTorque)), + + /** Second torque vector component entry (ADM V1 only). */ MAN_TOR_2((token, context, container) -> token.processAsIndexedDouble(1, Units.N_M, context.getParsedUnitsBehavior(), container::setTorque)), - /** Third torque vector component entry. */ + /** Second torque vector component entry. + * @since 12.0 + */ + MAN_TOR_Y((token, context, container) -> token.processAsIndexedDouble(1, Units.N_M, context.getParsedUnitsBehavior(), + container::setTorque)), + + /** Third torque vector component entry (ADM V1 only). */ MAN_TOR_3((token, context, container) -> token.processAsIndexedDouble(2, Units.N_M, context.getParsedUnitsBehavior(), - container::setTorque)); + container::setTorque)), + + /** Third torque vector component entry. + * @since 12.0 + */ + MAN_TOR_Z((token, context, container) -> token.processAsIndexedDouble(2, Units.N_M, context.getParsedUnitsBehavior(), + container::setTorque)), + + /** Mass change entry. + * @since 12.0 + */ + MAN_DELTA_MASS((token, context, container) -> token.processAsDouble(Unit.KILOGRAM, context.getParsedUnitsBehavior(), + container::setDeltaMass)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverWriter.java index e4f6d340c7..78e90a5a6b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/ManeuverWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -32,6 +32,11 @@ */ class ManeuverWriter extends AbstractWriter { + /** Format version. + * @since 12.0 + */ + private final double formatVersion; + /** Maneuver block. */ private final Maneuver maneuver; @@ -39,14 +44,16 @@ class ManeuverWriter extends AbstractWriter { private final TimeConverter timeConverter; /** Create a writer. + * @param formatVersion format version * @param xmlTag name of the XML tag surrounding the section * @param kvnTag name of the KVN tag surrounding the section (may be null) * @param maneuver maneuver data to write * @param timeConverter converter for dates */ - ManeuverWriter(final String xmlTag, final String kvnTag, + ManeuverWriter(final double formatVersion, final String xmlTag, final String kvnTag, final Maneuver maneuver, final TimeConverter timeConverter) { super(xmlTag, kvnTag); + this.formatVersion = formatVersion; this.maneuver = maneuver; this.timeConverter = timeConverter; } @@ -58,17 +65,24 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeComments(maneuver.getComments()); // time - generator.writeEntry(ManeuverKey.MAN_EPOCH_START.name(), timeConverter, maneuver.getEpochStart(), true); + generator.writeEntry(ManeuverKey.MAN_EPOCH_START.name(), timeConverter, maneuver.getEpochStart(), true, true); generator.writeEntry(ManeuverKey.MAN_DURATION.name(), maneuver.getDuration(), Unit.SECOND, true); // frame - generator.writeEntry(ManeuverKey.MAN_REF_FRAME.name(), maneuver.getRefFrameString(), null, false); + generator.writeEntry(ManeuverKey.MAN_REF_FRAME.name(), maneuver.getFrame().getName(), null, false); // torque final Vector3D torque = maneuver.getTorque(); - generator.writeEntry(ManeuverKey.MAN_TOR_1.name(), torque.getX(), Units.N_M, true); - generator.writeEntry(ManeuverKey.MAN_TOR_2.name(), torque.getY(), Units.N_M, true); - generator.writeEntry(ManeuverKey.MAN_TOR_3.name(), torque.getZ(), Units.N_M, true); + if (formatVersion < 2.0) { + generator.writeEntry(ManeuverKey.MAN_TOR_1.name(), torque.getX(), Units.N_M, true); + generator.writeEntry(ManeuverKey.MAN_TOR_2.name(), torque.getY(), Units.N_M, true); + generator.writeEntry(ManeuverKey.MAN_TOR_3.name(), torque.getZ(), Units.N_M, true); + } else { + generator.writeEntry(ManeuverKey.MAN_TOR_X.name(), torque.getX(), Units.N_M, true); + generator.writeEntry(ManeuverKey.MAN_TOR_Y.name(), torque.getY(), Units.N_M, true); + generator.writeEntry(ManeuverKey.MAN_TOR_Z.name(), torque.getZ(), Units.N_M, true); + generator.writeEntry(ManeuverKey.MAN_DELTA_MASS.name(), maneuver.getDeltaMass(), Unit.KILOGRAM, true); + } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParameters.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParameters.java deleted file mode 100644 index 0493fc34d0..0000000000 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParameters.java +++ /dev/null @@ -1,193 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.files.ccsds.ndm.adm.apm; - -import org.orekit.files.ccsds.definitions.FrameFacade; -import org.orekit.files.ccsds.section.CommentsContainer; - -/** - * Container for spacecraft parameters data. - * @author Bryan Cazabonne - * @since 10.2 - */ -public class SpacecraftParameters extends CommentsContainer { - - /** Coordinate system for the inertia tensor. */ - private FrameFacade inertiaReferenceFrame; - - /** Moment of Inertia about the 1-axis (kg.m²). */ - private double i11; - - /** Moment of Inertia about the 2-axis (kg.m²). */ - private double i22; - - /** Moment of Inertia about the 3-axis (kg.m²). */ - private double i33; - - /** Inertia Cross Product of the 1 and 2 axes (kg.m²). */ - private double i12; - - /** Inertia Cross Product of the 1 and 3 axes (kg.m²). */ - private double i13; - - /** Inertia Cross Product of the 2 and 3 axes (kg.m²). */ - private double i23; - - /** Simple constructor. - */ - public SpacecraftParameters() { - inertiaReferenceFrame = null; - i11 = Double.NaN; - i22 = Double.NaN; - i33 = Double.NaN; - i12 = Double.NaN; - i13 = Double.NaN; - i23 = Double.NaN; - } - - /** {@inheritDoc} */ - @Override - public void validate(final double version) { - super.validate(version); - checkNotNaN(i11, SpacecraftParametersKey.I11); - checkNotNaN(i22, SpacecraftParametersKey.I22); - checkNotNaN(i33, SpacecraftParametersKey.I33); - checkNotNaN(i12, SpacecraftParametersKey.I12); - checkNotNaN(i13, SpacecraftParametersKey.I13); - checkNotNaN(i23, SpacecraftParametersKey.I23); - } - - /** - * Get the coordinate system for the inertia tensor. - * @return the coordinate system for the inertia tensor - */ - public FrameFacade getInertiaReferenceFrame() { - return inertiaReferenceFrame; - } - - /** - * Set the coordinate system for the inertia tensor. - * @param inertiaReferenceFrame frame to be set - */ - public void setInertiaReferenceFrame(final FrameFacade inertiaReferenceFrame) { - refuseFurtherComments(); - this.inertiaReferenceFrame = inertiaReferenceFrame; - } - - /** - * Get the moment of Inertia about the 1-axis (N.m²). - * @return the moment of Inertia about the 1-axis. - */ - public double getI11() { - return i11; - } - - /** - * Set the moment of Inertia about the 1-axis (N.m²). - * @param i11 moment of Inertia about the 1-axis - */ - public void setI11(final double i11) { - refuseFurtherComments(); - this.i11 = i11; - } - - /** - * Get the moment of Inertia about the 2-axis (N.m²). - * @return the moment of Inertia about the 2-axis. - */ - public double getI22() { - return i22; - } - - /** - * Set the moment of Inertia about the 2-axis (N.m²). - * @param i22 moment of Inertia about the 2-axis - */ - public void setI22(final double i22) { - refuseFurtherComments(); - this.i22 = i22; - } - - /** - * Get the moment of Inertia about the 3-axis (N.m²). - * @return the moment of Inertia about the 3-axis. - */ - public double getI33() { - return i33; - } - - /** - * Set the moment of Inertia about the 3-axis (N.m²). - * @param i33 moment of Inertia about the 3-axis - */ - public void setI33(final double i33) { - refuseFurtherComments(); - this.i33 = i33; - } - - /** - * Get the moment of Inertia about the 1 and 2 axes (N.m²). - * @return the moment of Inertia about the 1 and 2 axes. - */ - public double getI12() { - return i12; - } - - /** - * Set the moment of Inertia about the 1 and 2 axes (N.m²). - * @param i12 moment of Inertia about the 1 and 2 axes - */ - public void setI12(final double i12) { - refuseFurtherComments(); - this.i12 = i12; - } - - /** - * Get the moment of Inertia about the 1 and 3 axes (N.m²). - * @return the moment of Inertia about the 1 and 3 axes. - */ - public double getI13() { - return i13; - } - - /** - * Set the moment of Inertia about the 1 and 3 axes (N.m²). - * @param i13 moment of Inertia about the 1 and 3 axes - */ - public void setI13(final double i13) { - refuseFurtherComments(); - this.i13 = i13; - } - - /** - * Get the moment of Inertia about the 2 and 3 axes (N.m²). - * @return the moment of Inertia about the 2 and 3 axes. - */ - public double getI23() { - return i23; - } - - /** - * Set the moment of Inertia about the 2 and 3 axes (N.m²). - * @param i23 moment of Inertia about the 2 and 3 axes - */ - public void setI23(final double i23) { - refuseFurtherComments(); - this.i23 = i23; - } - -} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParametersWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParametersWriter.java deleted file mode 100644 index d0d0fb5e16..0000000000 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpacecraftParametersWriter.java +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.orekit.files.ccsds.ndm.adm.apm; - -import java.io.IOException; - -import org.orekit.files.ccsds.definitions.Units; -import org.orekit.files.ccsds.section.AbstractWriter; -import org.orekit.files.ccsds.utils.generation.Generator; - -/** Writer for spacecraft parameters data. - * @author Luc Maisonobe - * @since 11.0 - */ -class SpacecraftParametersWriter extends AbstractWriter { - - /** Spacecraft parameters block. */ - private final SpacecraftParameters spacecraftParameters; - - /** Create a writer. - * @param xmlTag name of the XML tag surrounding the section - * @param kvnTag name of the KVN tag surrounding the section (may be null) - * @param spacecraftParameters spacecraft parameters data to write - */ - SpacecraftParametersWriter(final String xmlTag, final String kvnTag, - final SpacecraftParameters spacecraftParameters) { - super(xmlTag, kvnTag); - this.spacecraftParameters = spacecraftParameters; - } - - /** {@inheritDoc} */ - @Override - protected void writeContent(final Generator generator) throws IOException { - - generator.writeComments(spacecraftParameters.getComments()); - - // frame - if (spacecraftParameters.getInertiaReferenceFrame() != null) { - generator.writeEntry(SpacecraftParametersKey.INERTIA_REF_FRAME.name(), - spacecraftParameters.getInertiaReferenceFrame().getName(), - null, false); - } - - // inertia matrix - generator.writeEntry(SpacecraftParametersKey.I11.name(), spacecraftParameters.getI11(), Units.KG_M2, true); - generator.writeEntry(SpacecraftParametersKey.I22.name(), spacecraftParameters.getI22(), Units.KG_M2, true); - generator.writeEntry(SpacecraftParametersKey.I33.name(), spacecraftParameters.getI33(), Units.KG_M2, true); - generator.writeEntry(SpacecraftParametersKey.I12.name(), spacecraftParameters.getI12(), Units.KG_M2, true); - generator.writeEntry(SpacecraftParametersKey.I13.name(), spacecraftParameters.getI13(), Units.KG_M2, true); - generator.writeEntry(SpacecraftParametersKey.I23.name(), spacecraftParameters.getI23(), Units.KG_M2, true); - - } - -} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilized.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilized.java index 6e7e5a65b6..0e5ced1cd8 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilized.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilized.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; import org.orekit.files.ccsds.section.CommentsContainer; /** @@ -29,7 +29,7 @@ public class SpinStabilized extends CommentsContainer { /** Endpoints (i.e. frames A, B and their relationship). */ - private final AttitudeEndoints endpoints; + private final AttitudeEndpoints endpoints; /** Right ascension of spin axis vector (rad). */ private double spinAlpha; @@ -52,10 +52,25 @@ public class SpinStabilized extends CommentsContainer { /** Inertial nutation phase (rad). */ private double nutationPhase; + /** Right ascension of angular momentum vector (rad). + * @since 12.0 + */ + private double momentumAlpha; + + /** Declination of the angular momentum vector (rad). + * @since 12.0 + */ + private double momentumDelta; + + /** Angular velocity of spin vector around the angular momentum vector (rad/s). + * @since 12.0 + */ + private double nutationVel; + /** Simple constructor. */ public SpinStabilized() { - endpoints = new AttitudeEndoints(); + endpoints = new AttitudeEndpoints(); spinAlpha = Double.NaN; spinDelta = Double.NaN; spinAngle = Double.NaN; @@ -63,32 +78,42 @@ public SpinStabilized() { nutation = Double.NaN; nutationPer = Double.NaN; nutationPhase = Double.NaN; + momentumAlpha = Double.NaN; + momentumDelta = Double.NaN; + nutationVel = Double.NaN; } /** {@inheritDoc} */ @Override public void validate(final double version) { super.validate(version); - endpoints.checkMandatoryEntriesExceptExternalFrame(SpinStabilizedKey.SPIN_FRAME_A, + endpoints.checkMandatoryEntriesExceptExternalFrame(version, + SpinStabilizedKey.SPIN_FRAME_A, SpinStabilizedKey.SPIN_FRAME_B, SpinStabilizedKey.SPIN_DIR); endpoints.checkExternalFrame(SpinStabilizedKey.SPIN_FRAME_A, SpinStabilizedKey.SPIN_FRAME_B); - checkNotNaN(spinAlpha, SpinStabilizedKey.SPIN_ALPHA); - checkNotNaN(spinDelta, SpinStabilizedKey.SPIN_DELTA); - checkNotNaN(spinAngle, SpinStabilizedKey.SPIN_ANGLE); - checkNotNaN(spinAngleVel, SpinStabilizedKey.SPIN_ANGLE_VEL); + checkNotNaN(spinAlpha, SpinStabilizedKey.SPIN_ALPHA.name()); + checkNotNaN(spinDelta, SpinStabilizedKey.SPIN_DELTA.name()); + checkNotNaN(spinAngle, SpinStabilizedKey.SPIN_ANGLE.name()); + checkNotNaN(spinAngleVel, SpinStabilizedKey.SPIN_ANGLE_VEL.name()); if (Double.isNaN(nutation + nutationPer + nutationPhase)) { // if at least one is NaN, all must be NaN (i.e. not initialized) if (!(Double.isNaN(nutation) && Double.isNaN(nutationPer) && Double.isNaN(nutationPhase))) { throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "NUTATION*"); } } + if (Double.isNaN(momentumAlpha + momentumDelta + nutationVel)) { + // if at least one is NaN, all must be NaN (i.e. not initialized) + if (!(Double.isNaN(momentumAlpha) && Double.isNaN(momentumDelta) && Double.isNaN(nutationVel))) { + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "MOMENTUM*/NUTATION_VEL"); + } + } } /** Get the endpoints (i.e. frames A, B and their relationship). * @return endpoints */ - public AttitudeEndoints getEndpoints() { + public AttitudeEndpoints getEndpoints() { return endpoints; } @@ -211,4 +236,77 @@ public void setNutationPhase(final double nutationPhase) { this.nutationPhase = nutationPhase; } + /** + * Get the right ascension of angular momentum vector (rad). + * @return the right ascension of angular momentum vector + * @since 12.0 + */ + public double getMomentumAlpha() { + return momentumAlpha; + } + + /** + * Set the right ascension of angular momentum vector (rad). + * @param momentumAlpha value to be set + * @since 12.0 + */ + public void setMomentumAlpha(final double momentumAlpha) { + refuseFurtherComments(); + this.momentumAlpha = momentumAlpha; + } + + /** + * Get the declination of the angular momentum vector (rad). + * @return the declination of the angular momentum vector (rad). + * @since 12.0 + */ + public double getMomentumDelta() { + return momentumDelta; + } + + /** + * Set the declination of the angular momentum vector (rad). + * @param momentumDelta value to be set + * @since 12.0 + */ + public void setMomentumDelta(final double momentumDelta) { + refuseFurtherComments(); + this.momentumDelta = momentumDelta; + } + + /** + * Get the angular velocity of spin vector around angular momentum vector. + * @return angular velocity of spin vector around angular momentum vector (rad/s) + * @since 12.0 + */ + public double getNutationVel() { + return nutationVel; + } + + /** + * Set the angular velocity of spin vector around angular momentum vector. + * @param nutationVel angular velocity of spin vector around angular momentum vector (rad/s) + * @since 12.0 + */ + public void setNutationVel(final double nutationVel) { + refuseFurtherComments(); + this.nutationVel = nutationVel; + } + + /** Check if the logical block includes nutation. + * @return true if logical block includes nutation + * @since 12.0 + */ + public boolean hasNutation() { + return !Double.isNaN(nutation + nutationPer + nutationPhase); + } + + /** Check if the logical block includes momentum. + * @return true if logical block includes momentum + * @since 12.0 + */ + public boolean hasMomentum() { + return !Double.isNaN(momentumAlpha + momentumDelta + nutationVel); + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedKey.java index 495402e6ac..9f9a13c32b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -32,13 +32,18 @@ public enum SpinStabilizedKey { COMMENT((token, context, container) -> token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), - /** First reference frame entry. */ + /** First reference frame entry (only for ADM V1). */ SPIN_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), - /** Second reference frame entry. */ + /** First reference frame entry. + * @since 12.0 + */ + REF_FRAME_A((token, context, container) -> token.processAsFrame(container.getEndpoints()::setFrameA, context, true, true, true)), + + /** Second reference frame entry (only for ADM V1). */ SPIN_FRAME_B((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { - container.checkNotNull(container.getEndpoints().getFrameA(), SPIN_FRAME_A); + container.checkNotNull(container.getEndpoints().getFrameA(), SPIN_FRAME_A.name()); final boolean aIsSpaceraftBody = container.getEndpoints().getFrameA().asSpacecraftBodyFrame() != null; return token.processAsFrame(container.getEndpoints()::setFrameB, context, aIsSpaceraftBody, aIsSpaceraftBody, !aIsSpaceraftBody); @@ -46,7 +51,20 @@ public enum SpinStabilizedKey { return true; }), - /** Rotation direction entry. */ + /** Second reference frame entry. + * @since 12.0 + */ + REF_FRAME_B((token, context, container) -> { + if (token.getType() == TokenType.ENTRY) { + container.checkNotNull(container.getEndpoints().getFrameA(), REF_FRAME_A.name()); + final boolean aIsSpaceraftBody = container.getEndpoints().getFrameA().asSpacecraftBodyFrame() != null; + return token.processAsFrame(container.getEndpoints()::setFrameB, context, + aIsSpaceraftBody, aIsSpaceraftBody, !aIsSpaceraftBody); + } + return true; + }), + + /** Rotation direction entry (only for ADM V1). */ SPIN_DIR((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { container.getEndpoints().setA2b(token.getContentAsUppercaseCharacter() == 'A'); @@ -80,7 +98,25 @@ public enum SpinStabilizedKey { /** Nutation phase entry. */ NUTATION_PHASE((token, context, container) -> token.processAsDouble(Unit.DEGREE, context.getParsedUnitsBehavior(), - container::setNutationPhase)); + container::setNutationPhase)), + + /** Momentum right ascension entry. + * @since 12.0 + */ + MOMENTUM_ALPHA((token, context, container) -> token.processAsDouble(Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setMomentumAlpha)), + + /** Momentum declination entry. + * @since 12.0 + */ + MOMENTUM_DELTA((token, context, container) -> token.processAsDouble(Unit.DEGREE, context.getParsedUnitsBehavior(), + container::setMomentumDelta)), + + /** Nutation velocity entry. + * @since 12.0 + */ + NUTATION_VEL((token, context, container) -> token.processAsDouble(Units.DEG_PER_S, context.getParsedUnitsBehavior(), + container::setNutationVel)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedWriter.java index 65e444906b..af2c462377 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/SpinStabilizedWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import java.io.IOException; import org.orekit.files.ccsds.definitions.Units; -import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints; +import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints; import org.orekit.files.ccsds.section.AbstractWriter; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.utils.units.Unit; @@ -31,17 +31,24 @@ */ class SpinStabilizedWriter extends AbstractWriter { + /** Format version. + * @since 12.0 + */ + private final double formatVersion; + /** Spin stabilized block. */ private final SpinStabilized spinStabilized; /** Create a writer. + * @param formatVersion format version * @param xmlTag name of the XML tag surrounding the section * @param kvnTag name of the KVN tag surrounding the section (may be null) * @param spinStabilized spin stabilized data to write */ - SpinStabilizedWriter(final String xmlTag, final String kvnTag, - final SpinStabilized spinStabilized) { + SpinStabilizedWriter(final double formatVersion, final String xmlTag, final String kvnTag, + final SpinStabilized spinStabilized) { super(xmlTag, kvnTag); + this.formatVersion = formatVersion; this.spinStabilized = spinStabilized; } @@ -52,11 +59,16 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeComments(spinStabilized.getComments()); // endpoints - generator.writeEntry(SpinStabilizedKey.SPIN_FRAME_A.name(), spinStabilized.getEndpoints().getFrameA().getName(), null, true); - generator.writeEntry(SpinStabilizedKey.SPIN_FRAME_B.name(), spinStabilized.getEndpoints().getFrameB().getName(), null, true); - generator.writeEntry(SpinStabilizedKey.SPIN_DIR.name(), - spinStabilized.getEndpoints().isA2b() ? AttitudeEndoints.A2B : AttitudeEndoints.B2A, - null, true); + if (formatVersion < 2.0) { + generator.writeEntry(SpinStabilizedKey.SPIN_FRAME_A.name(), spinStabilized.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(SpinStabilizedKey.SPIN_FRAME_B.name(), spinStabilized.getEndpoints().getFrameB().getName(), null, true); + generator.writeEntry(SpinStabilizedKey.SPIN_DIR.name(), + spinStabilized.getEndpoints().isA2b() ? AttitudeEndpoints.A2B : AttitudeEndpoints.B2A, + null, true); + } else { + generator.writeEntry(SpinStabilizedKey.REF_FRAME_A.name(), spinStabilized.getEndpoints().getFrameA().getName(), null, true); + generator.writeEntry(SpinStabilizedKey.REF_FRAME_B.name(), spinStabilized.getEndpoints().getFrameB().getName(), null, true); + } // spin generator.writeEntry(SpinStabilizedKey.SPIN_ALPHA.name(), spinStabilized.getSpinAlpha(), Unit.DEGREE, true); @@ -64,10 +76,17 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(SpinStabilizedKey.SPIN_ANGLE.name(), spinStabilized.getSpinAngle(), Unit.DEGREE, true); generator.writeEntry(SpinStabilizedKey.SPIN_ANGLE_VEL.name(), spinStabilized.getSpinAngleVel(), Units.DEG_PER_S, true); - // nutation - generator.writeEntry(SpinStabilizedKey.NUTATION.name(), spinStabilized.getNutation(), Unit.DEGREE, false); - generator.writeEntry(SpinStabilizedKey.NUTATION_PER.name(), spinStabilized.getNutationPeriod(), Unit.SECOND, false); - generator.writeEntry(SpinStabilizedKey.NUTATION_PHASE.name(), spinStabilized.getNutationPhase(), Unit.DEGREE, false); + if (spinStabilized.hasMomentum()) { + // momentum + generator.writeEntry(SpinStabilizedKey.MOMENTUM_ALPHA.name(), spinStabilized.getMomentumAlpha(), Unit.DEGREE, true); + generator.writeEntry(SpinStabilizedKey.MOMENTUM_DELTA.name(), spinStabilized.getMomentumDelta(), Unit.DEGREE, true); + generator.writeEntry(SpinStabilizedKey.NUTATION_VEL.name(), spinStabilized.getNutationVel(), Units.DEG_PER_S, true); + } else if (spinStabilized.hasNutation()) { + // nutation + generator.writeEntry(SpinStabilizedKey.NUTATION.name(), spinStabilized.getNutation(), Unit.DEGREE, true); + generator.writeEntry(SpinStabilizedKey.NUTATION_PER.name(), spinStabilized.getNutationPeriod(), Unit.SECOND, true); + generator.writeEntry(SpinStabilizedKey.NUTATION_PHASE.name(), spinStabilized.getNutationPhase(), Unit.DEGREE, true); + } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/XmlSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/XmlSubStructureKey.java deleted file mode 100644 index be52e789ae..0000000000 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/XmlSubStructureKey.java +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.files.ccsds.ndm.adm.apm; - -import org.orekit.files.ccsds.utils.lexical.ParseToken; -import org.orekit.files.ccsds.utils.lexical.TokenType; - -/** Keywords for APM data sub-structure in XML files. - * @author Luc Maisonobe - * @since 11.0 - */ -enum XmlSubStructureKey { - - /** General comment. */ - COMMENT((token, parser) -> token.getType() == TokenType.ENTRY ? parser.addGeneralComment(token.getContentAsNormalizedString()) : true), - - /** Quaternion section. */ - quaternionState((token, parser) -> parser.manageQuaternionSection(token.getType() == TokenType.START)), - - /** Euler elements / three axis stabilized section. */ - eulerElementsThree((token, parser) -> parser.manageEulerElementsThreeSection(token.getType() == TokenType.START)), - - /** Euler elements /spin stabilized section. */ - eulerElementsSpin((token, parser) -> parser.manageEulerElementsSpinSection(token.getType() == TokenType.START)), - - /** Spacecraft parameters section. */ - spacecraftParameters((token, parser) -> parser.manageSpacecraftParametersSection(token.getType() == TokenType.START)), - - /** Maneuver parameters section. */ - maneuverParameters((token, parser) -> parser.manageManeuverParametersSection(token.getType() == TokenType.START)); - - /** Processing method. */ - private final TokenProcessor processor; - - /** Simple constructor. - * @param processor processing method - */ - XmlSubStructureKey(final TokenProcessor processor) { - this.processor = processor; - } - - /** Process one token. - * @param token token to process - * @param parser APM file parser - * @return true of token was accepted - */ - public boolean process(final ParseToken token, final ApmParser parser) { - return processor.process(token, parser); - } - - /** Interface for processing one token. */ - interface TokenProcessor { - /** Process one token. - * @param token token to process - * @param parser APM file parser - * @return true of token was accepted - */ - boolean process(ParseToken token, ApmParser parser); - } - -} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/package-info.java index dec3f014da..2efa2193f1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/apm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/adm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/adm/package-info.java index 874a174138..a8801ecc6c 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/adm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/adm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadata.java index fc80a88ad1..eed9ee4182 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadataKey.java index 26a54ec0d6..2b1f38c22b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalCovarianceMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,28 +31,30 @@ public enum AdditionalCovarianceMetadataKey { /** The atmospheric density forecast error. */ DENSITY_FORECAST_UNCERTAINTY((token, context, container) -> token.processAsDouble(Unit.NONE, context.getParsedUnitsBehavior(), - container::setDensityForecastUncertainty)), + container::setDensityForecastUncertainty)), /** The minimum suggested covariance scale factor. */ CSCALE_FACTOR_MIN((token, context, container) -> token.processAsDouble(Unit.NONE, context.getParsedUnitsBehavior(), - container::setcScaleFactorMin)), + container::setcScaleFactorMin)), /** The (median) suggested covariance scale factor. */ CSCALE_FACTOR((token, context, container) -> token.processAsDouble(Unit.NONE, context.getParsedUnitsBehavior(), - container::setcScaleFactor)), + container::setcScaleFactor)), /** The maximum suggested covariance scale factor. */ CSCALE_FACTOR_MAX((token, context, container) -> token.processAsDouble(Unit.NONE, context.getParsedUnitsBehavior(), - container::setcScaleFactorMax)), + container::setcScaleFactorMax)), /** The source (or origin) of the specific orbital data for this object. */ SCREENING_DATA_SOURCE((token, context, container) -> token.processAsFreeTextString(container::setScreeningDataSource)), /** The Drag Consider Parameter (DCP) sensitivity vector (position errors at TCA). */ - DCP_SENSITIVITY_VECTOR_POSITION((token, context, container) -> token.processAsDoubleArray(container::setDcpSensitivityVectorPosition)), + DCP_SENSITIVITY_VECTOR_POSITION((token, context, container) -> token.processAsDoubleArray(Unit.NONE, context.getParsedUnitsBehavior(), + container::setDcpSensitivityVectorPosition)), /** The Drag Consider Parameter (DCP) sensitivity vector (velocity errors at TCA). */ - DCP_SENSITIVITY_VECTOR_VELOCITY((token, context, container) -> token.processAsDoubleArray(container::setDcpSensitivityVectorVelocity)); + DCP_SENSITIVITY_VECTOR_VELOCITY((token, context, container) -> token.processAsDoubleArray(Unit.NONE, context.getParsedUnitsBehavior(), + container::setDcpSensitivityVectorVelocity)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParameters.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParameters.java index 86b7dbf764..3c7d501e4f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParameters.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -64,7 +64,7 @@ public class AdditionalParameters extends CommonPhysicalProperties { private double apoapsisAltitude; /** The distance of the closest point in the objects orbit above the equatorial radius of the central body . */ - private double periapsissAltitude; + private double periapsisAltitude; /** The angle between the objects orbit plane and the orbit centers equatorial plane. */ private double inclination; @@ -93,9 +93,9 @@ public AdditionalParameters() { thrustAcceleration = Double.NaN; sedr = Double.NaN; hbr = Double.NaN; - apoapsisAltitude = Double.NaN; - periapsissAltitude = Double.NaN; - inclination = Double.NaN; + apoapsisAltitude = Double.NaN; + periapsisAltitude = Double.NaN; + inclination = Double.NaN; covConfidence = Double.NaN; } @@ -304,16 +304,16 @@ public void setApoapsisAltitude(final double apoapsisAltitude) { /** Get the distance of the closest point in the objects orbit above the equatorial radius of the central body. * @return the periapsissAltitude */ - public double getPeriapsissAltitude() { - return periapsissAltitude; + public double getPeriapsisAltitude() { + return periapsisAltitude; } /** Set the distance of the closest point in the objects orbit above the equatorial radius of the central body. - * @param periapsissAltitude the periapsissHeight to set + * @param periapsisAltitude the periapsissHeight to set */ - public void setPeriapsissAltitude(final double periapsissAltitude) { + public void setPeriapsisAltitude(final double periapsisAltitude) { refuseFurtherComments(); - this.periapsissAltitude = periapsissAltitude; + this.periapsisAltitude = periapsisAltitude; } /** Get the angle between the objects orbit plane and the orbit centers equatorial plane. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersKey.java index 30ad1a3336..1a914a3f98 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -159,7 +159,7 @@ public enum AdditionalParametersKey { /** The distance of the closest point in the objects orbit above the equatorial radius of the central body. */ PERIAPSIS_ALTITUDE((token, context, container) -> token.processAsDouble(Unit.KILOMETRE, context.getParsedUnitsBehavior(), - container::setPeriapsissAltitude)), + container::setPeriapsisAltitude)), /** The angle between the objects orbit plane and the orbit centers equatorial plane. */ INCLINATION((token, context, container) -> token.processAsDouble(Unit.DEGREE, context.getParsedUnitsBehavior(), diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersWriter.java index 37656102b9..84f91ea84f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AdditionalParametersWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AltCovarianceType.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AltCovarianceType.java index a7368aa256..57caeaacb8 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/AltCovarianceType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/AltCovarianceType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/Cdm.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/Cdm.java index baa45da1b3..b5aef1852a 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/Cdm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/Cdm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmData.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmData.java index 59e334dc35..695cc18bfa 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,22 +33,22 @@ public class CdmData implements Data { private final CommentsContainer commentsBlock; /** Quaternion block. */ - private final ODParameters ODparametersBlock; + private ODParameters ODParametersBlock; /** Euler angles block. */ - private final AdditionalParameters additionalParametersBlock; + private AdditionalParameters additionalParametersBlock; /** Spin-stabilized block. */ private final StateVector stateVectorBlock; /** Spacecraft parameters block. */ - private final RTNCovariance covarianceMatrixBlock; + private RTNCovariance covarianceMatrixBlock; /** XYZ covariance block. */ private final XYZCovariance xyzCovarianceMatrixBlock; /** Sigma/Eigenvectors covariance block. */ - private final SigmaEigenvectorsCovariance sig3eigvec3CovarianceBlock; + private final SigmaEigenvectorsCovariance sig3EigVec3CovarianceBlock; /** Type of alternate covariance, if present. */ private AltCovarianceType altCovarianceType; @@ -65,31 +65,31 @@ public class CdmData implements Data { /** Default constructor. * @param commentsBlock general comments block - * @param ODparametersBlock OD parameters block (may be null) + * @param ODParametersBlock OD parameters block (may be null) * @param additionalParametersBlock additionnal parameters block (may be null) * @param stateVectorBlock state vector block * @param covarianceMatrixBlock covariance matrix in RTN coordinates frame block * @param xyzCovarianceBlock XYZ covariance matrix block - * @param sig3eigvec3CovarianceBlock sigma/eigenvector covariance block + * @param sig3EigVec3CovarianceBlock sigma/eigenvector covariance block * @param altCovarianceType type of alternate covariance * @param additionalCovMetadata additional covariance metadata */ private CdmData(final CommentsContainer commentsBlock, - final ODParameters ODparametersBlock, + final ODParameters ODParametersBlock, final AdditionalParameters additionalParametersBlock, final StateVector stateVectorBlock, final RTNCovariance covarianceMatrixBlock, final XYZCovariance xyzCovarianceBlock, - final SigmaEigenvectorsCovariance sig3eigvec3CovarianceBlock, + final SigmaEigenvectorsCovariance sig3EigVec3CovarianceBlock, final AltCovarianceType altCovarianceType, final AdditionalCovarianceMetadata additionalCovMetadata) { - this.commentsBlock = commentsBlock; - this.ODparametersBlock = ODparametersBlock; - this.additionalParametersBlock = additionalParametersBlock; + this.commentsBlock = commentsBlock; + this.ODParametersBlock = ODParametersBlock; + this.additionalParametersBlock = additionalParametersBlock; this.stateVectorBlock = stateVectorBlock; this.covarianceMatrixBlock = covarianceMatrixBlock; this.xyzCovarianceMatrixBlock = xyzCovarianceBlock; - this.sig3eigvec3CovarianceBlock = sig3eigvec3CovarianceBlock; + this.sig3EigVec3CovarianceBlock = sig3EigVec3CovarianceBlock; this.altCovarianceType = altCovarianceType; this.additionalCovMetadata = additionalCovMetadata; this.userDefinedBlock = null; @@ -97,42 +97,42 @@ private CdmData(final CommentsContainer commentsBlock, /** Constructor with RTN covariance. * @param commentsBlock general comments block - * @param ODparametersBlock OD parameters block (may be null) + * @param ODParametersBlock OD parameters block (may be null) * @param additionalParametersBlock additionnal parameters block (may be null) * @param stateVectorBlock state vector block * @param covarianceMatrixBlock covariance matrix in RTN coordinates frame block */ public CdmData(final CommentsContainer commentsBlock, - final ODParameters ODparametersBlock, + final ODParameters ODParametersBlock, final AdditionalParameters additionalParametersBlock, final StateVector stateVectorBlock, final RTNCovariance covarianceMatrixBlock) { - this(commentsBlock, ODparametersBlock, additionalParametersBlock, stateVectorBlock, - covarianceMatrixBlock, null, null, null, null); + this(commentsBlock, ODParametersBlock, additionalParametersBlock, stateVectorBlock, + covarianceMatrixBlock, null, null, null, null); } /** Constructor with RTN covariance. * @param commentsBlock general comments block - * @param ODparametersBlock OD parameters block (may be null) + * @param ODParametersBlock OD parameters block (may be null) * @param additionalParametersBlock additionnal parameters block (may be null) * @param stateVectorBlock state vector block * @param covarianceMatrixBlock covariance matrix in RTN coordinates frame block * @param additionalCovMetadata additional covariance metadata */ public CdmData(final CommentsContainer commentsBlock, - final ODParameters ODparametersBlock, + final ODParameters ODParametersBlock, final AdditionalParameters additionalParametersBlock, final StateVector stateVectorBlock, final RTNCovariance covarianceMatrixBlock, final AdditionalCovarianceMetadata additionalCovMetadata) { - this(commentsBlock, ODparametersBlock, additionalParametersBlock, stateVectorBlock, - covarianceMatrixBlock, null, null, null, additionalCovMetadata); + this(commentsBlock, ODParametersBlock, additionalParametersBlock, stateVectorBlock, + covarianceMatrixBlock, null, null, null, additionalCovMetadata); } /** Constructor with RTN and XYZ covariance. * @param commentsBlock general comments block - * @param ODparametersBlock OD parameters block (may be null) + * @param ODParametersBlock OD parameters block (may be null) * @param additionalParametersBlock additionnal parameters block (may be null) * @param stateVectorBlock state vector block * @param covarianceMatrixBlock covariance matrix in RTN coordinates frame block @@ -140,41 +140,41 @@ public CdmData(final CommentsContainer commentsBlock, * @param additionalCovMetadata additional covariance metadata */ public CdmData(final CommentsContainer commentsBlock, - final ODParameters ODparametersBlock, + final ODParameters ODParametersBlock, final AdditionalParameters additionalParametersBlock, final StateVector stateVectorBlock, final RTNCovariance covarianceMatrixBlock, final XYZCovariance xyzCovarianceBlock, final AdditionalCovarianceMetadata additionalCovMetadata) { - this(commentsBlock, ODparametersBlock, additionalParametersBlock, stateVectorBlock, - covarianceMatrixBlock, xyzCovarianceBlock, null, AltCovarianceType.XYZ, additionalCovMetadata); + this(commentsBlock, ODParametersBlock, additionalParametersBlock, stateVectorBlock, + covarianceMatrixBlock, xyzCovarianceBlock, null, AltCovarianceType.XYZ, additionalCovMetadata); } /** Constructor with RTN and sigma/eigenvector covariance. * @param commentsBlock general comments block - * @param ODparametersBlock OD parameters block (may be null) + * @param ODParametersBlock OD parameters block (may be null) * @param additionalParametersBlock additionnal parameters block (may be null) * @param stateVectorBlock state vector block * @param covarianceMatrixBlock covariance matrix in RTN coordinates frame block - * @param sig3eigvec3CovarianceBlock sigma/eigenvector covariance block + * @param sig3EigVec3CovarianceBlock sigma/eigenvector covariance block * @param additionalCovMetadata additional covariance metadata */ public CdmData(final CommentsContainer commentsBlock, - final ODParameters ODparametersBlock, + final ODParameters ODParametersBlock, final AdditionalParameters additionalParametersBlock, final StateVector stateVectorBlock, final RTNCovariance covarianceMatrixBlock, - final SigmaEigenvectorsCovariance sig3eigvec3CovarianceBlock, + final SigmaEigenvectorsCovariance sig3EigVec3CovarianceBlock, final AdditionalCovarianceMetadata additionalCovMetadata) { - this(commentsBlock, ODparametersBlock, additionalParametersBlock, stateVectorBlock, - covarianceMatrixBlock, null, sig3eigvec3CovarianceBlock, AltCovarianceType.CSIG3EIGVEC3, additionalCovMetadata); + this(commentsBlock, ODParametersBlock, additionalParametersBlock, stateVectorBlock, + covarianceMatrixBlock, null, sig3EigVec3CovarianceBlock, AltCovarianceType.CSIG3EIGVEC3, additionalCovMetadata); } /** {@inheritDoc} */ @Override public void validate(final double version) { - if (ODparametersBlock != null) { - ODparametersBlock.validate(version); + if (ODParametersBlock != null) { + ODParametersBlock.validate(version); } if (additionalParametersBlock != null) { additionalParametersBlock.validate(version); @@ -187,7 +187,7 @@ public void validate(final double version) { } else if (altCovarianceType == AltCovarianceType.XYZ) { xyzCovarianceMatrixBlock.validate(version); } else if (altCovarianceType == AltCovarianceType.CSIG3EIGVEC3) { - sig3eigvec3CovarianceBlock.validate(version); + sig3EigVec3CovarianceBlock.validate(version); } } @@ -203,7 +203,14 @@ public List getComments() { * @return OD parameters block (may be null) */ public ODParameters getODParametersBlock() { - return ODparametersBlock; + return ODParametersBlock; + } + + /** Set the OD parameters logical block. + * @param ODParametersBlock the OD Parameters logical block + */ + public void setODParametersBlock(final ODParameters ODParametersBlock) { + this.ODParametersBlock = ODParametersBlock; } /** Get the additional parameters logical block. @@ -213,6 +220,13 @@ public AdditionalParameters getAdditionalParametersBlock() { return additionalParametersBlock; } + /** Set the additional parameters logical block. + * @param additionalParametersBlock the additional parameters logical block + */ + public void setAdditionalParametersBlock(final AdditionalParameters additionalParametersBlock) { + this.additionalParametersBlock = additionalParametersBlock; + } + /** Get the state vector logical block. * @return state vector block */ @@ -243,8 +257,8 @@ public XYZCovariance getXYZCovarianceBlock() { *

          This method will return null if the block is not defined in the CDM.

          * @return the Sigma / Eigenvector covariance block */ - public SigmaEigenvectorsCovariance getSig3Eigvec3CovarianceBlock() { - return sig3eigvec3CovarianceBlock; + public SigmaEigenvectorsCovariance getSig3EigVec3CovarianceBlock() { + return sig3EigVec3CovarianceBlock; } /** Get the additional covariance metadata logical block. @@ -255,7 +269,14 @@ public AdditionalCovarianceMetadata getAdditionalCovMetadataBlock() { return additionalCovMetadata; } - /** Get the user defined logical block. + /** Set the additional covariance metadata logical block. + * @param covarianceMatrixBlock the additional covariance metadata logical block + */ + public void setCovarianceMatrixBlock(final RTNCovariance covarianceMatrixBlock) { + this.covarianceMatrixBlock = covarianceMatrixBlock; + } + + /** Get the user defined logical block. *

          This method will return null if the block is not defined in the CDM.

          * @return the additional covariance metadata logical block */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeader.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeader.java index 5fdfcff43b..8e66f151c6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeader.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,6 @@ package org.orekit.files.ccsds.ndm.cdm; import org.orekit.files.ccsds.section.Header; -import org.orekit.files.ccsds.section.HeaderKey; /** * Header of a CCSDS Conjunction Data Message. @@ -29,15 +28,11 @@ public class CdmHeader extends Header { /** ID that uniquely identifies a message from a given originator. */ private String messageFor; - /** User-defined free-text message classification or caveats of this CDM. */ - private String classification; - /** * Constructor. - * @param minVersionMessageId minimum version for {@link HeaderKey#MESSAGE_ID} */ - public CdmHeader(final double minVersionMessageId) { - super(minVersionMessageId); + public CdmHeader() { + super(1.0, 2.0); } /** {@inheritDoc} */ @@ -63,18 +58,4 @@ public void setMessageFor(final String spacecraftNames) { this.messageFor = spacecraftNames; } - /** Get the classification or caveats text message of this CDM. - * @return the classification - */ - public String getClassification() { - return classification; - } - - /** Set the classification or caveats text message of this CDM. - * @param classification the classification to set - */ - public void setClassification(final String classification) { - this.classification = classification; - } - } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderKey.java index 699fea8bc1..606fc81e7e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderProcessingState.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderProcessingState.java index 58691212c9..42882fa778 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderProcessingState.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmHeaderProcessingState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMessageWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMessageWriter.java index 53b761cb46..86727bddb5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMessageWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMessageWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -244,4 +244,22 @@ public void writeFooter(final Generator generator) throws IOException { generator.endMessage(root); } + /** {@inheritDoc} */ + @Override + public String getRoot() { + return root; + } + + /** {@inheritDoc} */ + @Override + public String getFormatVersionKey() { + return formatVersionKey; + } + + /** {@inheritDoc} */ + @Override + public double getVersion() { + return version; + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadata.java index 038a70cf65..b052c48255 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,15 +18,18 @@ import java.util.List; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.CelestialBody; import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.definitions.YesNoUnknown; import org.orekit.files.ccsds.definitions.BodyFacade; -import org.orekit.files.ccsds.definitions.CelestialBodyFrame; import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.definitions.CelestialBodyFrame; import org.orekit.files.ccsds.definitions.ModifiedFrame; import org.orekit.files.ccsds.definitions.TimeSystem; -import org.orekit.files.ccsds.definitions.YesNoUnknown; import org.orekit.files.ccsds.ndm.odm.ocm.ObjectType; import org.orekit.files.ccsds.section.Metadata; import org.orekit.frames.Frame; @@ -112,14 +115,14 @@ public class CdmMetadata extends Metadata { /** N-body perturbation bodies. */ private List nBodyPerturbations; - /** Is solar radiation pressure taken into account or not ? */ - private boolean isSolarRadPressure; + /** Is solar radiation pressure taken into account or not ? STANDARD CCSDS saying YES/NO choice and optional */ + private YesNoUnknown isSolarRadPressure; - /** Is solid Earth and ocean tides taken into account or not ? */ - private boolean isEarthTides; + /** Is solid Earth and ocean tides taken into account or not. STANDARD CCSDS saying YES/NO choice and optional */ + private YesNoUnknown isEarthTides; - /** Is in-track thrust modelling used or not ? */ - private boolean isIntrackThrustModeled; + /** Is in-track thrust modelling used or not. STANDARD CCSDS saying YES/NO choice and optional */ + private YesNoUnknown isIntrackThrustModeled; /** The source from which the covariance data used in the report for both Object 1 and Object 2 originates. */ private String covarianceSource; @@ -132,23 +135,35 @@ public class CdmMetadata extends Metadata { /** Simple constructor. */ + @DefaultDataContext public CdmMetadata() { super(null); + orbitCenter = new BodyFacade(CelestialBodyFactory.EARTH.toUpperCase(), CelestialBodyFactory.getEarth()); + } + + /** Simple constructor. + * + * @param dataContext data context + */ + public CdmMetadata(final DataContext dataContext) { + super(null); + final CelestialBody earth = dataContext.getCelestialBodies().getEarth(); + orbitCenter = new BodyFacade(earth.getName().toUpperCase(), earth); } /** {@inheritDoc} */ @Override public void validate(final double version) { // We only check values that are mandatory in a cdm file - checkNotNull(object, CdmMetadataKey.OBJECT); - checkNotNull(objectDesignator, CdmMetadataKey.OBJECT_DESIGNATOR); - checkNotNull(catalogName, CdmMetadataKey.CATALOG_NAME); - checkNotNull(objectName, CdmMetadataKey.OBJECT_NAME); - checkNotNull(internationalDesignator, CdmMetadataKey.INTERNATIONAL_DESIGNATOR); - checkNotNull(ephemName, CdmMetadataKey.EPHEMERIS_NAME); - checkNotNull(covarianceMethod, CdmMetadataKey.COVARIANCE_METHOD); - checkNotNull(maneuverable, CdmMetadataKey.MANEUVERABLE); - checkNotNull(refFrame, CdmMetadataKey.REF_FRAME); + checkNotNull(object, CdmMetadataKey.OBJECT.name()); + checkNotNull(objectDesignator, CdmMetadataKey.OBJECT_DESIGNATOR.name()); + checkNotNull(catalogName, CdmMetadataKey.CATALOG_NAME.name()); + checkNotNull(objectName, CdmMetadataKey.OBJECT_NAME.name()); + checkNotNull(internationalDesignator, CdmMetadataKey.INTERNATIONAL_DESIGNATOR.name()); + checkNotNull(ephemName, CdmMetadataKey.EPHEMERIS_NAME.name()); + checkNotNull(covarianceMethod, CdmMetadataKey.COVARIANCE_METHOD.name()); + checkNotNull(maneuverable, CdmMetadataKey.MANEUVERABLE.name()); + checkNotNull(refFrame, CdmMetadataKey.REF_FRAME.name()); } /** @@ -416,8 +431,13 @@ public Frame getFrame() { if (orbitCenter == null || orbitCenter.getBody() == null) { throw new OrekitException(OrekitMessages.NO_DATA_LOADED_FOR_CELESTIAL_BODY, "No Orbit center name"); } - if (refFrame.asFrame() == null) { - throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, refFrame.getName()); + if (refFrame == null) { + throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, "No reference frame"); + } + else { + if (refFrame.asFrame() == null) { + throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, refFrame.getName()); + } } // Just return frame if we don't need to shift the center based on CENTER_NAME // MCI and ICRF are the only non-Earth centered frames specified in Annex A. @@ -518,52 +538,52 @@ public void setNBodyPerturbations(final List nBody) { } /** - * Get boolean that indicates if Solar Radiation Pressure is taken into account or not. - * @return isSolarRadPressure boolean + * Get Enum YesNoUnknown that indicates if Solar Radiation Pressure is taken into account or not. + * @return isSolarRadPressure YesNoUnknown */ - public boolean getSolarRadiationPressure() { + public YesNoUnknown getSolarRadiationPressure() { return isSolarRadPressure; } /** - * Set boolean that indicates if Solar Radiation Pressure is taken into account or not. - * @param isSolRadPressure boolean + * Set Enum that indicates if Solar Radiation Pressure is taken into account or not. + * @param isSolRadPressure YesNoUnknown */ - public void setSolarRadiationPressure(final boolean isSolRadPressure) { + public void setSolarRadiationPressure(final YesNoUnknown isSolRadPressure) { refuseFurtherComments(); this.isSolarRadPressure = isSolRadPressure; } /** - * Get boolean that indicates if Earth and ocean tides are taken into account or not. - * @return isEarthTides boolean + * Get Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not. + * @return isEarthTides YesNoUnknown */ - public boolean getEarthTides() { + public YesNoUnknown getEarthTides() { return isEarthTides; } /** - * Set boolean that indicates if Earth and ocean tides are taken into account or not. - * @param EarthTides boolean + * Set Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not. + * @param EarthTides YesNoUnknown */ - public void setEarthTides(final boolean EarthTides) { + public void setEarthTides(final YesNoUnknown EarthTides) { refuseFurtherComments(); this.isEarthTides = EarthTides; } /** - * Get boolean that indicates if intrack thrust modeling was into account or not. - * @return isEarthTides boolean + * Get Enum YesNoUnknown that indicates if intrack thrust modeling was into account or not. + * @return isEarthTides YesNoUnknown */ - public boolean getIntrackThrust() { + public YesNoUnknown getIntrackThrust() { return isIntrackThrustModeled; } /** * Set boolean that indicates if intrack thrust modeling was into account or not. - * @param IntrackThrustModeled boolean + * @param IntrackThrustModeled YesNoUnknown */ - public void setIntrackThrust(final boolean IntrackThrustModeled) { + public void setIntrackThrust(final YesNoUnknown IntrackThrustModeled) { refuseFurtherComments(); this.isIntrackThrustModeled = IntrackThrustModeled; } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataKey.java index da9b9cc469..abefba30c4 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -107,13 +107,13 @@ public enum CdmMetadataKey { context.getDataContext().getCelestialBodies())), /** Is solar radiation pressure used for the OD of the object ? */ - SOLAR_RAD_PRESSURE((token, context, container) -> token.processAsBoolean(container::setSolarRadiationPressure)), + SOLAR_RAD_PRESSURE((token, context, container) -> token.processAsEnum(YesNoUnknown.class, container::setSolarRadiationPressure)), /** Is solid Earth and ocean tides used for the OD of the object ? */ - EARTH_TIDES((token, context, container) -> token.processAsBoolean(container::setEarthTides)), + EARTH_TIDES((token, context, container) -> token.processAsEnum(YesNoUnknown.class, container::setEarthTides)), /** Indication of whether in-track thrust modeling used for the object. */ - INTRACK_THRUST((token, context, container) -> token.processAsBoolean(container::setIntrackThrust)); + INTRACK_THRUST((token, context, container) -> token.processAsEnum(YesNoUnknown.class, container::setIntrackThrust)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataWriter.java index 6de93a4f81..df08970aa9 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmMetadataWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,12 +33,6 @@ */ public class CdmMetadataWriter extends AbstractWriter { - /** Constant for boolean translation. */ - private static final String YES = "YES"; - - /** Constant for boolean translation. */ - private static final String NO = "NO"; - /** Metadata. */ private final CdmMetadata metadata; @@ -119,10 +113,18 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(CdmMetadataKey.N_BODY_PERTURBATIONS.name(), names, false); } - // other perturbations - generator.writeEntry(CdmMetadataKey.SOLAR_RAD_PRESSURE.name(), metadata.getSolarRadiationPressure() ? YES : NO, null, false); - generator.writeEntry(CdmMetadataKey.EARTH_TIDES.name(), metadata.getEarthTides() ? YES : NO, null, false); - generator.writeEntry(CdmMetadataKey.INTRACK_THRUST.name(), metadata.getIntrackThrust() ? YES : NO, null, false); + if (metadata.getSolarRadiationPressure() != null) { + generator.writeEntry(CdmMetadataKey.SOLAR_RAD_PRESSURE.name(), metadata.getSolarRadiationPressure().name(), + null, false); + } + if (metadata.getEarthTides() != null) { + generator.writeEntry(CdmMetadataKey.EARTH_TIDES.name(), metadata.getEarthTides().name(), + null, false); + } + if (metadata.getIntrackThrust() != null) { + generator.writeEntry(CdmMetadataKey.INTRACK_THRUST.name(), metadata.getIntrackThrust().name(), + null, false); + } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmParser.java index 963cfc0359..d18ce5ff93 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import org.orekit.data.DataContext; import org.orekit.files.ccsds.definitions.TimeSystem; @@ -49,7 +50,7 @@ * @author Melina Vanel * @since 11.2 */ -public class CdmParser extends AbstractConstituentParser { +public class CdmParser extends AbstractConstituentParser { /** Comment key. */ private static String COMMENT = "COMMENT"; @@ -122,10 +123,13 @@ public class CdmParser extends AbstractConstituentParser { * @param simpleEOP if true, tidal effects are ignored when interpolating EOP * @param dataContext used to retrieve frames, time scales, etc. * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ public CdmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, - final ParsedUnitsBehavior parsedUnitsBehavior) { - super(Cdm.ROOT, Cdm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, parsedUnitsBehavior); + final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(Cdm.ROOT, Cdm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, parsedUnitsBehavior, filters); this.doRelativeMetadata = true; this.isDatafinished = false; } @@ -139,7 +143,7 @@ public CdmHeader getHeader() { /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new CdmHeader(1.0); + header = new CdmHeader(); segments = new ArrayList<>(); metadata = null; relativeMetadata = null; @@ -196,7 +200,7 @@ public boolean prepareMetadata() { relativeMetadata = new CdmRelativeMetadata(); relativeMetadata.setTimeSystem(TimeSystem.UTC); } - metadata = new CdmMetadata(); + metadata = new CdmMetadata(getDataContext()); metadata.setRelativeMetadata(relativeMetadata); // As no time system is defined in CDM because all dates are given in UTC, diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadata.java index 678663974b..2d9a829aac 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -556,71 +556,71 @@ public void setScreenType(final ScreenType screenType) { this.screenType = screenType; } - /** - * @return the maxCollisionProbability + /** Get max collision probability. + * @return the max collision probability */ public double getMaxCollisionProbability() { return maxCollisionProbability; } - /** - * @param maxCollisionProbability the maxCollisionProbability to set + /** Set max collision probability. + * @param maxCollisionProbability the max collision probability to set */ public void setMaxCollisionProbability(final double maxCollisionProbability) { this.maxCollisionProbability = maxCollisionProbability; } - /** - * @return the maxCollisionProbabilityMethod + /** Get max collision probability method. + * @return the max collision probability method */ public PocMethodFacade getMaxCollisionProbabilityMethod() { return maxCollisionProbabilityMethod; } - /** - * @param pocMethodFacade the maxCollisionProbabilityMethod to set + /** Set max collision probability method. + * @param pocMethodFacade the max collision probability method to set */ public void setMaxCollisionProbabilityMethod(final PocMethodFacade pocMethodFacade) { this.maxCollisionProbabilityMethod = pocMethodFacade; } - /** - * @return the sefiCollisionProbability + /** Get the Space Environment Fragmentation Impact probability. + * @return the Space Environment Fragmentation Impact probability */ public double getSefiCollisionProbability() { return sefiCollisionProbability; } - /** - * @param sefiCollisionProbability the sefiCollisionProbability to set + /** Set the Space Environment Fragmentation Impact probability. + * @param sefiCollisionProbability the Space Environment Fragmentation Impact probability to set */ public void setSefiCollisionProbability(final double sefiCollisionProbability) { this.sefiCollisionProbability = sefiCollisionProbability; } - /** - * @return the sefiCollisionProbabilityMethod + /** Get the Space Environment Fragmentation Impact probability method. + * @return the Space Environment Fragmentation Impact probability method */ public PocMethodFacade getSefiCollisionProbabilityMethod() { return sefiCollisionProbabilityMethod; } - /** - * @param pocMethodFacade the sefiCollisionProbabilityMethod to set + /** Set the Space Environment Fragmentation Impact probability method. + * @param pocMethodFacade the Space Environment Fragmentation Impact probability method to set */ public void setSefiCollisionProbabilityMethod(final PocMethodFacade pocMethodFacade) { this.sefiCollisionProbabilityMethod = pocMethodFacade; } - /** - * @return the sefiFragmentationModel + /** Get the Space Environment Fragmentation Impact fragmentation model. + * @return the Space Environment Fragmentation Impact fragmentation model */ public String getSefiFragmentationModel() { return sefiFragmentationModel; } - /** - * @param sefiFragmentationModel the sefiFragmentationModel to set + /** Set the Space Environment Fragmentation Impact fragmentation model. + * @param sefiFragmentationModel the Space Environment Fragmentation Impact fragmentation model to set */ public void setSefiFragmentationModel(final String sefiFragmentationModel) { this.sefiFragmentationModel = sefiFragmentationModel; @@ -642,15 +642,15 @@ public void setMahalanobisDistance(final double mahalanobisDistance) { this.mahalanobisDistance = mahalanobisDistance; } - /** - * @return the screenVolumeRadius + /** Get the screen volume radius. + * @return the screen volume radius */ public double getScreenVolumeRadius() { return screenVolumeRadius; } - /** - * @param screenVolumeRadius the screenVolumeRadius to set + /** set the screen volume radius. + * @param screenVolumeRadius the screen volume radius to set */ public void setScreenVolumeRadius(final double screenVolumeRadius) { this.screenVolumeRadius = screenVolumeRadius; @@ -670,6 +670,13 @@ public void setScreenPcThreshold(final double screenPcThreshold) { this.screenPcThreshold = screenPcThreshold; } + /** + * Check screen volume conditions. + *

          + * The method verifies that all keys are present. + * Otherwise, an exception is thrown. + *

          + */ public void checkScreenVolumeConditions() { if (this.getScreenType() == ScreenType.SHAPE) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadataKey.java index 926951120a..5c1b0a7fef 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmRelativeMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -160,7 +160,7 @@ public enum CdmRelativeMetadataKey { SEFI_FRAGMENTATION_MODEL((token, context, container) -> token.processAsNormalizedString(container::setSefiFragmentationModel)), /** ID of previous CDM issued for event identified by CONJUNCTION_ID. */ - PREVIOUS_MESSAGE_ID((token, context, container) -> token.processAsNormalizedString(container::setPreviousMessageId)), + PREVIOUS_MESSAGE_ID((token, context, container) -> token.processAsFreeTextString(container::setPreviousMessageId)), /** UTC epoch of the previous CDM issued for the event identified by CONJUNCTION_ID. */ PREVIOUS_MESSAGE_EPOCH((token, context, container) -> token.processAsDate(container::setPreviousMessageEpoch, context)), diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmSegment.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmSegment.java index a906214a5c..bc724792eb 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmSegment.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmSegment.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmWriter.java index 5827f872be..688243cbc7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CdmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -133,7 +133,7 @@ public void writeRelativeMetadataContent(final Generator generator, final double } generator.writeComments(relativeMetadata.getComment()); - generator.writeEntry(CdmRelativeMetadataKey.TCA.name(), getTimeConverter(), relativeMetadata.getTca(), true); + generator.writeEntry(CdmRelativeMetadataKey.TCA.name(), getTimeConverter(), relativeMetadata.getTca(), true, true); generator.writeEntry(CdmRelativeMetadataKey.MISS_DISTANCE.name(), relativeMetadata.getMissDistance(), Unit.METRE, true); generator.writeEntry(CdmRelativeMetadataKey.RELATIVE_SPEED.name(), relativeMetadata.getRelativeSpeed(), Units.M_PER_S, false); @@ -167,18 +167,18 @@ public void writeRelativeMetadataContent(final Generator generator, final double } generator.writeEntry(CdmRelativeMetadataKey.START_SCREEN_PERIOD.name(), getTimeConverter(), - relativeMetadata.getStartScreenPeriod(), false); + relativeMetadata.getStartScreenPeriod(), true, false); generator.writeEntry(CdmRelativeMetadataKey.STOP_SCREEN_PERIOD.name(), getTimeConverter(), - relativeMetadata.getStopScreenPeriod(), false); + relativeMetadata.getStopScreenPeriod(), true, false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_VOLUME_FRAME.name(), relativeMetadata.getScreenVolumeFrame(), false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_VOLUME_SHAPE.name(), relativeMetadata.getScreenVolumeShape(), false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_VOLUME_X.name(), relativeMetadata.getScreenVolumeX(), Unit.METRE, false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_VOLUME_Y.name(), relativeMetadata.getScreenVolumeY(), Unit.METRE, false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_VOLUME_Z.name(), relativeMetadata.getScreenVolumeZ(), Unit.METRE, false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_ENTRY_TIME.name(), getTimeConverter(), - relativeMetadata.getScreenEntryTime(), false); + relativeMetadata.getScreenEntryTime(), true, false); generator.writeEntry(CdmRelativeMetadataKey.SCREEN_EXIT_TIME.name(), getTimeConverter(), - relativeMetadata.getScreenExitTime(), false); + relativeMetadata.getScreenExitTime(), true, false); generator.writeEntry(CdmRelativeMetadataKey.COLLISION_PROBABILITY.name(), relativeMetadata.getCollisionProbability(), Unit.ONE, false); if (relativeMetadata.getCollisionProbaMethod() != null) { generator.writeEntry(CdmRelativeMetadataKey.COLLISION_PROBABILITY_METHOD.name(), diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CovarianceMethod.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CovarianceMethod.java index bf14b54a7e..ee2d20189e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/CovarianceMethod.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/CovarianceMethod.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/Maneuvrable.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/Maneuvrable.java index 553a2e0607..eec203d3f5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/Maneuvrable.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/Maneuvrable.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,19 +34,33 @@ public enum Maneuvrable { /** Value of the enum .*/ private String value; + /** + * Constructor. + * @param value String value of the enum + */ Maneuvrable(final String value) { this.value = value; } + /** Get the String representation of the enum. + * @return the String representation of the enum + */ public String getValue() { return value; } + /** {@inheritDoc}. */ @Override public String toString() { return this.getValue(); } + /** Get the enum entry corresponding to the given String. + * @param keyValue input Sring value + * @return the corresponding enum entry + * @throws IllegalArgumentException if there is no enum entry corresponding + * to the given String value + */ public static Maneuvrable getEnum(final String keyValue) { for (Maneuvrable v : values()) { if (v.getValue().equalsIgnoreCase(keyValue)) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParameters.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParameters.java index 060a32e0d3..d7d9385902 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParameters.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersKey.java index 4378582886..53647b74f9 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersWriter.java index 31b6f02b34..778df3fe86 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ODParametersWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -56,8 +56,8 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeComments(ODparameters.getComments()); // OD parameters - generator.writeEntry(ODParametersKey.TIME_LASTOB_START.name(), timeConverter, ODparameters.getTimeLastObsStart(), false); - generator.writeEntry(ODParametersKey.TIME_LASTOB_END.name(), timeConverter, ODparameters.getTimeLastObsEnd(), false); + generator.writeEntry(ODParametersKey.TIME_LASTOB_START.name(), timeConverter, ODparameters.getTimeLastObsStart(), true, false); + generator.writeEntry(ODParametersKey.TIME_LASTOB_END.name(), timeConverter, ODparameters.getTimeLastObsEnd(), true, false); generator.writeEntry(ODParametersKey.RECOMMENDED_OD_SPAN.name(), ODparameters.getRecommendedOdSpan(), Unit.DAY, false); generator.writeEntry(ODParametersKey.ACTUAL_OD_SPAN.name(), ODparameters.getActualOdSpan(), Unit.DAY, false); generator.writeEntry(ODParametersKey.OBS_AVAILABLE.name(), ODparameters.getObsAvailable(), false); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovariance.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovariance.java index a827c97233..b9c7e7c5a0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovariance.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovariance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -56,27 +56,27 @@ public RTNCovariance() { public void validate(final double version) { super.validate(version); // We only check values that are mandatory in a cdm file - checkNotNaN(getCrr(), RTNCovarianceKey.CR_R); - checkNotNaN(getCtr(), RTNCovarianceKey.CT_R); - checkNotNaN(getCtt(), RTNCovarianceKey.CT_T); - checkNotNaN(getCnr(), RTNCovarianceKey.CN_R); - checkNotNaN(getCnt(), RTNCovarianceKey.CN_T); - checkNotNaN(getCnn(), RTNCovarianceKey.CN_N); - checkNotNaN(getCrdotr(), RTNCovarianceKey.CRDOT_R); - checkNotNaN(getCrdott(), RTNCovarianceKey.CRDOT_T); - checkNotNaN(getCrdotn(), RTNCovarianceKey.CRDOT_N); - checkNotNaN(getCrdotrdot(), RTNCovarianceKey.CRDOT_RDOT); - checkNotNaN(getCtdotr(), RTNCovarianceKey.CTDOT_R); - checkNotNaN(getCtdott(), RTNCovarianceKey.CTDOT_T); - checkNotNaN(getCtdotn(), RTNCovarianceKey.CTDOT_N); - checkNotNaN(getCtdotrdot(), RTNCovarianceKey.CTDOT_RDOT); - checkNotNaN(getCtdottdot(), RTNCovarianceKey.CTDOT_TDOT); - checkNotNaN(getCndotr(), RTNCovarianceKey.CNDOT_R); - checkNotNaN(getCndott(), RTNCovarianceKey.CNDOT_T); - checkNotNaN(getCndotn(), RTNCovarianceKey.CNDOT_N); - checkNotNaN(getCndotrdot(), RTNCovarianceKey.CNDOT_RDOT); - checkNotNaN(getCndottdot(), RTNCovarianceKey.CNDOT_TDOT); - checkNotNaN(getCndotndot(), RTNCovarianceKey.CNDOT_NDOT); + checkNotNaN(getCrr(), RTNCovarianceKey.CR_R.name()); + checkNotNaN(getCtr(), RTNCovarianceKey.CT_R.name()); + checkNotNaN(getCtt(), RTNCovarianceKey.CT_T.name()); + checkNotNaN(getCnr(), RTNCovarianceKey.CN_R.name()); + checkNotNaN(getCnt(), RTNCovarianceKey.CN_T.name()); + checkNotNaN(getCnn(), RTNCovarianceKey.CN_N.name()); + checkNotNaN(getCrdotr(), RTNCovarianceKey.CRDOT_R.name()); + checkNotNaN(getCrdott(), RTNCovarianceKey.CRDOT_T.name()); + checkNotNaN(getCrdotn(), RTNCovarianceKey.CRDOT_N.name()); + checkNotNaN(getCrdotrdot(), RTNCovarianceKey.CRDOT_RDOT.name()); + checkNotNaN(getCtdotr(), RTNCovarianceKey.CTDOT_R.name()); + checkNotNaN(getCtdott(), RTNCovarianceKey.CTDOT_T.name()); + checkNotNaN(getCtdotn(), RTNCovarianceKey.CTDOT_N.name()); + checkNotNaN(getCtdotrdot(), RTNCovarianceKey.CTDOT_RDOT.name()); + checkNotNaN(getCtdottdot(), RTNCovarianceKey.CTDOT_TDOT.name()); + checkNotNaN(getCndotr(), RTNCovarianceKey.CNDOT_R.name()); + checkNotNaN(getCndott(), RTNCovarianceKey.CNDOT_T.name()); + checkNotNaN(getCndotn(), RTNCovarianceKey.CNDOT_N.name()); + checkNotNaN(getCndotrdot(), RTNCovarianceKey.CNDOT_RDOT.name()); + checkNotNaN(getCndottdot(), RTNCovarianceKey.CNDOT_TDOT.name()); + checkNotNaN(getCndotndot(), RTNCovarianceKey.CNDOT_NDOT.name()); } /** Set an entry in the RTN covariance matrix. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceKey.java index dc3c116a5c..d96768842b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceWriter.java index 85a07b56df..5f4bb659a7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/RTNCovarianceWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenType.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenType.java index a03ec25b5d..87121d6ae8 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeFrame.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeFrame.java index 48817ac8b0..a7c9c05d5b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeFrame.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeShape.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeShape.java index f9371f2295..34b6ec3014 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeShape.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/ScreenVolumeShape.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovariance.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovariance.java index 8bd7191db6..ce1f72babf 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovariance.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovariance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -59,7 +59,7 @@ public void validate(final double version) { // We only check values that are mandatory in a cdm file for (int i = 0; i < getCsig3eigvec3().length; i++) { - checkNotNaN(getCsig3eigvec3()[i], SigmaEigenvectorsCovarianceKey.CSIG3EIGVEC3); + checkNotNaN(getCsig3eigvec3()[i], SigmaEigenvectorsCovarianceKey.CSIG3EIGVEC3.name()); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovarianceKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovarianceKey.java index 7bce8bc794..bfc0ab9275 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovarianceKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/SigmaEigenvectorsCovarianceKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.lexical.TokenType; +import org.orekit.utils.units.Unit; /** Keys for {@link SigmaEigenvectorsCovariance covariance format} entries. */ @@ -30,7 +31,8 @@ public enum SigmaEigenvectorsCovarianceKey { /** The 3x3 positional covariance one-sigma dispersions corresponding to the major, intermediate and minor eigenvalues, * followed by the associated eigenvectors. */ - CSIG3EIGVEC3((token, context, container) -> token.processAsDoubleArray(container::setCsig3eigvec3)); + CSIG3EIGVEC3((token, context, container) -> token.processAsDoubleArray(Unit.NONE, context.getParsedUnitsBehavior(), + container::setCsig3eigvec3)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVector.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVector.java index b59da3b9a5..e3a4ef7ed1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVector.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -60,12 +60,12 @@ public StateVector() { @Override public void validate(final double version) { super.validate(version); - checkNotNaN(x, StateVectorKey.X); - checkNotNaN(y, StateVectorKey.Y); - checkNotNaN(z, StateVectorKey.Z); - checkNotNaN(xDot, StateVectorKey.X_DOT); - checkNotNaN(yDot, StateVectorKey.Y_DOT); - checkNotNaN(zDot, StateVectorKey.Z_DOT); + checkNotNaN(x, StateVectorKey.X.name()); + checkNotNaN(y, StateVectorKey.Y.name()); + checkNotNaN(z, StateVectorKey.Z.name()); + checkNotNaN(xDot, StateVectorKey.X_DOT.name()); + checkNotNaN(yDot, StateVectorKey.Y_DOT.name()); + checkNotNaN(zDot, StateVectorKey.Z_DOT.name()); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorKey.java index bd327076ea..a9b09268fb 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorWriter.java index 17c4c2fd13..5f902ea42f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/StateVectorWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovariance.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovariance.java index 635d385558..e037aa35f3 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovariance.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovariance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -71,27 +71,27 @@ public void validate(final double version) { } // We only check values that are mandatory in a cdm file - checkNotNaN(getCxx(), XYZCovarianceKey.CX_X); - checkNotNaN(getCyx(), XYZCovarianceKey.CY_X); - checkNotNaN(getCyy(), XYZCovarianceKey.CY_Y); - checkNotNaN(getCzx(), XYZCovarianceKey.CZ_X); - checkNotNaN(getCzy(), XYZCovarianceKey.CZ_Y); - checkNotNaN(getCzz(), XYZCovarianceKey.CZ_Z); - checkNotNaN(getCxdotx(), XYZCovarianceKey.CXDOT_X); - checkNotNaN(getCxdoty(), XYZCovarianceKey.CXDOT_Y); - checkNotNaN(getCxdotz(), XYZCovarianceKey.CXDOT_Z); - checkNotNaN(getCxdotxdot(), XYZCovarianceKey.CXDOT_XDOT); - checkNotNaN(getCydotx(), XYZCovarianceKey.CYDOT_X); - checkNotNaN(getCydoty(), XYZCovarianceKey.CYDOT_Y); - checkNotNaN(getCydotz(), XYZCovarianceKey.CYDOT_Z); - checkNotNaN(getCydotxdot(), XYZCovarianceKey.CYDOT_XDOT); - checkNotNaN(getCydotydot(), XYZCovarianceKey.CYDOT_YDOT); - checkNotNaN(getCzdotx(), XYZCovarianceKey.CZDOT_X); - checkNotNaN(getCzdoty(), XYZCovarianceKey.CZDOT_Y); - checkNotNaN(getCzdotz(), XYZCovarianceKey.CZDOT_Z); - checkNotNaN(getCzdotxdot(), XYZCovarianceKey.CZDOT_XDOT); - checkNotNaN(getCzdotydot(), XYZCovarianceKey.CZDOT_YDOT); - checkNotNaN(getCzdotzdot(), XYZCovarianceKey.CZDOT_ZDOT); + checkNotNaN(getCxx(), XYZCovarianceKey.CX_X.name()); + checkNotNaN(getCyx(), XYZCovarianceKey.CY_X.name()); + checkNotNaN(getCyy(), XYZCovarianceKey.CY_Y.name()); + checkNotNaN(getCzx(), XYZCovarianceKey.CZ_X.name()); + checkNotNaN(getCzy(), XYZCovarianceKey.CZ_Y.name()); + checkNotNaN(getCzz(), XYZCovarianceKey.CZ_Z.name()); + checkNotNaN(getCxdotx(), XYZCovarianceKey.CXDOT_X.name()); + checkNotNaN(getCxdoty(), XYZCovarianceKey.CXDOT_Y.name()); + checkNotNaN(getCxdotz(), XYZCovarianceKey.CXDOT_Z.name()); + checkNotNaN(getCxdotxdot(), XYZCovarianceKey.CXDOT_XDOT.name()); + checkNotNaN(getCydotx(), XYZCovarianceKey.CYDOT_X.name()); + checkNotNaN(getCydoty(), XYZCovarianceKey.CYDOT_Y.name()); + checkNotNaN(getCydotz(), XYZCovarianceKey.CYDOT_Z.name()); + checkNotNaN(getCydotxdot(), XYZCovarianceKey.CYDOT_XDOT.name()); + checkNotNaN(getCydotydot(), XYZCovarianceKey.CYDOT_YDOT.name()); + checkNotNaN(getCzdotx(), XYZCovarianceKey.CZDOT_X.name()); + checkNotNaN(getCzdoty(), XYZCovarianceKey.CZDOT_Y.name()); + checkNotNaN(getCzdotz(), XYZCovarianceKey.CZDOT_Z.name()); + checkNotNaN(getCzdotxdot(), XYZCovarianceKey.CZDOT_XDOT.name()); + checkNotNaN(getCzdotydot(), XYZCovarianceKey.CZDOT_YDOT.name()); + checkNotNaN(getCzdotzdot(), XYZCovarianceKey.CZDOT_ZDOT.name()); } /** Set an entry in the XYZ covariance matrix. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovarianceKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovarianceKey.java index cac4b3dccf..662f22dff6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovarianceKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/XYZCovarianceKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/XmlSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/XmlSubStructureKey.java index ecbf78c794..2e6534d91a 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/XmlSubStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/XmlSubStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/cdm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/cdm/package-info.java index 8e3a1868ea..4f0d0f9769 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/cdm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/cdm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovariance.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovariance.java index cb58a1db49..75ea5d2ce6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovariance.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovariance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -69,7 +69,7 @@ public CartesianCovariance(final Supplier defaultFrameSupplier) { @Override public void validate(final double version) { super.validate(version); - checkNotNull(epoch, CartesianCovarianceKey.EPOCH); + checkNotNull(epoch, CartesianCovarianceKey.EPOCH.name()); for (int i = 0; i < covarianceMatrix.getRowDimension(); ++i) { for (int j = 0; j <= i; ++j) { if (Double.isNaN(covarianceMatrix.getEntry(i, j))) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceKey.java index 04d1187553..411e9734e8 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceWriter.java index 3ae6b0dffb..1e8e0c90ef 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/CartesianCovarianceWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataKey.java index 5daf5d4214..acd7426f8d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import org.orekit.files.ccsds.utils.lexical.ParseToken; -/** Keys for {@link CommonMetadata common ODM container} entries. +/** Keys for {@link OdmCommonMetadata common ODM container} entries. * @author Luc Maisonobe * @since 11.0 */ @@ -40,7 +40,7 @@ public enum CommonMetadataKey { REF_FRAME_EPOCH((token, context, container) -> token.processAsUppercaseString(container::setFrameEpochString)); /** Processing method. */ - private final TokenProcessor processor; + private final transient TokenProcessor processor; /** Simple constructor. * @param processor processing method @@ -55,7 +55,7 @@ public enum CommonMetadataKey { * @param container container to fill * @return true of token was accepted */ - public boolean process(final ParseToken token, final ContextBinding context, final CommonMetadata container) { + public boolean process(final ParseToken token, final ContextBinding context, final OdmCommonMetadata container) { return processor.process(token, context, container); } @@ -67,7 +67,7 @@ interface TokenProcessor { * @param container container to fill * @return true of token was accepted */ - boolean process(ParseToken token, ContextBinding context, CommonMetadata container); + boolean process(ParseToken token, ContextBinding context, OdmCommonMetadata container); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataWriter.java index 23ca0c7103..9bf94ec151 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadataWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,7 +33,7 @@ public class CommonMetadataWriter extends AbstractWriter { /** Metadata. */ - private final CommonMetadata metadata; + private final OdmCommonMetadata metadata; /** Converter for dates. */ private final TimeConverter timeConverter; @@ -42,7 +42,7 @@ public class CommonMetadataWriter extends AbstractWriter { * @param metadata metadata to write * @param timeConverter converter for dates */ - public CommonMetadataWriter(final CommonMetadata metadata, final TimeConverter timeConverter) { + public CommonMetadataWriter(final OdmCommonMetadata metadata, final TimeConverter timeConverter) { super(XmlStructureKey.metadata.name(), null); this.metadata = metadata; this.timeConverter = timeConverter; @@ -61,7 +61,7 @@ protected void writeContent(final Generator generator) throws IOException { // frames generator.writeEntry(CommonMetadataKey.CENTER_NAME.name(), metadata.getCenter().getName(), null, true); generator.writeEntry(CommonMetadataKey.REF_FRAME.name(), metadata.getReferenceFrame().getName(), null, true); - generator.writeEntry(CommonMetadataKey.REF_FRAME_EPOCH.name(), timeConverter, metadata.getFrameEpoch(), false); + generator.writeEntry(CommonMetadataKey.REF_FRAME_EPOCH.name(), timeConverter, metadata.getFrameEpoch(), true, false); // time generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), metadata.getTimeSystem(), true); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElements.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElements.java index e8b0e63ef1..72abc28980 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElements.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.orekit.files.ccsds.section.Data; import org.orekit.frames.Frame; import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.time.AbsoluteDate; /** Container for Keplerian elements. @@ -59,7 +59,7 @@ public class KeplerianElements extends CommentsContainer implements Data { private double anomaly; /** Orbit anomaly type (mean or true). */ - private PositionAngle anomalyType; + private PositionAngleType anomalyType; /** Gravitational coefficient. */ private double mu; @@ -86,12 +86,12 @@ public KeplerianElements() { @Override public void validate(final double version) { super.validate(version); - checkNotNull(epoch, StateVectorKey.EPOCH); - checkNotNaN(e, KeplerianElementsKey.ECCENTRICITY); - checkNotNaN(i, KeplerianElementsKey.INCLINATION); - checkNotNaN(raan, KeplerianElementsKey.RA_OF_ASC_NODE); - checkNotNaN(pa, KeplerianElementsKey.ARG_OF_PERICENTER); - checkNotNaN(anomaly, KeplerianElementsKey.MEAN_ANOMALY); + checkNotNull(epoch, StateVectorKey.EPOCH.name()); + checkNotNaN(e, KeplerianElementsKey.ECCENTRICITY.name()); + checkNotNaN(i, KeplerianElementsKey.INCLINATION.name()); + checkNotNaN(raan, KeplerianElementsKey.RA_OF_ASC_NODE.name()); + checkNotNaN(pa, KeplerianElementsKey.ARG_OF_PERICENTER.name()); + checkNotNaN(anomaly, KeplerianElementsKey.MEAN_ANOMALY.name()); } /** Get epoch of state vector, Keplerian elements and covariance matrix data. @@ -216,14 +216,14 @@ public void setAnomaly(final double anomaly) { /** Get the type of anomaly (true or mean). * @return the type of anomaly */ - public PositionAngle getAnomalyType() { + public PositionAngleType getAnomalyType() { return anomalyType; } /** Set the type of anomaly. * @param anomalyType the type of anomaly to be set */ - public void setAnomalyType(final PositionAngle anomalyType) { + public void setAnomalyType(final PositionAngleType anomalyType) { refuseFurtherComments(); this.anomalyType = anomalyType; } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElementsKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElementsKey.java index cfca7ef397..36211b0c5b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElementsKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/KeplerianElementsKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.lexical.TokenType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.utils.units.Unit; @@ -63,7 +63,7 @@ public enum KeplerianElementsKey { select(token.getUnits(), Unit.DEGREE). toSI(token.getContentAsDouble()); container.setAnomaly(angle); - container.setAnomalyType(PositionAngle.TRUE); + container.setAnomalyType(PositionAngleType.TRUE); } return true; }), @@ -76,7 +76,7 @@ public enum KeplerianElementsKey { select(token.getUnits(), Unit.DEGREE). toSI(token.getContentAsDouble()); container.setAnomaly(angle); - container.setAnomalyType(PositionAngle.MEAN); + container.setAnomalyType(PositionAngleType.MEAN); } return true; }), diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmCommonMetadata.java similarity index 97% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadata.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/OdmCommonMetadata.java index d74f6bc453..c864e55fc2 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/CommonMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmCommonMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -32,7 +32,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class CommonMetadata extends OdmMetadata { +public class OdmCommonMetadata extends OdmMetadata { /** Object identifier of the object for which the orbit state is provided. */ private String objectID; @@ -54,7 +54,7 @@ public class CommonMetadata extends OdmMetadata { /** Simple constructor. */ - public CommonMetadata() { + public OdmCommonMetadata() { super(null); } @@ -62,10 +62,10 @@ public CommonMetadata() { @Override public void validate(final double version) { super.validate(version); - checkNotNull(getObjectName(), OdmMetadataKey.OBJECT_NAME); - checkNotNull(objectID, CommonMetadataKey.OBJECT_ID); - checkNotNull(center, CommonMetadataKey.CENTER_NAME); - checkNotNull(referenceFrame, CommonMetadataKey.REF_FRAME); + checkNotNull(getObjectName(), OdmMetadataKey.OBJECT_NAME.name()); + checkNotNull(objectID, CommonMetadataKey.OBJECT_ID.name()); + checkNotNull(center, CommonMetadataKey.CENTER_NAME.name()); + checkNotNull(referenceFrame, CommonMetadataKey.REF_FRAME.name()); } /** Finalize the metadata. @@ -73,7 +73,7 @@ public void validate(final double version) { * ODM standard enforces {@code TIME_SYSTEM} to appear *after* * {@code REF_FRAME_EPOCH}, despite it is needed to interpret it. * We have to wait until parsing end to finalize this date. - *

          + *

          * @param context context binding */ public void finalizeMetadata(final ContextBinding context) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmHeader.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmHeader.java new file mode 100644 index 0000000000..da825ada46 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmHeader.java @@ -0,0 +1,35 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.odm; + +import org.orekit.files.ccsds.section.Header; + +/** + * Header of a CCSDS Orbit Data Message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class OdmHeader extends Header { + + /** + * Constructor. + */ + public OdmHeader() { + super(3.0, 3.0); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadata.java index d3b4ba35df..f7892735d0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadataKey.java index be1ebd2a8f..05f38eeac2 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmParser.java index cb04ca256f..079f07845c 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/OdmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,11 +16,15 @@ */ package org.orekit.files.ccsds.ndm.odm; +import java.util.List; +import java.util.function.Function; + import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.ndm.NdmConstituent; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.parsing.AbstractConstituentParser; import org.orekit.time.AbsoluteDate; import org.orekit.utils.IERSConventions; @@ -40,7 +44,8 @@ * @author Luc Maisonobe * @since 11.0 */ -public abstract class OdmParser, P extends OdmParser> extends AbstractConstituentParser { +public abstract class OdmParser, P extends OdmParser> + extends AbstractConstituentParser { /** Reference date for Mission Elapsed Time or Mission Relative Time time systems. */ private final AbsoluteDate missionReferenceDate; @@ -63,12 +68,15 @@ public abstract class OdmParser, P extends OdmPar * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems * @param mu gravitational coefficient * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ protected OdmParser(final String root, final String formatVersionKey, final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, final AbsoluteDate missionReferenceDate, - final double mu, final ParsedUnitsBehavior parsedUnitsBehavior) { - super(root, formatVersionKey, conventions, simpleEOP, dataContext, parsedUnitsBehavior); + final double mu, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(root, formatVersionKey, conventions, simpleEOP, dataContext, parsedUnitsBehavior, filters); this.missionReferenceDate = missionReferenceDate; this.muSet = mu; this.muParsed = Double.NaN; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParameters.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParameters.java index dc3b9eebba..827b8aa419 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParameters.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -54,7 +54,7 @@ public SpacecraftParameters() { /** {@inheritDoc} */ @Override public void validate(final double version) { - checkNotNaN(mass, SpacecraftParametersKey.MASS); + checkNotNaN(mass, SpacecraftParametersKey.MASS.name()); } /** Get the spacecraft mass. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersKey.java index fe2404600e..b3666f665b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersWriter.java index f69c1c1c0c..528ca832bb 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/SpacecraftParametersWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVector.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVector.java index 7132e60c6d..95ef493ba4 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVector.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -59,7 +59,7 @@ public StateVector() { @Override public void validate(final double version) { super.validate(version); - checkNotNull(epoch, StateVectorKey.EPOCH); + checkNotNull(epoch, StateVectorKey.EPOCH.name()); if (Double.isNaN(position[0] + position[1] + position[2])) { throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}"); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorKey.java index c99ec75270..4df2497411 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorWriter.java index 7442639870..f1f6547419 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/StateVectorWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -58,7 +58,7 @@ protected void writeContent(final Generator generator) throws IOException { // state vector block final TimeStampedPVCoordinates pv = stateVector.toTimeStampedPVCoordinates(); generator.writeComments(stateVector.getComments()); - generator.writeEntry(StateVectorKey.EPOCH.name(), timeConverter, pv.getDate(), true); + generator.writeEntry(StateVectorKey.EPOCH.name(), timeConverter, pv.getDate(), true, true); generator.writeEntry(StateVectorKey.X.name(), pv.getPosition().getX(), Unit.KILOMETRE, true); generator.writeEntry(StateVectorKey.Y.name(), pv.getPosition().getY(), Unit.KILOMETRE, true); generator.writeEntry(StateVectorKey.Z.name(), pv.getPosition().getZ(), Unit.KILOMETRE, true); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefined.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefined.java index 03aeec35fe..dd797939a6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefined.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefined.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefinedWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefinedWriter.java index 02099d3017..180c081d31 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefinedWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/UserDefinedWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceIndexer.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceIndexer.java index 4463f6cf32..974f70b4f7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceIndexer.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceIndexer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/EphemerisOcmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/EphemerisOcmWriter.java new file mode 100644 index 0000000000..892b231652 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/EphemerisOcmWriter.java @@ -0,0 +1,254 @@ +/* Copyright 2016 Applied Defense Solutions (ADS) + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * ADS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.odm.ocm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; +import org.orekit.files.ccsds.section.XmlStructureKey; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.files.ccsds.utils.generation.KvnGenerator; +import org.orekit.files.ccsds.utils.generation.XmlGenerator; +import org.orekit.files.general.EphemerisFile; +import org.orekit.files.general.EphemerisFile.SatelliteEphemeris; +import org.orekit.files.general.EphemerisFileWriter; +import org.orekit.frames.Frame; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** An {@link EphemerisFileWriter} generating {@link Ocm OCM} files. + *

          + * This writer is intended to write only trajectory state history blocks. + * It does not writes physical properties, covariance data, maneuver data, + * perturbations parameters, orbit determination or user-defined parameters. + * If these blocks are needed, then {@link OcmWriter OcmWriter} must be + * used as it handles all OCM data blocks. + *

          + *

          + * The trajectory blocks metadata identifiers ({@code TRAJ_ID}, + * {@code TRAJ_PREV_ID}, {@code TRAJ_NEXT_ID}) are updated automatically + * using {@link TrajectoryStateHistoryMetadata#incrementTrajID(String)}, + * so users should generally only set {@link TrajectoryStateHistoryMetadata#setTrajID(String)} + * in the template. + *

          + * @author Luc Maisonobe + * @since 12.0 + * @see OcmWriter + * @see StreamingOcmWriter + */ +public class EphemerisOcmWriter implements EphemerisFileWriter { + + /** Underlying writer. */ + private final OcmWriter writer; + + /** Header. */ + private final OdmHeader header; + + /** File metadata. */ + private final OcmMetadata metadata; + + /** Current trajectory metadata. */ + private final TrajectoryStateHistoryMetadata trajectoryMetadata; + + /** File format to use. */ + private final FileFormat fileFormat; + + /** Output name for error messages. */ + private final String outputName; + + /** Column number for aligning units. */ + private final int unitsColumn; + + /** Maximum offset for relative dates. */ + private final double maxRelativeOffset; + + /** Central body. + * @since 12.0 + */ + private final OneAxisEllipsoid body; + + /** + * Constructor used to create a new OCM writer configured with the necessary parameters + * to successfully fill in all required fields that aren't part of a standard object. + *

          + * If the mandatory header entries are not present (or if header is null), + * built-in defaults will be used + *

          + *

          + * The writer is built from the complete header and partial metadata. The template + * metadata is used to initialize and independent local copy, that will be updated + * as new segments are written (with at least the segment start and stop will change, + * but some other parts may change too). The {@code template} argument itself is not + * changed. + *

          + * @param writer underlying writer + * @param header file header (may be null) + * @param metadata file metadata + * @param template template for trajectory metadata + * @param fileFormat file format to use + * @param outputName output name for error messages + * @param maxRelativeOffset maximum offset in seconds to use relative dates + * (if a date is too far from reference, it will be displayed as calendar elements) + * @param unitsColumn columns number for aligning units (if negative or zero, units are not output) + */ + public EphemerisOcmWriter(final OcmWriter writer, + final OdmHeader header, final OcmMetadata metadata, + final TrajectoryStateHistoryMetadata template, + final FileFormat fileFormat, final String outputName, + final double maxRelativeOffset, final int unitsColumn) { + this.writer = writer; + this.header = header; + this.metadata = metadata.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); + this.trajectoryMetadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); + this.fileFormat = fileFormat; + this.outputName = outputName; + this.maxRelativeOffset = maxRelativeOffset; + this.unitsColumn = unitsColumn; + this.body = Double.isNaN(writer.getEquatorialRadius()) ? + null : + new OneAxisEllipsoid(writer.getEquatorialRadius(), + writer.getFlattening(), + template.getTrajReferenceFrame().asFrame()); + } + + /** {@inheritDoc} + *

          + * As {@code EphemerisFile.SatelliteEphemeris} does not have all the entries + * from {@link OcmMetadata}, the only values that will be extracted from the + * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation + * method and interpolation degree. The missing values (like object name, local spacecraft + * body frame...) will be inherited from the template metadata set at writer + * {@link #EphemerisOcmWriter(OcmWriter, OdmHeader, OcmMetadata, TrajectoryStateHistoryMetadata, + * FileFormat, String, double, int) construction}. + *

          + */ + @Override + public > + void write(final Appendable appendable, final EphemerisFile ephemerisFile) + throws IOException { + + if (appendable == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer"); + } + + if (ephemerisFile == null) { + return; + } + + final String name; + if (metadata.getObjectName() != null) { + name = metadata.getObjectName(); + } else if (metadata.getInternationalDesignator() != null) { + name = metadata.getInternationalDesignator(); + } else if (metadata.getObjectDesignator() != null) { + name = metadata.getObjectDesignator(); + } else { + name = Ocm.UNKNOWN_OBJECT; + } + final SatelliteEphemeris satEphem = ephemerisFile.getSatellites().get(name); + if (satEphem == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND, + name, "ephemerisFile"); + } + + // Get trajectory blocks to output. + final List blocks = satEphem.getSegments(); + if (blocks.isEmpty()) { + // No data -> No output + return; + } + + try (Generator generator = fileFormat == FileFormat.KVN ? + new KvnGenerator(appendable, OcmWriter.KVN_PADDING_WIDTH, outputName, + maxRelativeOffset, unitsColumn) : + new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, + maxRelativeOffset, unitsColumn > 0, null)) { + + writer.writeHeader(generator, header); + + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(XmlStructureKey.segment.name()); + } + + // write single segment metadata + metadata.setStartTime(blocks.get(0).getStart()); + metadata.setStopTime(blocks.get(blocks.size() - 1).getStop()); + new OcmMetadataWriter(metadata, writer.getTimeConverter()).write(generator); + + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(XmlStructureKey.data.name()); + } + + // Loop on trajectory blocks + double lastZ = Double.NaN; + for (final S block : blocks) { + + // prepare metadata + trajectoryMetadata.setTrajNextID(TrajectoryStateHistoryMetadata.incrementTrajID(trajectoryMetadata.getTrajID())); + trajectoryMetadata.setUseableStartTime(block.getStart()); + trajectoryMetadata.setUseableStopTime(block.getStop()); + trajectoryMetadata.setInterpolationDegree(block.getInterpolationSamples() - 1); + + // prepare data + final OrbitElementsType type = trajectoryMetadata.getTrajType(); + final Frame frame = trajectoryMetadata.getTrajReferenceFrame().asFrame(); + int crossings = 0; + final List states = new ArrayList<>(block.getCoordinates().size()); + for (final C pv : block.getCoordinates()) { + if (lastZ < 0.0 && pv.getPosition().getZ() >= 0.0) { + // we crossed ascending node + ++crossings; + } + lastZ = pv.getPosition().getZ(); + states.add(new TrajectoryState(type, pv.getDate(), type.toRawElements(pv, frame, body, block.getMu()))); + } + final TrajectoryStateHistory history = new TrajectoryStateHistory(trajectoryMetadata, states, + body, block.getMu()); + + // write trajectory block + final TrajectoryStateHistoryWriter trajectoryWriter = + new TrajectoryStateHistoryWriter(history, writer.getTimeConverter()); + trajectoryWriter.write(generator); + + // update the trajectory IDs + trajectoryMetadata.setTrajPrevID(trajectoryMetadata.getTrajID()); + trajectoryMetadata.setTrajID(trajectoryMetadata.getTrajNextID()); + + if (trajectoryMetadata.getOrbRevNum() >= 0) { + // update the orbits revolution number + trajectoryMetadata.setOrbRevNum(trajectoryMetadata.getOrbRevNum() + crossings); + } + + } + + if (generator.getFormat() == FileFormat.XML) { + generator.exitSection(); // exit data + generator.exitSection(); // exit segment + } + + writer.writeFooter(generator); + + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManBasis.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManBasis.java index 279b7dfca0..2b0dfa3d88 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManBasis.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManBasis.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverFieldType.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverFieldType.java index 97de1ef792..487aae6f2e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverFieldType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverFieldType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -245,7 +245,7 @@ public Unit getUnit() { * @param parsedUnit unit to check */ public void checkUnit(final Unit parsedUnit) { - if ((unit == Unit.NONE) ^ (parsedUnit == Unit.NONE) || + if (unit == Unit.NONE ^ parsedUnit == Unit.NONE || !(unit.sameDimension(parsedUnit) && Precision.equals(unit.getScale(), parsedUnit.getScale(), 1))) { throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS, unit.getName(), parsedUnit.getName()); @@ -275,7 +275,7 @@ private static double toSI(final String field, final Unit unit) { * @param lineNumber line number at which the field occurs * @param fileName name of the file in which the field occurs */ - public void process(final String field, final ContextBinding context, final Maneuver maneuver, + public void process(final String field, final ContextBinding context, final OrbitManeuver maneuver, final int lineNumber, final String fileName) { processor.process(field, unit, context, maneuver, lineNumber, fileName); } @@ -285,7 +285,7 @@ public void process(final String field, final ContextBinding context, final Mane * @param maneuver maneuver containing the field to output * @return output field */ - public String outputField(final TimeConverter converter, final Maneuver maneuver) { + public String outputField(final TimeConverter converter, final OrbitManeuver maneuver) { return writer.output(unit, converter, maneuver); } @@ -299,7 +299,7 @@ interface FieldProcessor { * @param lineNumber line number at which the field occurs * @param fileName name of the file in which the field occurs */ - void process(String field, Unit unit, ContextBinding context, Maneuver maneuver, + void process(String field, Unit unit, ContextBinding context, OrbitManeuver maneuver, int lineNumber, String fileName); } @@ -311,7 +311,7 @@ interface FieldWriter { * @param maneuver maneuver containing the field to output * @return output field */ - String output(Unit unit, TimeConverter converter, Maneuver maneuver); + String output(Unit unit, TimeConverter converter, OrbitManeuver maneuver); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryWriter.java deleted file mode 100644 index 6b8c0cb894..0000000000 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryWriter.java +++ /dev/null @@ -1,151 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.orekit.files.ccsds.ndm.odm.ocm; - -import java.io.IOException; -import java.util.List; - -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.files.ccsds.definitions.DutyCycleType; -import org.orekit.files.ccsds.definitions.TimeConverter; -import org.orekit.files.ccsds.section.AbstractWriter; -import org.orekit.files.ccsds.utils.FileFormat; -import org.orekit.files.ccsds.utils.generation.Generator; -import org.orekit.utils.AccurateFormatter; -import org.orekit.utils.units.Unit; - -/** Writer for maneuvers history data. - * @author Luc Maisonobe - * @since 11.0 - */ -class ManeuverHistoryWriter extends AbstractWriter { - - /** Maneuvers history block. */ - private final ManeuverHistory history; - - /** Converter for dates. */ - private final TimeConverter timeConverter; - - /** Create a writer. - * @param maneuverHistory maneuvers history to write - * @param timeConverter converter for dates - */ - ManeuverHistoryWriter(final ManeuverHistory maneuverHistory, - final TimeConverter timeConverter) { - super(OcmDataSubStructureKey.man.name(), OcmDataSubStructureKey.MAN.name()); - this.history = maneuverHistory; - this.timeConverter = timeConverter; - } - - /** {@inheritDoc} */ - @Override - protected void writeContent(final Generator generator) throws IOException { - - // maneuvers history block - final ManeuverHistoryMetadata metadata = history.getMetadata(); - generator.writeComments(metadata.getComments()); - - // identifiers - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_ID.name(), metadata.getManID(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_PREV_ID.name(), metadata.getManPrevID(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_NEXT_ID.name(), metadata.getManNextID(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_BASIS.name(), metadata.getManBasis(), false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_BASIS_ID.name(), metadata.getManBasisID(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_DEVICE_ID.name(), metadata.getManDeviceID(), null, false); - - // time - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_PREV_EPOCH.name(), timeConverter, metadata.getManPrevEpoch(), false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_NEXT_EPOCH.name(), timeConverter, metadata.getManNextEpoch(), false); - - // references - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_PURPOSE.name(), metadata.getManPurpose(), false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_PRED_SOURCE.name(), metadata.getManPredSource(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_REF_FRAME.name(), metadata.getManReferenceFrame().getName(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_FRAME_EPOCH.name(), timeConverter, metadata.getManFrameEpoch(), false); - if (metadata.getGravitationalAssist() != null) { - generator.writeEntry(ManeuverHistoryMetadataKey.GRAV_ASSIST_NAME.name(), metadata.getGravitationalAssist().getName(), null, false); - } - - // duty cycle - final boolean notContinuous = metadata.getDcType() != DutyCycleType.CONTINUOUS; - final boolean timeAndAngle = metadata.getDcType() == DutyCycleType.TIME_AND_ANGLE; - generator.writeEntry(ManeuverHistoryMetadataKey.DC_TYPE.name(), metadata.getDcType(), false); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_WIN_OPEN.name(), timeConverter, metadata.getDcWindowOpen(), notContinuous); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_WIN_CLOSE.name(), timeConverter, metadata.getDcWindowClose(), notContinuous); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_MIN_CYCLES.name(), metadata.getDcMinCycles(), false); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_MAX_CYCLES.name(), metadata.getDcMaxCycles(), false); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_EXEC_START.name(), timeConverter, metadata.getDcExecStart(), notContinuous); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_EXEC_STOP.name(), timeConverter, metadata.getDcExecStop(), notContinuous); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_REF_TIME.name(), timeConverter, metadata.getDcRefTime(), notContinuous); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_TIME_PULSE_DURATION.name(), metadata.getDcTimePulseDuration(), Unit.SECOND, notContinuous); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_TIME_PULSE_PERIOD.name(), metadata.getDcTimePulsePeriod(), Unit.SECOND, notContinuous); - if (timeAndAngle) { - generator.writeEntry(ManeuverHistoryMetadataKey.DC_REF_DIR.name(), toString(metadata.getDcRefDir()), null, timeAndAngle); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_BODY_FRAME.name(), - metadata.getDcBodyFrame().toString().replace(' ', '_'), - null, timeAndAngle); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_BODY_TRIGGER.name(), toString(metadata.getDcBodyTrigger()), null, timeAndAngle); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_PA_START_ANGLE.name(), metadata.getDcPhaseStartAngle(), Unit.DEGREE, timeAndAngle); - generator.writeEntry(ManeuverHistoryMetadataKey.DC_PA_STOP_ANGLE.name(), metadata.getDcPhaseStopAngle(), Unit.DEGREE, timeAndAngle); - } - - // elements - final List types = metadata.getManComposition(); - final StringBuilder composition = new StringBuilder(); - for (int i = 0; i < types.size(); ++i) { - if (i > 0) { - composition.append(','); - } - composition.append(types.get(i).name()); - } - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_COMPOSITION.name(), composition.toString(), null, false); - generator.writeEntry(ManeuverHistoryMetadataKey.MAN_UNITS.name(), generator.unitsListToString(metadata.getManUnits()), null, false); - - // data - for (final Maneuver maneuver : history.getManeuvers()) { - final StringBuilder line = new StringBuilder(); - for (int i = 0; i < types.size(); ++i) { - if (i > 0) { - line.append(' '); - } - line.append(types.get(i).outputField(timeConverter, maneuver)); - } - if (generator.getFormat() == FileFormat.XML) { - generator.writeEntry(Ocm.MAN_LINE, line.toString(), null, true); - } else { - generator.writeRawData(line); - generator.newLine(); - } - } - } - - /** Convert a vector to a space separated string. - * @param vector vector to convert - * @return orrespondong string - */ - private String toString(final Vector3D vector) { - final StringBuilder builder = new StringBuilder(); - builder.append(AccurateFormatter.format(Unit.ONE.fromSI(vector.getX()))); - builder.append(' '); - builder.append(AccurateFormatter.format(Unit.ONE.fromSI(vector.getY()))); - builder.append(' '); - builder.append(AccurateFormatter.format(Unit.ONE.fromSI(vector.getZ()))); - return builder.toString(); - } - -} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ObjectType.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ObjectType.java index cca3a95499..9328fa5707 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ObjectType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ObjectType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ocm.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ocm.java index 4e371e62ff..4770f4b3c8 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ocm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ocm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.NdmConstituent; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.section.Segment; import org.orekit.files.general.EphemerisFile; import org.orekit.utils.IERSConventions; @@ -33,7 +33,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class Ocm extends NdmConstituent> +public class Ocm extends NdmConstituent> implements EphemerisFile { /** Root element for XML messages. */ @@ -64,7 +64,7 @@ public class Ocm extends NdmConstituent> * @param dataContext used for creating frames, time scales, etc. * @param mu Gravitational coefficient to use for building Cartesian/Keplerian orbits. */ - public Ocm(final Header header, final List> segments, + public Ocm(final OdmHeader header, final List> segments, final IERSConventions conventions, final DataContext dataContext, final double mu) { super(header, segments, conventions, dataContext); @@ -111,7 +111,7 @@ public Map getSatellites() { } else { name = UNKNOWN_OBJECT; } - final List histories = getSegments().get(0).getData().getOTrajectoryBlocks(); + final List histories = getSegments().get(0).getData().getTrajectoryBlocks(); final OcmSatelliteEphemeris ose = new OcmSatelliteEphemeris(name, mu, histories); return Collections.singletonMap(name, ose); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmData.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmData.java index f719eeb3e0..076a6e70f1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ import org.orekit.files.ccsds.section.Data; /** Data container for Orbit Comprehensive Messages. - * @author LOuc Maisonobe + * @author Luc Maisonobe * @since 11.0 */ public class OcmData implements Data { @@ -32,13 +32,13 @@ public class OcmData implements Data { private final List trajectoryBlocks; /** Physical properties logical block. */ - private final PhysicalProperties physicBlock; + private final OrbitPhysicalProperties physicBlock; /** Covariance logical blocks. */ - private final List covarianceBlocks; + private final List covarianceBlocks; /** Maneuvers logical blocks. */ - private final List maneuverBlocks; + private final List maneuverBlocks; /** Perturbations logical block. */ private final Perturbations perturbationsBlock; @@ -59,9 +59,9 @@ public class OcmData implements Data { * @param userDefinedBlock user defined parameters logical block (may be null) */ public OcmData(final List trajectoryBlocks, - final PhysicalProperties physicBlock, - final List covarianceBlocks, - final List maneuverBlocks, + final OrbitPhysicalProperties physicBlock, + final List covarianceBlocks, + final List maneuverBlocks, final Perturbations perturbationsBlock, final OrbitDetermination orbitDeterminationBlock, final UserDefined userDefinedBlock) { @@ -86,12 +86,12 @@ public void validate(final double version) { physicBlock.validate(version); } if (covarianceBlocks != null) { - for (final CovarianceHistory ch : covarianceBlocks) { + for (final OrbitCovarianceHistory ch : covarianceBlocks) { ch.getMetadata().validate(version); } } if (maneuverBlocks != null) { - for (final ManeuverHistory mh : maneuverBlocks) { + for (final OrbitManeuverHistory mh : maneuverBlocks) { mh.getMetadata().validate(version); } } @@ -108,29 +108,30 @@ public void validate(final double version) { /** Get trajectory state histories logical blocks. * @return trajectory state histories logical blocks (may be null) + * @since 12.0 */ - public List getOTrajectoryBlocks() { + public List getTrajectoryBlocks() { return trajectoryBlocks; } /** Get physical properties logical block. * @return physical properties logical block (may be null) */ - public PhysicalProperties getPhysicBlock() { + public OrbitPhysicalProperties getPhysicBlock() { return physicBlock; } /** Get covariance logical blocks. * @return covariance logical blocks (may be null) */ - public List getCovarianceBlocks() { + public List getCovarianceBlocks() { return covarianceBlocks; } /** Get maneuvers logical blocks. * @return maneuvers logical block (may be null) */ - public List getManeuverBlocks() { + public List getManeuverBlocks() { return maneuverBlocks; } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmDataSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmDataSubStructureKey.java index a9e181e3fa..5239265f8f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmDataSubStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmDataSubStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -41,7 +41,7 @@ public enum OcmDataSubStructureKey { COV((token, parser) -> parser.manageCovarianceHistorySection(token.getType() == TokenType.START)), /** Covariance time history section. */ - covar((token, parser) -> parser.manageCovarianceHistorySection(token.getType() == TokenType.START)), + cov((token, parser) -> parser.manageCovarianceHistorySection(token.getType() == TokenType.START)), /** Maneuvers section. */ MAN((token, parser) -> parser.manageManeuversSection(token.getType() == TokenType.START)), @@ -65,7 +65,7 @@ public enum OcmDataSubStructureKey { USER((token, parser) -> parser.manageUserDefinedParametersSection(token.getType() == TokenType.START)), /** User-defined parameters section. */ - userDef((token, parser) -> parser.manageUserDefinedParametersSection(token.getType() == TokenType.START)); + user((token, parser) -> parser.manageUserDefinedParametersSection(token.getType() == TokenType.START)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmElements.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmElements.java new file mode 100644 index 0000000000..3d1d747d41 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmElements.java @@ -0,0 +1,46 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.odm.ocm; + +/** Data elements types used in CCSDS {@link Ocm Orbit Comprehensive Messages}. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum OcmElements { + + /** Trajectory state time history (orbit). */ + ORB, + + /** Physical characteristics. */ + PHYS, + + /** Covariance time history. */ + COV, + + /** Maneuver. */ + MAN, + + /** Perturbations. */ + PERT, + + /** Orbit determination. */ + OD, + + /** User defined. */ + USER; + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadata.java index eec864be56..7d296c185f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,11 +30,15 @@ */ public class OcmMetadata extends OdmMetadata { - /** Default interpolation method for EOP and Space Weather data. */ - private static final String DEFAULT_INTERPOLATION_METHOD = "LINEAR"; + /** Default value for SCLK_OFFSET_AT_EPOCH. + * @since 12.0 + */ + public static final double DEFAULT_SCLK_OFFSET_AT_EPOCH = 0.0; - /** Classification for this message. */ - private String classification; + /** Default value for SCLK_SEC_PER_SI_SEC. + * @since 12.0 + */ + public static final double DEFAULT_SCLK_SEC_PER_SI_SEC = 1.0; /** International designator for the object as assigned by the UN Committee * on Space Research (COSPAR) and the US National Space Science Data Center (NSSDC). */ @@ -92,13 +96,13 @@ public class OcmMetadata extends OdmMetadata { /** Unique ID identifying next message from a given originator. */ private String nextMessageID; - /** Unique identifier of Attitude Data Message linkrd to this Orbit Data Message. */ + /** Unique identifier of Attitude Data Message linked to this Orbit Data Message. */ private String admMessageLink; - /** Unique identifier of Conjunction Data Message linkrd to this Orbit Data Message. */ + /** Unique identifier of Conjunction Data Message linked to this Orbit Data Message. */ private String cdmMessageLink; - /** Unique identifier of Pointing Request Message linkrd to this Orbit Data Message. */ + /** Unique identifier of Pointing Request Message linked to this Orbit Data Message. */ private String prmMessageLink; /** Unique identifier of Reentry Data Messages linked to this Orbit Data Message. */ @@ -129,11 +133,11 @@ public class OcmMetadata extends OdmMetadata { /** Operational status. */ private OpsStatus opsStatus; - /** Orbit catgory. */ + /** Orbit category. */ private OrbitCategory orbitCategory; /** List of elements of information data blocks included in this message. */ - private List ocmDataElements; + private List ocmDataElements; /** Spacecraft clock count at {@link #getEpochT0()}. */ private double sclkOffsetAtEpoch; @@ -181,18 +185,25 @@ public class OcmMetadata extends OdmMetadata { /** Source and version of celestial body (e.g. Sun/Earth/Planetary). */ private String celestialSource; + /** Data context. + * @since 12.0 + */ + private final DataContext dataContext; + /** Create a new meta-data. * @param dataContext data context */ - OcmMetadata(final DataContext dataContext) { + public OcmMetadata(final DataContext dataContext) { // set up the few fields that have default values as per CCSDS standard super(TimeSystem.UTC); - catalogName = "CSPOC"; - sclkOffsetAtEpoch = 0.0; - sclkSecPerSISec = 1.0; + sclkOffsetAtEpoch = DEFAULT_SCLK_OFFSET_AT_EPOCH; + sclkSecPerSISec = DEFAULT_SCLK_SEC_PER_SI_SEC; + timeSpan = Double.NaN; + taimutcT0 = Double.NaN; + ut1mutcT0 = Double.NaN; nextLeapTaimutc = Double.NaN; - interpMethodEOP = DEFAULT_INTERPOLATION_METHOD; + this.dataContext = dataContext; } @@ -203,28 +214,13 @@ public void validate(final double version) { // all of the parameters considered mandatory at ODM level // for OPM, OMM and OEM are in fact optional in OCM // only TIME_SYSTEM and EPOCH_TZERO are mandatory - checkNotNull(getTimeSystem(), MetadataKey.TIME_SYSTEM); - checkNotNull(epochT0, OcmMetadataKey.EPOCH_TZERO); + checkNotNull(getTimeSystem(), MetadataKey.TIME_SYSTEM.name()); + checkNotNull(epochT0, OcmMetadataKey.EPOCH_TZERO.name()); if (nextLeapEpoch != null) { - checkNotNaN(nextLeapTaimutc, OcmMetadataKey.NEXT_LEAP_TAIMUTC); + checkNotNaN(nextLeapTaimutc, OcmMetadataKey.NEXT_LEAP_TAIMUTC.name()); } } - /** Get the message classification. - * @return message classification. - */ - public String getClassification() { - return classification; - } - - /** Set the message classification. - * @param classification message classification - */ - public void setClassification(final String classification) { - refuseFurtherComments(); - this.classification = classification; - } - /** Get the international designator for the object. * @return international designator for the object */ @@ -682,14 +678,14 @@ public void setOrbitCategory(final OrbitCategory orbitCategory) { /** Get the list of elements of information data blocks included in this message. * @return list of elements of information data blocks included in this message */ - public List getOcmDataElements() { + public List getOcmDataElements() { return ocmDataElements; } /** Set the list of elements of information data blocks included in this message. * @param ocmDataElements list of elements of information data blocks included in this message */ - public void setOcmDataElements(final List ocmDataElements) { + public void setOcmDataElements(final List ocmDataElements) { refuseFurtherComments(); this.ocmDataElements = ocmDataElements; } @@ -908,4 +904,68 @@ public void setCelestialSource(final String celestialSource) { this.celestialSource = celestialSource; } + /** Copy the instance, making sure mandatory fields have been initialized. + *

          + * Message ID, previous/next references, start and stop times are not copied. + *

          + * @param version format version + * @return a new copy + * @since 12.0 + */ + public OcmMetadata copy(final double version) { + + validate(version); + + // allocate new instance + final OcmMetadata copy = new OcmMetadata(dataContext); + + // copy comments + for (String comment : getComments()) { + copy.addComment(comment); + } + + // copy metadata + copy.setInternationalDesignator(getInternationalDesignator()); + copy.setCatalogName(getCatalogName()); + copy.setObjectDesignator(getObjectDesignator()); + copy.setAlternateNames(getAlternateNames()); + copy.setOriginatorPOC(getOriginatorPOC()); + copy.setOriginatorPosition(getOriginatorPosition()); + copy.setOriginatorPhone(getOriginatorPhone()); + copy.setOriginatorEmail(getOriginatorEmail()); + copy.setOriginatorAddress(getOriginatorAddress()); + copy.setTechOrg(getTechOrg()); + copy.setTechPOC(getTechPOC()); + copy.setTechPosition(getTechPosition()); + copy.setTechPhone(getTechPhone()); + copy.setTechEmail(getTechEmail()); + copy.setTechAddress(getTechAddress()); + copy.setAdmMessageLink(getAdmMessageLink()); + copy.setCdmMessageLink(getCdmMessageLink()); + copy.setPrmMessageLink(getPrmMessageLink()); + copy.setRdmMessageLink(getRdmMessageLink()); + copy.setTdmMessageLink(getTdmMessageLink()); + copy.setOperator(getOperator()); + copy.setOwner(getOwner()); + copy.setCountry(getCountry()); + copy.setConstellation(getConstellation()); + copy.setObjectType(getObjectType()); + copy.setEpochT0(getEpochT0()); + copy.setOpsStatus(getOpsStatus()); + copy.setOrbitCategory(getOrbitCategory()); + copy.setOcmDataElements(getOcmDataElements()); + copy.setSclkOffsetAtEpoch(getSclkOffsetAtEpoch()); + copy.setSclkSecPerSISec(getSclkSecPerSISec()); + copy.setTaimutcT0(getTaimutcT0()); + copy.setNextLeapEpoch(getNextLeapEpoch()); + copy.setNextLeapTaimutc(getNextLeapTaimutc()); + copy.setUt1mutcT0(getUt1mutcT0()); + copy.setEopSource(getEopSource()); + copy.setInterpMethodEOP(getInterpMethodEOP()); + copy.setCelestialSource(getCelestialSource()); + + return copy; + + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataKey.java index d69af0e582..71184cdb0a 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,9 +27,6 @@ */ public enum OcmMetadataKey { - /** Classification of this message. */ - CLASSIFICATION((token, context, container) -> token.processAsNormalizedString(container::setClassification)), - /** International designator for the object as assigned by the UN Committee * on Space Research (COSPAR) and the US National Space Science Data Center (NSSDC). */ INTERNATIONAL_DESIGNATOR((token, context, container) -> token.processAsNormalizedString(container::setInternationalDesignator)), @@ -44,74 +41,74 @@ public enum OcmMetadataKey { ALTERNATE_NAMES((token, context, container) -> token.processAsNormalizedList(container::setAlternateNames)), /** Programmatic Point Of Contact at originator. */ - ORIGINATOR_POC((token, context, container) -> token.processAsNormalizedString(container::setOriginatorPOC)), + ORIGINATOR_POC((token, context, container) -> token.processAsFreeTextString(container::setOriginatorPOC)), /** Position of Programmatic Point Of Contact at originator. */ - ORIGINATOR_POSITION((token, context, container) -> token.processAsNormalizedString(container::setOriginatorPosition)), + ORIGINATOR_POSITION((token, context, container) -> token.processAsFreeTextString(container::setOriginatorPosition)), /** Phone number of Programmatic Point Of Contact at originator. */ - ORIGINATOR_PHONE((token, context, container) -> token.processAsNormalizedString(container::setOriginatorPhone)), + ORIGINATOR_PHONE((token, context, container) -> token.processAsFreeTextString(container::setOriginatorPhone)), /** Email address of Programmatic Point Of Contact at originator. * @since 11.2 */ - ORIGINATOR_EMAIL((token, context, container) -> token.processAsNormalizedString(container::setOriginatorEmail)), + ORIGINATOR_EMAIL((token, context, container) -> token.processAsFreeTextString(container::setOriginatorEmail)), /** Address of Programmatic Point Of Contact at originator. */ - ORIGINATOR_ADDRESS((token, context, container) -> token.processAsNormalizedString(container::setOriginatorAddress)), + ORIGINATOR_ADDRESS((token, context, container) -> token.processAsFreeTextString(container::setOriginatorAddress)), /** Creating agency or operator. */ - TECH_ORG((token, context, container) -> token.processAsNormalizedString(container::setTechOrg)), + TECH_ORG((token, context, container) -> token.processAsFreeTextString(container::setTechOrg)), /** Technical Point Of Contact at originator. */ - TECH_POC((token, context, container) -> token.processAsNormalizedString(container::setTechPOC)), + TECH_POC((token, context, container) -> token.processAsFreeTextString(container::setTechPOC)), /** Position of Technical Point Of Contact at originator. */ - TECH_POSITION((token, context, container) -> token.processAsNormalizedString(container::setTechPosition)), + TECH_POSITION((token, context, container) -> token.processAsFreeTextString(container::setTechPosition)), /** Phone number of Technical Point Of Contact at originator. */ - TECH_PHONE((token, context, container) -> token.processAsNormalizedString(container::setTechPhone)), + TECH_PHONE((token, context, container) -> token.processAsFreeTextString(container::setTechPhone)), /** Email address of Technical Point Of Contact at originator. * @since 11.2 */ - TECH_EMAIL((token, context, container) -> token.processAsNormalizedString(container::setTechEmail)), + TECH_EMAIL((token, context, container) -> token.processAsFreeTextString(container::setTechEmail)), /** Address of Technical Point Of Contact at originator. */ - TECH_ADDRESS((token, context, container) -> token.processAsNormalizedString(container::setTechAddress)), + TECH_ADDRESS((token, context, container) -> token.processAsFreeTextString(container::setTechAddress)), /** Unique ID identifying previous message from a given originator. */ - PREVIOUS_MESSAGE_ID((token, context, container) -> token.processAsNormalizedString(container::setPreviousMessageID)), + PREVIOUS_MESSAGE_ID((token, context, container) -> token.processAsFreeTextString(container::setPreviousMessageID)), /** Unique ID identifying next message from a given originator. */ - NEXT_MESSAGE_ID((token, context, container) -> token.processAsNormalizedString(container::setNextMessageID)), + NEXT_MESSAGE_ID((token, context, container) -> token.processAsFreeTextString(container::setNextMessageID)), /** Unique identifier of Attitude Data Message linked to this Orbit Data Message. */ - ADM_MSG_LINK((token, context, container) -> token.processAsNormalizedString(container::setAdmMessageLink)), + ADM_MSG_LINK((token, context, container) -> token.processAsFreeTextString(container::setAdmMessageLink)), /** Unique identifier of Conjunction Data Message linked to this Orbit Data Message. */ - CDM_MSG_LINK((token, context, container) -> token.processAsNormalizedString(container::setCdmMessageLink)), + CDM_MSG_LINK((token, context, container) -> token.processAsFreeTextString(container::setCdmMessageLink)), /** Unique identifier of Pointing Request Message linked to this Orbit Data Message. */ - PRM_MSG_LINK((token, context, container) -> token.processAsNormalizedString(container::setPrmMessageLink)), + PRM_MSG_LINK((token, context, container) -> token.processAsFreeTextString(container::setPrmMessageLink)), /** Unique identifier of Reentry Data Message linked to this Orbit Data Message. */ - RDM_MSG_LINK((token, context, container) -> token.processAsNormalizedString(container::setRdmMessageLink)), + RDM_MSG_LINK((token, context, container) -> token.processAsFreeTextString(container::setRdmMessageLink)), /** Unique identifier of Tracking Data Message linked to this Orbit Data Message. */ - TDM_MSG_LINK((token, context, container) -> token.processAsNormalizedString(container::setTdmMessageLink)), + TDM_MSG_LINK((token, context, container) -> token.processAsFreeTextString(container::setTdmMessageLink)), /** Operator of the space object. */ - OPERATOR((token, context, container) -> token.processAsNormalizedString(container::setOperator)), + OPERATOR((token, context, container) -> token.processAsFreeTextString(container::setOperator)), /** Owner of the space object. */ - OWNER((token, context, container) -> token.processAsNormalizedString(container::setOwner)), + OWNER((token, context, container) -> token.processAsFreeTextString(container::setOwner)), /** Name of the country where the space object owner is based. */ - COUNTRY((token, context, container) -> token.processAsNormalizedString(container::setCountry)), + COUNTRY((token, context, container) -> token.processAsFreeTextString(container::setCountry)), /** Name of the constellation this space object belongs to. */ - CONSTELLATION((token, context, container) -> token.processAsNormalizedString(container::setConstellation)), + CONSTELLATION((token, context, container) -> token.processAsFreeTextString(container::setConstellation)), /** Type of object. * @see ObjectType @@ -132,9 +129,8 @@ public enum OcmMetadataKey { */ ORBIT_CATEGORY((token, context, container) -> token.processAsEnum(OrbitCategory.class, container::setOrbitCategory)), - /** List of elements of information data blocks included in this message. */ - OCM_DATA_ELEMENTS((token, context, container) -> token.processAsUppercaseList(container::setOcmDataElements)), + OCM_DATA_ELEMENTS((token, context, container) -> token.processAsEnumsList(OcmElements.class, container::setOcmDataElements)), /** Spacecraft clock count at {@link #EPOCH_TZERO}. */ SCLK_OFFSET_AT_EPOCH((token, context, container) -> token.processAsDouble(Unit.SECOND, context.getParsedUnitsBehavior(), @@ -180,13 +176,13 @@ public enum OcmMetadataKey { container::setUt1mutcT0)), /** Source and version of Earth Orientation Parameters. */ - EOP_SOURCE((token, context, container) -> token.processAsNormalizedString(container::setEopSource)), + EOP_SOURCE((token, context, container) -> token.processAsFreeTextString(container::setEopSource)), /** Interpolation method for Earth Orientation Parameters. */ INTERP_METHOD_EOP((token, context, container) -> token.processAsNormalizedString(container::setInterpMethodEOP)), /** Source and version of celestial body (e.g. Sun/Earth/Planetary). */ - CELESTIAL_SOURCE((token, context, container) -> token.processAsNormalizedString(container::setCelestialSource)); + CELESTIAL_SOURCE((token, context, container) -> token.processAsFreeTextString(container::setCelestialSource)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataWriter.java index df63d9f883..82190bf090 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmMetadataWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,9 @@ package org.orekit.files.ccsds.ndm.odm.ocm; import java.io.IOException; +import java.util.stream.Collectors; +import org.hipparchus.util.Precision; import org.orekit.files.ccsds.definitions.TimeConverter; import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey; import org.orekit.files.ccsds.section.AbstractWriter; @@ -57,9 +59,6 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeComments(metadata.getComments()); - generator.writeEntry(OcmMetadataKey.CLASSIFICATION.name(), - metadata.getClassification(), null, false); - // object generator.writeEntry(OdmMetadataKey.OBJECT_NAME.name(), metadata.getObjectName(), null, false); @@ -130,32 +129,37 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), metadata.getTimeSystem(), false); generator.writeEntry(OcmMetadataKey.EPOCH_TZERO.name(), timeConverter, - metadata.getEpochT0(), true); + metadata.getEpochT0(), true, true); // definitions generator.writeEntry(OcmMetadataKey.OPS_STATUS.name(), metadata.getOpsStatus(), false); generator.writeEntry(OcmMetadataKey.ORBIT_CATEGORY.name(), metadata.getOrbitCategory(), false); - generator.writeEntry(OcmMetadataKey.OCM_DATA_ELEMENTS.name(), - metadata.getOcmDataElements(), false); + if (metadata.getOcmDataElements() != null) { + generator.writeEntry(OcmMetadataKey.OCM_DATA_ELEMENTS.name(), + metadata.getOcmDataElements().stream().map(e -> e.name()).collect(Collectors.toList()), false); + } // other times - generator.writeEntry(OcmMetadataKey.SCLK_OFFSET_AT_EPOCH.name(), metadata.getSclkOffsetAtEpoch(), Unit.SECOND, false); - generator.writeEntry(OcmMetadataKey.SCLK_SEC_PER_SI_SEC.name(), metadata.getSclkSecPerSISec(), Unit.SECOND, false); + if (!(Precision.equals(metadata.getSclkOffsetAtEpoch(), OcmMetadata.DEFAULT_SCLK_OFFSET_AT_EPOCH) && + Precision.equals(metadata.getSclkSecPerSISec(), OcmMetadata.DEFAULT_SCLK_SEC_PER_SI_SEC))) { + generator.writeEntry(OcmMetadataKey.SCLK_OFFSET_AT_EPOCH.name(), metadata.getSclkOffsetAtEpoch(), Unit.SECOND, false); + generator.writeEntry(OcmMetadataKey.SCLK_SEC_PER_SI_SEC.name(), metadata.getSclkSecPerSISec(), Unit.SECOND, false); + } generator.writeEntry(OcmMetadataKey.PREVIOUS_MESSAGE_EPOCH.name(), timeConverter, - metadata.getPreviousMessageEpoch(), false); + metadata.getPreviousMessageEpoch(), true, false); generator.writeEntry(OcmMetadataKey.NEXT_MESSAGE_EPOCH.name(), timeConverter, - metadata.getNextMessageEpoch(), false); + metadata.getNextMessageEpoch(), true, false); generator.writeEntry(OcmMetadataKey.START_TIME.name(), timeConverter, - metadata.getStartTime(), false); + metadata.getStartTime(), false, false); generator.writeEntry(OcmMetadataKey.STOP_TIME.name(), timeConverter, - metadata.getStopTime(), false); + metadata.getStopTime(), false, false); generator.writeEntry(OcmMetadataKey.TIME_SPAN.name(), metadata.getTimeSpan(), Unit.DAY, false); generator.writeEntry(OcmMetadataKey.TAIMUTC_AT_TZERO.name(), metadata.getTaimutcT0(), Unit.SECOND, false); if (metadata.getNextLeapEpoch() != null) { generator.writeEntry(OcmMetadataKey.NEXT_LEAP_EPOCH.name(), timeConverter, - metadata.getNextLeapEpoch(), true); + metadata.getNextLeapEpoch(), true, true); generator.writeEntry(OcmMetadataKey.NEXT_LEAP_TAIMUTC.name(), metadata.getNextLeapTaimutc(), Unit.SECOND, true); } generator.writeEntry(OcmMetadataKey.UT1MUTC_AT_TZERO.name(), metadata.getUt1mutcT0(), Unit.SECOND, false); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmParser.java index 5b7ba48ec9..634c7b60a1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,18 +20,20 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.regex.Pattern; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.data.DataContext; import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; -import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey; +import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.odm.UserDefined; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.KvnStructureProcessingState; import org.orekit.files.ccsds.section.MetadataKey; @@ -68,11 +70,21 @@ public class OcmParser extends OdmParser implements EphemerisFil private static final Pattern SPLIT_AT_BLANKS = Pattern.compile("\\s+"); /** File header. */ - private Header header; + private OdmHeader header; /** Metadata for current observation block. */ private OcmMetadata metadata; + /** Central body equatorial radius. + * @since 12.0 + */ + private final double equatorialRadius; + + /** Central body flattening. + * @since 12.0 + */ + private final double flattening; + /** Context binding valid for current metadata. */ private ContextBinding context; @@ -86,25 +98,25 @@ public class OcmParser extends OdmParser implements EphemerisFil private List currentTrajectoryStateHistory; /** Physical properties logical block. */ - private PhysicalProperties physicBlock; + private OrbitPhysicalProperties physicBlock; /** Covariance logical blocks. */ - private List covarianceBlocks; + private List covarianceBlocks; /** Current covariance metadata. */ - private CovarianceHistoryMetadata currentCovarianceHistoryMetadata; + private OrbitCovarianceHistoryMetadata currentCovarianceHistoryMetadata; /** Current covariance history being read. */ - private List currentCovarianceHistory; + private List currentCovarianceHistory; /** Maneuver logical blocks. */ - private List maneuverBlocks; + private List maneuverBlocks; /** Current maneuver metadata. */ - private ManeuverHistoryMetadata currentManeuverHistoryMetadata; + private OrbitManeuverHistoryMetadata currentManeuverHistoryMetadata; /** Current maneuver history being read. */ - private List currentManeuverHistory; + private List currentManeuverHistory; /** Perturbations logical block. */ private Perturbations perturbationsBlock; @@ -126,14 +138,24 @@ public class OcmParser extends OdmParser implements EphemerisFil * parserBuilder.buildOcmParser()}. *

          * @param conventions IERS Conventions + * @param equatorialRadius central body equatorial radius + * @param flattening central body flattening * @param simpleEOP if true, tidal effects are ignored when interpolating EOP * @param dataContext used to retrieve frames, time scales, etc. * @param mu gravitational coefficient * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ - public OcmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, - final double mu, final ParsedUnitsBehavior parsedUnitsBehavior) { - super(Ocm.ROOT, Ocm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, null, mu, parsedUnitsBehavior); + public OcmParser(final IERSConventions conventions, + final double equatorialRadius, final double flattening, + final boolean simpleEOP, final DataContext dataContext, + final double mu, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(Ocm.ROOT, Ocm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, null, + mu, parsedUnitsBehavior, filters); + this.equatorialRadius = equatorialRadius; + this.flattening = flattening; } /** {@inheritDoc} */ @@ -157,14 +179,14 @@ public Ocm parse(final DataSource source) { /** {@inheritDoc} */ @Override - public Header getHeader() { + public OdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(3.0); + header = new OdmHeader(); metadata = null; context = null; trajectoryBlocks = null; @@ -249,12 +271,17 @@ public boolean inData() { /** {@inheritDoc} */ @Override public boolean finalizeData() { - // fix gravitational parameter now that all sections have been completed final List old = trajectoryBlocks; if (old != null) { + final OneAxisEllipsoid body = + currentTrajectoryStateHistoryMetadata.getTrajType() == OrbitElementsType.GEODETIC ? + new OneAxisEllipsoid(equatorialRadius, flattening, + currentTrajectoryStateHistoryMetadata.getTrajReferenceFrame().asFrame()) : + null; trajectoryBlocks = new ArrayList<>(old.size()); for (final TrajectoryStateHistory osh : old) { - trajectoryBlocks.add(new TrajectoryStateHistory(osh.getMetadata(), osh.getTrajectoryStates(), getSelectedMu())); + trajectoryBlocks.add(new TrajectoryStateHistory(osh.getMetadata(), osh.getTrajectoryStates(), + body, getSelectedMu())); } } return true; @@ -276,6 +303,11 @@ boolean manageTrajectoryStateSection(final boolean starting) { currentTrajectoryStateHistory = new ArrayList<>(); anticipateNext(this::processTrajectoryStateToken); } else { + final OneAxisEllipsoid body = + currentTrajectoryStateHistoryMetadata.getTrajType() == OrbitElementsType.GEODETIC ? + new OneAxisEllipsoid(equatorialRadius, flattening, + currentTrajectoryStateHistoryMetadata.getTrajReferenceFrame().asFrame()) : + null; anticipateNext(structureProcessor); if (currentTrajectoryStateHistoryMetadata.getCenter().getBody() != null) { setMuCreated(currentTrajectoryStateHistoryMetadata.getCenter().getBody().getGM()); @@ -284,7 +316,7 @@ boolean manageTrajectoryStateSection(final boolean starting) { // as we may get a proper one in the perturbations section trajectoryBlocks.add(new TrajectoryStateHistory(currentTrajectoryStateHistoryMetadata, currentTrajectoryStateHistory, - Double.NaN)); + body, Double.NaN)); } return true; } @@ -298,7 +330,7 @@ boolean managePhysicalPropertiesSection(final boolean starting) { if (starting) { if (physicBlock == null) { // this is the first (and unique) physical properties block, we need to allocate the container - physicBlock = new PhysicalProperties(metadata.getEpochT0()); + physicBlock = new OrbitPhysicalProperties(metadata.getEpochT0()); } anticipateNext(this::processPhysicalPropertyToken); } else { @@ -318,13 +350,13 @@ boolean manageCovarianceHistorySection(final boolean starting) { // this is the first covariance block, we need to allocate the container covarianceBlocks = new ArrayList<>(); } - currentCovarianceHistoryMetadata = new CovarianceHistoryMetadata(metadata.getEpochT0()); + currentCovarianceHistoryMetadata = new OrbitCovarianceHistoryMetadata(metadata.getEpochT0()); currentCovarianceHistory = new ArrayList<>(); anticipateNext(this::processCovarianceToken); } else { anticipateNext(structureProcessor); - covarianceBlocks.add(new CovarianceHistory(currentCovarianceHistoryMetadata, - currentCovarianceHistory)); + covarianceBlocks.add(new OrbitCovarianceHistory(currentCovarianceHistoryMetadata, + currentCovarianceHistory)); currentCovarianceHistoryMetadata = null; currentCovarianceHistory = null; } @@ -342,13 +374,13 @@ boolean manageManeuversSection(final boolean starting) { // this is the first maneuver block, we need to allocate the container maneuverBlocks = new ArrayList<>(); } - currentManeuverHistoryMetadata = new ManeuverHistoryMetadata(metadata.getEpochT0()); + currentManeuverHistoryMetadata = new OrbitManeuverHistoryMetadata(metadata.getEpochT0()); currentManeuverHistory = new ArrayList<>(); anticipateNext(this::processManeuverToken); } else { anticipateNext(structureProcessor); - maneuverBlocks.add(new ManeuverHistory(currentManeuverHistoryMetadata, - currentManeuverHistory)); + maneuverBlocks.add(new OrbitManeuverHistory(currentManeuverHistoryMetadata, + currentManeuverHistory)); currentManeuverHistoryMetadata = null; currentManeuverHistory = null; } @@ -412,22 +444,34 @@ boolean manageUserDefinedParametersSection(final boolean starting) { /** {@inheritDoc} */ @Override public Ocm build() { + // OCM KVN file lack a DATA_STOP keyword, hence we can't call finalizeData() // automatically before the end of the file finalizeData(); if (userDefinedBlock != null && userDefinedBlock.getParameters().isEmpty()) { userDefinedBlock = null; } - if (perturbationsBlock != null) { - // this may be Double.NaN, but it will be handled correctly - setMuParsed(perturbationsBlock.getGm()); + + // the mu is needed only if there are trajectories + final double mu; + if (trajectoryBlocks == null) { + mu = Double.NaN; + } else { + if (perturbationsBlock != null) { + // this may be Double.NaN, but it will be handled correctly + setMuParsed(perturbationsBlock.getGm()); + } + mu = getSelectedMu(); } + final OcmData data = new OcmData(trajectoryBlocks, physicBlock, covarianceBlocks, maneuverBlocks, perturbationsBlock, orbitDeterminationBlock, userDefinedBlock); data.validate(header.getFormatVersion()); + return new Ocm(header, Collections.singletonList(new Segment<>(metadata, data)), - getConventions(), getDataContext(), getSelectedMu()); + getConventions(), getDataContext(), mu); + } /** Process one metadata token. @@ -501,7 +545,7 @@ private boolean processTrajectoryStateToken(final ParseToken token) { } final AbsoluteDate epoch = context.getTimeSystem().getConverter(context).parse(fields[0]); return currentTrajectoryStateHistory.add(new TrajectoryState(currentTrajectoryStateHistoryMetadata.getTrajType(), - epoch, fields, 1, units)); + epoch, fields, 1, units)); } catch (NumberFormatException | OrekitIllegalArgumentException e) { throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString()); @@ -515,12 +559,12 @@ private boolean processTrajectoryStateToken(final ParseToken token) { */ private boolean processPhysicalPropertyToken(final ParseToken token) { if (physicBlock == null) { - physicBlock = new PhysicalProperties(metadata.getEpochT0()); + physicBlock = new OrbitPhysicalProperties(metadata.getEpochT0()); } anticipateNext(this::processDataSubStructureToken); try { return token.getName() != null && - PhysicalPropertiesKey.valueOf(token.getName()).process(token, context, physicBlock); + OrbitPhysicalPropertiesKey.valueOf(token.getName()).process(token, context, physicBlock); } catch (IllegalArgumentException iae) { // token has not been recognized return false; @@ -535,7 +579,7 @@ private boolean processCovarianceToken(final ParseToken token) { if (token.getName() != null && !token.getName().equals(Ocm.COV_LINE)) { // we are in the section metadata part try { - return CovarianceHistoryMetadataKey.valueOf(token.getName()). + return OrbitCovarianceHistoryMetadataKey.valueOf(token.getName()). process(token, context, currentCovarianceHistoryMetadata); } catch (IllegalArgumentException iae) { // token has not been recognized @@ -553,12 +597,12 @@ private boolean processCovarianceToken(final ParseToken token) { } try { final String[] fields = SPLIT_AT_BLANKS.split(token.getRawContent().trim()); - final int n = currentCovarianceHistoryMetadata.getCovUnits().size(); + final int n = currentCovarianceHistoryMetadata.getCovType().getUnits().size(); if (fields.length - 1 != currentCovarianceHistoryMetadata.getCovOrdering().nbElements(n)) { throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString()); } - currentCovarianceHistory.add(new Covariance(currentCovarianceHistoryMetadata.getCovType(), + currentCovarianceHistory.add(new OrbitCovariance(currentCovarianceHistoryMetadata.getCovType(), currentCovarianceHistoryMetadata.getCovOrdering(), context.getTimeSystem().getConverter(context).parse(fields[0]), fields, 1)); @@ -578,7 +622,7 @@ private boolean processManeuverToken(final ParseToken token) { if (token.getName() != null && !token.getName().equals(Ocm.MAN_LINE)) { // we are in the section metadata part try { - return ManeuverHistoryMetadataKey.valueOf(token.getName()). + return OrbitManeuverHistoryMetadataKey.valueOf(token.getName()). process(token, context, currentManeuverHistoryMetadata); } catch (IllegalArgumentException iae) { // token has not been recognized @@ -601,7 +645,7 @@ private boolean processManeuverToken(final ParseToken token) { throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString()); } - final Maneuver maneuver = new Maneuver(); + final OrbitManeuver maneuver = new OrbitManeuver(); for (int i = 0; i < fields.length; ++i) { types.get(i).process(fields[i], context, maneuver, token.getLineNumber(), token.getFileName()); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmSatelliteEphemeris.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmSatelliteEphemeris.java index 6ef1fb29df..487f73d3d7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmSatelliteEphemeris.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmSatelliteEphemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmWriter.java index 8c3b86c6a3..05627019e2 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OcmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,8 +21,8 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.UserDefinedWriter; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.Segment; import org.orekit.files.ccsds.section.XmlStructureKey; import org.orekit.files.ccsds.utils.ContextBinding; @@ -35,10 +35,12 @@ /** * Writer for CCSDS Orbit Comprehensive Message. * + * @see EphemerisOcmWriter + * @see StreamingOcmWriter * @author Luc Maisonobe * @since 11.0 */ -public class OcmWriter extends AbstractMessageWriter, Ocm> { +public class OcmWriter extends AbstractMessageWriter, Ocm> { /** Version number implemented. **/ public static final double CCSDS_OCM_VERS = 3.0; @@ -46,6 +48,16 @@ public class OcmWriter extends AbstractMessageWriter token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), /** Covariance identification number. */ - COV_ID((token, context, container) -> token.processAsNormalizedString(container::setCovID)), + COV_ID((token, context, container) -> token.processAsFreeTextString(container::setCovID)), /** Identification number of previous covariance. */ - COV_PREV_ID((token, context, container) -> token.processAsNormalizedString(container::setCovPrevID)), + COV_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setCovPrevID)), /** Identification number of next covariance. */ - COV_NEXT_ID((token, context, container) -> token.processAsNormalizedString(container::setCovNextID)), + COV_NEXT_ID((token, context, container) -> token.processAsFreeTextString(container::setCovNextID)), /** Basis of this covariance time history data. */ - COV_BASIS((token, context, container) -> token.processAsNormalizedString(container::setCovBasis)), + COV_BASIS((token, context, container) -> token.processAsFreeTextString(container::setCovBasis)), /** Identification number of the orbit determination or simulation upon which this covariance is based.*/ - COV_BASIS_ID((token, context, container) -> token.processAsNormalizedString(container::setCovBasisID)), + COV_BASIS_ID((token, context, container) -> token.processAsFreeTextString(container::setCovBasisID)), /** Reference frame of the covariance. */ COV_REF_FRAME((token, context, container) -> token.processAsFrame(container::setCovReferenceFrame, context, true, true, false)), @@ -67,9 +66,9 @@ public enum CovarianceHistoryMetadataKey { container::setCovConfidence)), /** Covariance element set type. - * @see ElementsType + * @see OrbitElementsType */ - COV_TYPE((token, context, container) -> token.processAsEnum(ElementsType.class, container::setCovType)), + COV_TYPE((token, context, container) -> token.processAsEnum(OrbitElementsType.class, container::setCovType)), /** Covariance ordering. */ COV_ORDERING((token, context, container) -> token.processAsEnum(Ordering.class, container::setCovOrdering)), @@ -83,7 +82,7 @@ public enum CovarianceHistoryMetadataKey { /** Simple constructor. * @param processor processing method */ - CovarianceHistoryMetadataKey(final TokenProcessor processor) { + OrbitCovarianceHistoryMetadataKey(final TokenProcessor processor) { this.processor = processor; } @@ -93,7 +92,7 @@ public enum CovarianceHistoryMetadataKey { * @param container container to fill * @return true of token was accepted */ - public boolean process(final ParseToken token, final ContextBinding context, final CovarianceHistoryMetadata container) { + public boolean process(final ParseToken token, final ContextBinding context, final OrbitCovarianceHistoryMetadata container) { return processor.process(token, context, container); } @@ -105,7 +104,7 @@ interface TokenProcessor { * @param container container to fill * @return true of token was accepted */ - boolean process(ParseToken token, ContextBinding context, CovarianceHistoryMetadata container); + boolean process(ParseToken token, ContextBinding context, OrbitCovarianceHistoryMetadata container); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceHistoryWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitCovarianceHistoryWriter.java similarity index 59% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceHistoryWriter.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitCovarianceHistoryWriter.java index 7b8f838e06..3e4d6e4f2e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/CovarianceHistoryWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitCovarianceHistoryWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,10 +33,10 @@ * @author Luc Maisonobe * @since 11.0 */ -class CovarianceHistoryWriter extends AbstractWriter { +class OrbitCovarianceHistoryWriter extends AbstractWriter { /** Covariance history block. */ - private final CovarianceHistory history; + private final OrbitCovarianceHistory history; /** Converter for dates. */ private final TimeConverter timeConverter; @@ -45,9 +45,9 @@ class CovarianceHistoryWriter extends AbstractWriter { * @param covarianceHistory covariance history to write * @param timeConverter converter for dates */ - CovarianceHistoryWriter(final CovarianceHistory covarianceHistory, + OrbitCovarianceHistoryWriter(final OrbitCovarianceHistory covarianceHistory, final TimeConverter timeConverter) { - super(OcmDataSubStructureKey.covar.name(), OcmDataSubStructureKey.COV.name()); + super(OcmDataSubStructureKey.cov.name(), OcmDataSubStructureKey.COV.name()); this.history = covarianceHistory; this.timeConverter = timeConverter; } @@ -57,33 +57,37 @@ class CovarianceHistoryWriter extends AbstractWriter { protected void writeContent(final Generator generator) throws IOException { // covariance history block - final CovarianceHistoryMetadata metadata = history.getMetadata(); + final OrbitCovarianceHistoryMetadata metadata = history.getMetadata(); generator.writeComments(metadata.getComments()); // identifiers - generator.writeEntry(CovarianceHistoryMetadataKey.COV_ID.name(), metadata.getCovID(), null, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_PREV_ID.name(), metadata.getCovPrevID(), null, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_NEXT_ID.name(), metadata.getCovNextID(), null, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_BASIS.name(), metadata.getCovBasis(), null, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_BASIS_ID.name(), metadata.getCovBasisID(), null, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_ID.name(), metadata.getCovID(), null, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_PREV_ID.name(), metadata.getCovPrevID(), null, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_NEXT_ID.name(), metadata.getCovNextID(), null, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_BASIS.name(), metadata.getCovBasis(), null, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_BASIS_ID.name(), metadata.getCovBasisID(), null, false); // references - generator.writeEntry(CovarianceHistoryMetadataKey.COV_REF_FRAME.name(), metadata.getCovReferenceFrame().getName(), null, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_FRAME_EPOCH.name(), timeConverter, metadata.getCovFrameEpoch(), false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_REF_FRAME.name(), metadata.getCovReferenceFrame().getName(), null, false); + if (!metadata.getCovFrameEpoch().equals(timeConverter.getReferenceDate()) && + metadata.getCovReferenceFrame().asOrbitRelativeFrame() == null && + metadata.getCovReferenceFrame().asSpacecraftBodyFrame() == null) { + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_FRAME_EPOCH.name(), timeConverter, metadata.getCovFrameEpoch(), true, false); + } // scaling - generator.writeEntry(CovarianceHistoryMetadataKey.COV_SCALE_MIN.name(), metadata.getCovScaleMin(), Unit.ONE, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_SCALE_MAX.name(), metadata.getCovScaleMax(), Unit.ONE, false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_CONFIDENCE.name(), metadata.getCovConfidence(), Unit.PERCENT, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_SCALE_MIN.name(), metadata.getCovScaleMin(), Unit.ONE, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_SCALE_MAX.name(), metadata.getCovScaleMax(), Unit.ONE, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_CONFIDENCE.name(), metadata.getCovConfidence(), Unit.PERCENT, false); // elements - generator.writeEntry(CovarianceHistoryMetadataKey.COV_TYPE.name(), metadata.getCovType(), false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_ORDERING.name(), metadata.getCovOrdering(), false); - generator.writeEntry(CovarianceHistoryMetadataKey.COV_UNITS.name(), generator.unitsListToString(metadata.getCovUnits()), null, false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_TYPE.name(), metadata.getCovType(), false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_ORDERING.name(), metadata.getCovOrdering(), false); + generator.writeEntry(OrbitCovarianceHistoryMetadataKey.COV_UNITS.name(), generator.unitsListToString(metadata.getCovUnits()), null, false); // data final List units = metadata.getCovType().getUnits(); - for (final Covariance covariance : history.getCovariances()) { + for (final OrbitCovariance covariance : history.getCovariances()) { final RealMatrix matrix = covariance.getMatrix(); final Ordering ordering = metadata.getCovOrdering(); final CovarianceIndexer indexer = new CovarianceIndexer(units.size()); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDetermination.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDetermination.java index a356d1295c..70b24a968d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDetermination.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDetermination.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -102,6 +102,11 @@ public class OrbitDetermination extends CommentsContainer { /** Description of consider parameters. */ private List considerParameters; + /** Specific Energy Dissipation Rate. + * @since 12.0 + */ + private double sedr; + /** Number of sensors used. */ private int sensorsN; @@ -116,7 +121,8 @@ public class OrbitDetermination extends CommentsContainer { /** Simple constructor. */ - OrbitDetermination() { + public OrbitDetermination() { + sedr = Double.NaN; solveStates = Collections.emptyList(); considerParameters = Collections.emptyList(); sensors = Collections.emptyList(); @@ -127,9 +133,9 @@ public class OrbitDetermination extends CommentsContainer { @Override public void validate(final double version) { super.validate(version); - checkNotNull(id, OrbitDeterminationKey.OD_ID); - checkNotNull(method, OrbitDeterminationKey.OD_METHOD); - checkNotNull(epoch, OrbitDeterminationKey.OD_EPOCH); + checkNotNull(id, OrbitDeterminationKey.OD_ID.name()); + checkNotNull(method, OrbitDeterminationKey.OD_METHOD.name()); + checkNotNull(epoch, OrbitDeterminationKey.OD_EPOCH.name()); } /** Get identification number. @@ -468,6 +474,22 @@ public void setConsiderParameters(final List considerParameters) { this.considerParameters = considerParameters; } + /** Get Specific Energy Dissipation Rate. + * @return Specific Energy Dissipation Rate + * @since 12.0 + */ + public double getSedr() { + return sedr; + } + + /** Set Specific Energy Dissipation Rate. + * @param sedr Specific Energy Dissipation Rate (W/kg) + * @since 12.0 + */ + public void setSedr(final double sedr) { + this.sedr = sedr; + } + /** Get number of sensors used. * @return number of sensors used */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationKey.java index a3df4adf3e..66a060f7d5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.files.ccsds.ndm.odm.ocm; import org.orekit.files.ccsds.definitions.OdMethodFacade; +import org.orekit.files.ccsds.definitions.Units; import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.files.ccsds.utils.lexical.TokenType; @@ -34,15 +35,15 @@ public enum OrbitDeterminationKey { token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), /** Identification number. */ - OD_ID((token, context, container) -> token.processAsNormalizedString(container::setId)), + OD_ID((token, context, container) -> token.processAsFreeTextString(container::setId)), /** Identification of previous orbit determination. */ - OD_PREV_ID((token, context, container) -> token.processAsNormalizedString(container::setPrevId)), + OD_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setPrevId)), /** Orbit determination method. */ OD_METHOD((token, context, container) -> { if (token.getType() == TokenType.ENTRY) { - container.setMethod(OdMethodFacade.parse(token.getContentAsNormalizedString())); + container.setMethod(OdMethodFacade.parse(token.getRawContent())); } return true; }), @@ -58,7 +59,7 @@ public enum OrbitDeterminationKey { DAYS_SINCE_LAST_OBS((token, context, container) -> token.processAsDouble(Unit.DAY, context.getParsedUnitsBehavior(), container::setTimeSinceLastObservation)), - /** Sime span of observation recommended for the OD of the object. */ + /** Time span of observation recommended for the OD of the object. */ RECOMMENDED_OD_SPAN((token, context, container) -> token.processAsDouble(Unit.DAY, context.getParsedUnitsBehavior(), container::setRecommendedOdSpan)), @@ -113,26 +114,32 @@ public enum OrbitDeterminationKey { SOLVE_N((token, context, container) -> token.processAsInteger(container::setSolveN)), /** Description of state elements solved-for. */ - SOLVE_STATES((token, context, container) -> token.processAsNormalizedList(container::setSolveStates)), + SOLVE_STATES((token, context, container) -> token.processAsFreeTextList(container::setSolveStates)), /** Number of consider parameters. */ CONSIDER_N((token, context, container) -> token.processAsInteger(container::setConsiderN)), /** Description of consider parameters. */ - CONSIDER_PARAMS((token, context, container) -> token.processAsNormalizedList(container::setConsiderParameters)), + CONSIDER_PARAMS((token, context, container) -> token.processAsFreeTextList(container::setConsiderParameters)), + + /** Specific Energy Dissipation Rate. + * @since 12.0 + */ + SEDR((token, context, container) -> token.processAsDouble(Units.W_PER_KG, context.getParsedUnitsBehavior(), + container::setSedr)), /** Number of sensors used. */ SENSORS_N((token, context, container) -> token.processAsInteger(container::setSensorsN)), /** Description of sensors used. */ - SENSORS((token, context, container) -> token.processAsNormalizedList(container::setSensors)), + SENSORS((token, context, container) -> token.processAsFreeTextList(container::setSensors)), /** Weighted RMS residual ratio. */ WEIGHTED_RMS((token, context, container) -> token.processAsDouble(Unit.ONE, context.getParsedUnitsBehavior(), container::setWeightedRms)), /** Observation data types used. */ - DATA_TYPES((token, context, container) -> token.processAsNormalizedList(container::setDataTypes)); + DATA_TYPES((token, context, container) -> token.processAsFreeTextList(container::setDataTypes)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationWriter.java index 0d5938f762..546d6d74c6 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitDeterminationWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import java.io.IOException; import org.orekit.files.ccsds.definitions.TimeConverter; +import org.orekit.files.ccsds.definitions.Units; import org.orekit.files.ccsds.section.AbstractWriter; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.utils.units.Unit; @@ -68,7 +69,7 @@ protected void writeContent(final Generator generator) throws IOException { } // time - generator.writeEntry(OrbitDeterminationKey.OD_EPOCH.name(), timeConverter, od.getEpoch(), false); + generator.writeEntry(OrbitDeterminationKey.OD_EPOCH.name(), timeConverter, od.getEpoch(), false, false); generator.writeEntry(OrbitDeterminationKey.DAYS_SINCE_FIRST_OBS.name(), od.getTimeSinceFirstObservation(), Unit.DAY, false); generator.writeEntry(OrbitDeterminationKey.DAYS_SINCE_LAST_OBS.name(), od.getTimeSinceLastObservation(), Unit.DAY, false); generator.writeEntry(OrbitDeterminationKey.RECOMMENDED_OD_SPAN.name(), od.getRecommendedOdSpan(), Unit.DAY, false); @@ -91,12 +92,13 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(OrbitDeterminationKey.GDOP.name(), od.getGdop(), Unit.ONE, false); // parameters - generator.writeEntry(OrbitDeterminationKey.SOLVE_N.name(), od.getSolveN(), false); - generator.writeEntry(OrbitDeterminationKey.SOLVE_STATES.name(), od.getSolveStates(), false); - generator.writeEntry(OrbitDeterminationKey.CONSIDER_N.name(), od.getConsiderN(), false); - generator.writeEntry(OrbitDeterminationKey.CONSIDER_PARAMS.name(), od.getConsiderParameters(), false); - generator.writeEntry(OrbitDeterminationKey.SENSORS_N.name(), od.getSensorsN(), false); - generator.writeEntry(OrbitDeterminationKey.SENSORS.name(), od.getSensors(), false); + generator.writeEntry(OrbitDeterminationKey.SOLVE_N.name(), od.getSolveN(), false); + generator.writeEntry(OrbitDeterminationKey.SOLVE_STATES.name(), od.getSolveStates(), false); + generator.writeEntry(OrbitDeterminationKey.CONSIDER_N.name(), od.getConsiderN(), false); + generator.writeEntry(OrbitDeterminationKey.CONSIDER_PARAMS.name(), od.getConsiderParameters(), false); + generator.writeEntry(OrbitDeterminationKey.SEDR.name(), od.getSedr(), Units.W_PER_KG, false); + generator.writeEntry(OrbitDeterminationKey.SENSORS_N.name(), od.getSensorsN(), false); + generator.writeEntry(OrbitDeterminationKey.SENSORS.name(), od.getSensors(), false); // observations generator.writeEntry(OrbitDeterminationKey.WEIGHTED_RMS.name(), od.getWeightedRms(), Unit.ONE, false); diff --git a/src/main/java/org/orekit/files/ccsds/definitions/ElementsType.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitElementsType.java similarity index 54% rename from src/main/java/org/orekit/files/ccsds/definitions/ElementsType.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitElementsType.java index da26f32060..a4eafbb60e 100644 --- a/src/main/java/org/orekit/files/ccsds/definitions/ElementsType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitElementsType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,22 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.files.ccsds.definitions; +package org.orekit.files.ccsds.ndm.odm.ocm; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.Precision; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.files.ccsds.ndm.odm.ocm.Ocm; +import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.time.AbsoluteDate; import org.orekit.utils.TimeStampedPVCoordinates; import org.orekit.utils.units.Unit; @@ -39,51 +43,90 @@ * @author Luc Maisonobe * @since 11.0 */ -public enum ElementsType { +public enum OrbitElementsType { // CHECKSTYLE: stop MultipleStringLiterals check /** Spherical 6-element set (α,δ,β,A,r,v). */ ADBARV("Spherical 6-element set (α,δ,β,A,r,v)", - "°", "°", "°", "°", "km", "km"), + "°", "°", "°", "°", "km", "km/s"), /** Cartesian 3-element position (X, Y, Z). */ CARTP("Cartesian 3-element position (X, Y, Z)", "km", "km", "km") { + /** {@inheritDoc} */ @Override - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { return new TimeStampedPVCoordinates(date, new Vector3D(elements[0], elements[1], elements[2]), Vector3D.ZERO, Vector3D.ZERO); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + return new double[] { + pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ() + }; + } + }, /** Cartesian 6-element position and velocity (X, Y, Z, XD, YD, ZD). */ CARTPV("Cartesian 6-element position and velocity (X, Y, Z, XD, YD, ZD)", "km", "km", "km", "km/s", "km/s", "km/s") { + /** {@inheritDoc} */ @Override - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { return new TimeStampedPVCoordinates(date, new Vector3D(elements[0], elements[1], elements[2]), new Vector3D(elements[3], elements[4], elements[5]), Vector3D.ZERO); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + return new double[] { + pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(), + pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ() + }; + } + }, /** Cartesian 9-element position, velocity and acceleration (X, Y, Z, XD, YD, ZD, XDD, YDD, ZDD). */ CARTPVA("Cartesian 9-element position, velocity and acceleration (X, Y, Z, XD, YD, ZD, XDD, YDD, ZDD)", "km", "km", "km", "km/s", "km/s", "km/s", "km/s²", "km/s²", "km/s²") { + /** {@inheritDoc} */ @Override - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { return new TimeStampedPVCoordinates(date, new Vector3D(elements[0], elements[1], elements[2]), new Vector3D(elements[3], elements[4], elements[5]), new Vector3D(elements[6], elements[7], elements[8])); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + return new double[] { + pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(), + pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ(), + pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ() + }; + } + }, /** Delaunay elements (L, G, H, l, g, h). */ @@ -101,10 +144,12 @@ public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final doubl /** Equinoctial elements (a, af, ag, L=M+ω+frΩ, χ, ψ, fr). */ EQUINOCTIAL("Equinoctial elements (a, af, ag, L=M+ω+frΩ, χ, ψ, fr)", "km", "n/a", "n/a", "°", "n/a", "n/a", "n/a") { + /** {@inheritDoc} */ @Override @DefaultDataContext - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { if (elements[6] < 0) { // retrograde throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL, @@ -112,65 +157,155 @@ public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final doubl } return new EquinoctialOrbit(elements[0], elements[1], elements[2], elements[5], elements[4], // BEWARE! the inversion here is intentional - elements[3], PositionAngle.MEAN, + elements[3], PositionAngleType.MEAN, FramesFactory.getGCRF(), date, mu). getPVCoordinates(); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + final EquinoctialOrbit orbit = new EquinoctialOrbit(pv, frame, mu); + return new double[] { + orbit.getA(), orbit.getEquinoctialEx(), orbit.getEquinoctialEy(), + orbit.getLM(), orbit.getHy(), orbit.getHx(), +1 + }; + } + }, /** Modified equinoctial elements (p=a(1−e²), af, ag, L'=υ+ω+frΩ, χ, ψ, fr). */ EQUINOCTIALMOD("Modified equinoctial elements (p=a(1−e²), af, ag, L'=υ+ω+frΩ, χ, ψ, fr)", "km", "n/a", "n/a", "°", "n/a", "n/a", "n/a") { + /** {@inheritDoc} */ @Override @DefaultDataContext - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { if (elements[6] < 0) { // retrograde throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL, EQUINOCTIALMOD.name()); } - final double oMe2 = 1.0 - elements[1] * elements[1] - elements[2] * elements[2]; + final double oMe2 = 1.0 - (elements[1] * elements[1] + elements[2] * elements[2]); return new EquinoctialOrbit(elements[0] / oMe2, elements[1], elements[2], elements[5], elements[4], // BEWARE! the inversion here is intentional - elements[3], PositionAngle.TRUE, + elements[3], PositionAngleType.TRUE, FramesFactory.getGCRF(), date, mu). getPVCoordinates(); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + final EquinoctialOrbit orbit = new EquinoctialOrbit(pv, frame, mu); + final double ex = orbit.getEquinoctialEx(); + final double ey = orbit.getEquinoctialEy(); + return new double[] { + orbit.getA() * (1 - (ex * ex + ey * ey)), ex, ey, + orbit.getLv(), orbit.getHy(), orbit.getHx(), +1 + }; + } + }, /** Geodetic elements (λ, ΦGD, β, A, h, vre). */ GEODETIC("Geodetic elements (λ, ΦGD, β, A, h, vre)", - "°", "°", "°", "°", "km", "km/s"), + "°", "°", "°", "°", "km", "km/s") { + + /** {@inheritDoc} */ + @Override + @DefaultDataContext + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { + final GeodeticPoint gp = new GeodeticPoint(elements[1], elements[0], elements[4]); + final Vector3D position = body.transform(gp); + final SinCos scBeta = FastMath.sinCos(elements[2]); + final SinCos scAzi = FastMath.sinCos(elements[3]); + final Vector3D velocity = new Vector3D(elements[5] * scBeta.cos() * scAzi.sin(), gp.getEast(), + elements[5] * scBeta.cos() * scAzi.cos(), gp.getNorth(), + elements[5] * scBeta.sin(), gp.getZenith()); + return new TimeStampedPVCoordinates(date, position, velocity); + } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + final GeodeticPoint gp = body.transform(pv.getPosition(), frame, pv.getDate()); + return new double[] { + gp.getLongitude(), gp.getLatitude(), + MathUtils.SEMI_PI - Vector3D.angle(pv.getVelocity(), gp.getZenith()), + FastMath.atan2(Vector3D.dotProduct(pv.getVelocity(), gp.getEast()), + Vector3D.dotProduct(pv.getVelocity(), gp.getNorth())), + gp.getAltitude(), + pv.getVelocity().getNorm() + }; + } + + }, /** Keplerian 6-element classical set (a, e, i, Ω, ω, ν). */ KEPLERIAN("Keplerian 6-elemnt classical set (a, e, i, Ω, ω, ν)", "km", "n/a", "°", "°", "°", "°") { + /** {@inheritDoc} */ @Override @DefaultDataContext - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { return new KeplerianOrbit(elements[0], elements[1], elements[2], elements[4], elements[3], // BEWARE! the inversion here is intentional - elements[5], PositionAngle.TRUE, + elements[5], PositionAngleType.TRUE, FramesFactory.getGCRF(), date, mu). getPVCoordinates(); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + final KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, mu); + return new double[] { + orbit.getA(), orbit.getE(), orbit.getI(), + orbit.getRightAscensionOfAscendingNode(), + orbit.getPerigeeArgument(), orbit.getTrueAnomaly() + }; + } + }, /** Keplerian 6-element classical set (a, e, i, Ω, ω, M). */ KEPLERIANMEAN("Keplerian 6-elemnt classical set (a, e, i, Ω, ω, M)", "km", "n/a", "°", "°", "°", "°") { + /** {@inheritDoc} */ @Override @DefaultDataContext - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { return new KeplerianOrbit(elements[0], elements[1], elements[2], elements[4], elements[3], // BEWARE! the inversion here is intentional - elements[5], PositionAngle.MEAN, + elements[5], PositionAngleType.MEAN, FramesFactory.getGCRF(), date, mu). getPVCoordinates(); } + + /** {@inheritDoc} */ + @Override + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { + final KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, mu); + return new double[] { + orbit.getA(), orbit.getE(), orbit.getI(), + orbit.getRightAscensionOfAscendingNode(), + orbit.getPerigeeArgument(), orbit.getMeanAnomaly() + }; + } + }, /** Modified spherical 6-element set (λ, δ, β, A, r, v). */ @@ -197,7 +332,7 @@ public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final doubl * @param description description * @param unitsSpecifications elements units specifications */ - ElementsType(final String description, final String... unitsSpecifications) { + OrbitElementsType(final String description, final String... unitsSpecifications) { this.description = description; this.units = Stream.of(unitsSpecifications). map(s -> Unit.parse(s)). @@ -211,31 +346,30 @@ public List getUnits() { return units; } - /** Check if parsed units are compatible with elements types. - * @param parsedUnits units to check - */ - public void checkUnits(final List parsedUnits) { - if (parsedUnits.size() != units.size()) { - throw new OrekitException(OrekitMessages.CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS, - name(), toString(), units.size()); - } - for (int i = 0; i < units.size(); ++i) { - if (!(units.get(i).sameDimension(parsedUnits.get(i)) && - Precision.equals(units.get(i).getScale(), parsedUnits.get(i).getScale(), 1))) { - throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS, - units.get(i).getName(), - parsedUnits.get(i).getName()); - } - } - } - /** Convert to Cartesian coordinates. * @param date elements date - * @param elements elements values + * @param elements elements values in SI units + * @param body central body + * (may be null if type is not {@link OrbitElementsType#GEODETIC}) * @param mu gravitational parameter in m³/s² * @return Cartesian coordinates */ - public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, final double mu) { + public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements, + final OneAxisEllipsoid body, final double mu) { + throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE, name(), toString()); + } + + /** Convert to raw elements array. + * @param pv Cartesian coordinates + * @param frame inertial frame where elements are defined + * @param body central body + * (may be null if type is not {@link OrbitElementsType#GEODETIC}) + * @param mu gravitational parameter in m³/s² + * @return elements elements values in SI units + * @since 12.0 + */ + public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame, + final OneAxisEllipsoid body, final double mu) { throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE, name(), toString()); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Maneuver.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuver.java similarity index 99% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Maneuver.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuver.java index 0c60edaf55..e78583d5e7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Maneuver.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,7 +26,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class Maneuver implements TimeStamped { +public class OrbitManeuver implements TimeStamped { /** Maneuver date. */ private AbsoluteDate date; @@ -99,7 +99,7 @@ public class Maneuver implements TimeStamped { /** Build an uninitialized maneuver. */ - public Maneuver() { + public OrbitManeuver() { acceleration = new double[3]; dV = new double[3]; thrust = new double[3]; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistory.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistory.java similarity index 76% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistory.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistory.java index ab18f8380f..d13c652668 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistory.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,20 +24,20 @@ * @author Luc Maisonobe * @since 11.0 */ -public class ManeuverHistory { +public class OrbitManeuverHistory { /** Metadata. */ - private final ManeuverHistoryMetadata metadata; + private final OrbitManeuverHistoryMetadata metadata; /** Maneuver. */ - private final List maneuvers; + private final List maneuvers; /** Simple constructor. * @param metadata metadata * @param maneuvers maneuvers */ - ManeuverHistory(final ManeuverHistoryMetadata metadata, - final List maneuvers) { + public OrbitManeuverHistory(final OrbitManeuverHistoryMetadata metadata, + final List maneuvers) { this.metadata = metadata; this.maneuvers = maneuvers; } @@ -45,14 +45,14 @@ public class ManeuverHistory { /** Get metadata. * @return metadata */ - public ManeuverHistoryMetadata getMetadata() { + public OrbitManeuverHistoryMetadata getMetadata() { return metadata; } /** Get the maneuvers. * @return maneuvers */ - public List getManeuvers() { + public List getManeuvers() { return Collections.unmodifiableList(maneuvers); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryMetadata.java similarity index 91% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryMetadata.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryMetadata.java index d64231a11b..9fe924e87b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -36,7 +36,12 @@ * @author Luc Maisonobe * @since 11.0 */ -public class ManeuverHistoryMetadata extends CommentsContainer { +public class OrbitManeuverHistoryMetadata extends CommentsContainer { + + /** Default duty cycle type. + * @since 12.0 + */ + public static final DutyCycleType DEFAULT_DC_TYPE = DutyCycleType.CONTINUOUS; /** Maneuver identification number. */ private String manID; @@ -131,7 +136,7 @@ public class ManeuverHistoryMetadata extends CommentsContainer { /** Simple constructor. * @param epochT0 T0 epoch from file metadata */ - ManeuverHistoryMetadata(final AbsoluteDate epochT0) { + public OrbitManeuverHistoryMetadata(final AbsoluteDate epochT0) { // we don't call the setXxx() methods in order to avoid // calling refuseFurtherComments as a side effect manBasis = ManBasis.PLANNED; @@ -140,9 +145,9 @@ public class ManeuverHistoryMetadata extends CommentsContainer { OrbitRelativeFrame.TNW_INERTIAL.name()); manFrameEpoch = epochT0; manPurpose = Collections.emptyList(); - dcType = DutyCycleType.CONTINUOUS; - dcMinCycles = 0; - dcMaxCycles = 0; + dcType = DEFAULT_DC_TYPE; + dcMinCycles = -1; + dcMaxCycles = -1; dcTimePulseDuration = Double.NaN; dcTimePulsePeriod = Double.NaN; } @@ -151,27 +156,27 @@ public class ManeuverHistoryMetadata extends CommentsContainer { @Override public void validate(final double version) { super.validate(version); - checkNotNull(manID, ManeuverHistoryMetadataKey.MAN_ID); - checkNotNull(manDeviceID, ManeuverHistoryMetadataKey.MAN_DEVICE_ID); + checkNotNull(manID, OrbitManeuverHistoryMetadataKey.MAN_ID.name()); + checkNotNull(manDeviceID, OrbitManeuverHistoryMetadataKey.MAN_DEVICE_ID.name()); if (dcType != DutyCycleType.CONTINUOUS) { - checkNotNull(dcWindowOpen, ManeuverHistoryMetadataKey.DC_WIN_OPEN); - checkNotNull(dcWindowClose, ManeuverHistoryMetadataKey.DC_WIN_CLOSE); - checkNotNull(dcExecStart, ManeuverHistoryMetadataKey.DC_EXEC_START); - checkNotNull(dcExecStop, ManeuverHistoryMetadataKey.DC_EXEC_STOP); - checkNotNull(dcRefTime, ManeuverHistoryMetadataKey.DC_REF_TIME); - checkNotNaN(dcTimePulseDuration, ManeuverHistoryMetadataKey.DC_TIME_PULSE_DURATION); - checkNotNaN(dcTimePulsePeriod, ManeuverHistoryMetadataKey.DC_TIME_PULSE_PERIOD); + checkNotNull(dcWindowOpen, OrbitManeuverHistoryMetadataKey.DC_WIN_OPEN.name()); + checkNotNull(dcWindowClose, OrbitManeuverHistoryMetadataKey.DC_WIN_CLOSE.name()); + checkNotNull(dcExecStart, OrbitManeuverHistoryMetadataKey.DC_EXEC_START.name()); + checkNotNull(dcExecStop, OrbitManeuverHistoryMetadataKey.DC_EXEC_STOP.name()); + checkNotNull(dcRefTime, OrbitManeuverHistoryMetadataKey.DC_REF_TIME.name()); + checkNotNaN(dcTimePulseDuration, OrbitManeuverHistoryMetadataKey.DC_TIME_PULSE_DURATION.name()); + checkNotNaN(dcTimePulsePeriod, OrbitManeuverHistoryMetadataKey.DC_TIME_PULSE_PERIOD.name()); } if (dcType == DutyCycleType.TIME_AND_ANGLE) { - checkNotNull(dcRefDir, ManeuverHistoryMetadataKey.DC_REF_DIR); - checkNotNull(dcBodyFrame, ManeuverHistoryMetadataKey.DC_BODY_FRAME); - checkNotNull(dcBodyTrigger, ManeuverHistoryMetadataKey.DC_BODY_TRIGGER); - checkNotNull(dcPhaseStartAngle, ManeuverHistoryMetadataKey.DC_PA_START_ANGLE); - checkNotNull(dcPhaseStopAngle, ManeuverHistoryMetadataKey.DC_PA_STOP_ANGLE); + checkNotNull(dcRefDir, OrbitManeuverHistoryMetadataKey.DC_REF_DIR.name()); + checkNotNull(dcBodyFrame, OrbitManeuverHistoryMetadataKey.DC_BODY_FRAME.name()); + checkNotNull(dcBodyTrigger, OrbitManeuverHistoryMetadataKey.DC_BODY_TRIGGER.name()); + checkNotNull(dcPhaseStartAngle, OrbitManeuverHistoryMetadataKey.DC_PA_START_ANGLE.name()); + checkNotNull(dcPhaseStopAngle, OrbitManeuverHistoryMetadataKey.DC_PA_STOP_ANGLE.name()); } - checkNotNull(manComposition, ManeuverHistoryMetadataKey.MAN_COMPOSITION); + checkNotNull(manComposition, OrbitManeuverHistoryMetadataKey.MAN_COMPOSITION.name()); if (!manComposition.get(0).isTime()) { throw new OrekitException(OrekitMessages.CCSDS_MANEUVER_MISSING_TIME, manID); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryMetadataKey.java similarity index 85% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryMetadataKey.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryMetadataKey.java index 76b4b6c5d3..4b7a8c9503 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ManeuverHistoryMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,33 +23,33 @@ import org.orekit.utils.units.Unit; -/** Keys for {@link ManeuverHistoryMetadata maneuver history container} entries. +/** Keys for {@link OrbitManeuverHistoryMetadata maneuver history container} entries. * @author Luc Maisonobe * @since 11.0 */ -public enum ManeuverHistoryMetadataKey { +public enum OrbitManeuverHistoryMetadataKey { /** Comment entry. */ COMMENT((token, context, container) -> token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), /** Maneuver identification number. */ - MAN_ID((token, context, container) -> token.processAsNormalizedString(container::setManID)), + MAN_ID((token, context, container) -> token.processAsFreeTextString(container::setManID)), /** Identification number of previous maneuver. */ - MAN_PREV_ID((token, context, container) -> token.processAsNormalizedString(container::setManPrevID)), + MAN_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setManPrevID)), /** Identification number of next maneuver. */ - MAN_NEXT_ID((token, context, container) -> token.processAsNormalizedString(container::setManNextID)), + MAN_NEXT_ID((token, context, container) -> token.processAsFreeTextString(container::setManNextID)), /** Basis of this maneuver history data. */ MAN_BASIS((token, context, container) -> token.processAsEnum(ManBasis.class, container::setManBasis)), /** Identification number of the orbit determination or simulation upon which this maneuver is based.*/ - MAN_BASIS_ID((token, context, container) -> token.processAsNormalizedString(container::setManBasisID)), + MAN_BASIS_ID((token, context, container) -> token.processAsFreeTextString(container::setManBasisID)), /** Identifier of the device used for this maneuver.*/ - MAN_DEVICE_ID((token, context, container) -> token.processAsNormalizedString(container::setManDeviceID)), + MAN_DEVICE_ID((token, context, container) -> token.processAsFreeTextString(container::setManDeviceID)), /** Completion time of previous maneuver. */ MAN_PREV_EPOCH((token, context, container) -> token.processAsDate(container::setManPrevEpoch, context)), @@ -58,10 +58,10 @@ public enum ManeuverHistoryMetadataKey { MAN_NEXT_EPOCH((token, context, container) -> token.processAsDate(container::setManNextEpoch, context)), /** Purposes of the maneuver. */ - MAN_PURPOSE((token, context, container) -> token.processAsNormalizedList(container::setManPurpose)), + MAN_PURPOSE((token, context, container) -> token.processAsFreeTextList(container::setManPurpose)), /** Prediction source on which this maneuver is based. */ - MAN_PRED_SOURCE((token, context, container) -> token.processAsNormalizedString(container::setManPredSource)), + MAN_PRED_SOURCE((token, context, container) -> token.processAsFreeTextString(container::setManPredSource)), /** Reference frame of the maneuver. */ MAN_REF_FRAME((token, context, container) -> token.processAsFrame(container::setManReferenceFrame, context, true, true, false)), @@ -106,14 +106,16 @@ public enum ManeuverHistoryMetadataKey { container::setDcTimePulsePeriod)), /** Reference direction for triggering duty cycle. */ - DC_REF_DIR((token, context, container) -> token.processAsVector(container::setDcRefDir)), + DC_REF_DIR((token, context, container) -> token.processAsVector(Unit.NONE, context.getParsedUnitsBehavior(), + container::setDcRefDir)), /** Spacecraft body frame in which {@link #DC_BODY_TRIGGER} is specified. */ DC_BODY_FRAME((token, context, container) -> token.processAsFrame(f -> container.setDcBodyFrame(f.asSpacecraftBodyFrame()), context, false, false, true)), /** Direction in {@link #DC_BODY_FRAME body frame} for triggering duty cycle. */ - DC_BODY_TRIGGER((token, context, container) -> token.processAsVector(container::setDcBodyTrigger)), + DC_BODY_TRIGGER((token, context, container) -> token.processAsVector(Unit.NONE, context.getParsedUnitsBehavior(), + container::setDcBodyTrigger)), /** Phase angle of pulse start. */ DC_PA_START_ANGLE((token, context, container) -> token.processAsDouble(Unit.DEGREE, context.getParsedUnitsBehavior(), @@ -135,7 +137,7 @@ public enum ManeuverHistoryMetadataKey { /** Simple constructor. * @param processor processing method */ - ManeuverHistoryMetadataKey(final TokenProcessor processor) { + OrbitManeuverHistoryMetadataKey(final TokenProcessor processor) { this.processor = processor; } @@ -145,7 +147,7 @@ public enum ManeuverHistoryMetadataKey { * @param container container to fill * @return true of token was accepted */ - public boolean process(final ParseToken token, final ContextBinding context, final ManeuverHistoryMetadata container) { + public boolean process(final ParseToken token, final ContextBinding context, final OrbitManeuverHistoryMetadata container) { return processor.process(token, context, container); } @@ -157,7 +159,7 @@ interface TokenProcessor { * @param container container to fill * @return true of token was accepted */ - boolean process(ParseToken token, ContextBinding context, ManeuverHistoryMetadata container); + boolean process(ParseToken token, ContextBinding context, OrbitManeuverHistoryMetadata container); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryWriter.java new file mode 100644 index 0000000000..5a8314ae2f --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitManeuverHistoryWriter.java @@ -0,0 +1,159 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.odm.ocm; + +import java.io.IOException; +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.files.ccsds.definitions.DutyCycleType; +import org.orekit.files.ccsds.definitions.TimeConverter; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.AccurateFormatter; +import org.orekit.utils.units.Unit; + +/** Writer for maneuvers history data. + * @author Luc Maisonobe + * @since 11.0 + */ +class OrbitManeuverHistoryWriter extends AbstractWriter { + + /** Maneuvers history block. */ + private final OrbitManeuverHistory history; + + /** Converter for dates. */ + private final TimeConverter timeConverter; + + /** Create a writer. + * @param maneuverHistory maneuvers history to write + * @param timeConverter converter for dates + */ + OrbitManeuverHistoryWriter(final OrbitManeuverHistory maneuverHistory, + final TimeConverter timeConverter) { + super(OcmDataSubStructureKey.man.name(), OcmDataSubStructureKey.MAN.name()); + this.history = maneuverHistory; + this.timeConverter = timeConverter; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // maneuvers history block + final OrbitManeuverHistoryMetadata metadata = history.getMetadata(); + generator.writeComments(metadata.getComments()); + + // identifiers + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_ID.name(), metadata.getManID(), null, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PREV_ID.name(), metadata.getManPrevID(), null, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_NEXT_ID.name(), metadata.getManNextID(), null, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_BASIS.name(), metadata.getManBasis(), false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_BASIS_ID.name(), metadata.getManBasisID(), null, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_DEVICE_ID.name(), metadata.getManDeviceID(), null, false); + + // time + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PREV_EPOCH.name(), timeConverter, metadata.getManPrevEpoch(), true, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_NEXT_EPOCH.name(), timeConverter, metadata.getManNextEpoch(), true, false); + + // references + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PURPOSE.name(), metadata.getManPurpose(), false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PRED_SOURCE.name(), metadata.getManPredSource(), null, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_REF_FRAME.name(), metadata.getManReferenceFrame().getName(), null, false); + if (!metadata.getManFrameEpoch().equals(timeConverter.getReferenceDate()) && + metadata.getManReferenceFrame().asOrbitRelativeFrame() == null && + metadata.getManReferenceFrame().asSpacecraftBodyFrame() == null) { + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_FRAME_EPOCH.name(), timeConverter, metadata.getManFrameEpoch(), true, false); + } + if (metadata.getGravitationalAssist() != null) { + generator.writeEntry(OrbitManeuverHistoryMetadataKey.GRAV_ASSIST_NAME.name(), metadata.getGravitationalAssist().getName(), null, false); + } + + // duty cycle + final boolean notContinuous = metadata.getDcType() != DutyCycleType.CONTINUOUS; + final boolean timeAndAngle = metadata.getDcType() == DutyCycleType.TIME_AND_ANGLE; + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_TYPE.name(), metadata.getDcType(), false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_WIN_OPEN.name(), timeConverter, metadata.getDcWindowOpen(), false, notContinuous); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_WIN_CLOSE.name(), timeConverter, metadata.getDcWindowClose(), false, notContinuous); + if (metadata.getDcMinCycles() >= 0) { + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_MIN_CYCLES.name(), metadata.getDcMinCycles(), false); + } + if (metadata.getDcMaxCycles() >= 0) { + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_MAX_CYCLES.name(), metadata.getDcMaxCycles(), false); + } + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_EXEC_START.name(), timeConverter, metadata.getDcExecStart(), false, notContinuous); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_EXEC_STOP.name(), timeConverter, metadata.getDcExecStop(), false, notContinuous); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_REF_TIME.name(), timeConverter, metadata.getDcRefTime(), false, notContinuous); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_TIME_PULSE_DURATION.name(), metadata.getDcTimePulseDuration(), Unit.SECOND, notContinuous); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_TIME_PULSE_PERIOD.name(), metadata.getDcTimePulsePeriod(), Unit.SECOND, notContinuous); + if (timeAndAngle) { + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_REF_DIR.name(), toString(metadata.getDcRefDir()), null, timeAndAngle); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_BODY_FRAME.name(), + metadata.getDcBodyFrame().toString().replace(' ', '_'), + null, timeAndAngle); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_BODY_TRIGGER.name(), toString(metadata.getDcBodyTrigger()), null, timeAndAngle); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_PA_START_ANGLE.name(), metadata.getDcPhaseStartAngle(), Unit.DEGREE, timeAndAngle); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_PA_STOP_ANGLE.name(), metadata.getDcPhaseStopAngle(), Unit.DEGREE, timeAndAngle); + } + + // elements + final List types = metadata.getManComposition(); + final StringBuilder composition = new StringBuilder(); + for (int i = 0; i < types.size(); ++i) { + if (i > 0) { + composition.append(','); + } + composition.append(types.get(i).name()); + } + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_COMPOSITION.name(), composition.toString(), null, false); + generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_UNITS.name(), generator.unitsListToString(metadata.getManUnits()), null, false); + + // data + for (final OrbitManeuver maneuver : history.getManeuvers()) { + final StringBuilder line = new StringBuilder(); + for (int i = 0; i < types.size(); ++i) { + if (i > 0) { + line.append(' '); + } + line.append(types.get(i).outputField(timeConverter, maneuver)); + } + if (generator.getFormat() == FileFormat.XML) { + generator.writeEntry(Ocm.MAN_LINE, line.toString(), null, true); + } else { + generator.writeRawData(line); + generator.newLine(); + } + } + } + + /** Convert a vector to a space separated string. + * @param vector vector to convert + * @return orrespondong string + */ + private String toString(final Vector3D vector) { + final StringBuilder builder = new StringBuilder(); + builder.append(AccurateFormatter.format(Unit.ONE.fromSI(vector.getX()))); + builder.append(' '); + builder.append(AccurateFormatter.format(Unit.ONE.fromSI(vector.getY()))); + builder.append(' '); + builder.append(AccurateFormatter.format(Unit.ONE.fromSI(vector.getZ()))); + return builder.toString(); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalProperties.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalProperties.java similarity index 99% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalProperties.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalProperties.java index da72d951fe..6dc22caf53 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalProperties.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalProperties.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,7 +30,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class PhysicalProperties extends CommonPhysicalProperties { +public class OrbitPhysicalProperties extends CommonPhysicalProperties { /** Satellite manufacturer name. */ private String manufacturer; @@ -110,7 +110,7 @@ public class PhysicalProperties extends CommonPhysicalProperties { /** Simple constructor. * @param epochT0 T0 epoch from file metadata */ - PhysicalProperties(final AbsoluteDate epochT0) { + public OrbitPhysicalProperties(final AbsoluteDate epochT0) { // Call to CommonPhysicalProperties constructor super(); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalPropertiesKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalPropertiesKey.java similarity index 95% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalPropertiesKey.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalPropertiesKey.java index a375817299..9face97f22 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalPropertiesKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalPropertiesKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,24 +23,24 @@ import org.orekit.utils.units.Unit; -/** Keys for {@link PhysicalProperties physical properties data} entries. +/** Keys for {@link OrbitPhysicalProperties physical properties data} entries. * @author Luc Maisonobe * @since 11.0 */ -public enum PhysicalPropertiesKey { +public enum OrbitPhysicalPropertiesKey { /** Comment entry. */ COMMENT((token, context, container) -> token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), /** Satellite manufacturer name. */ - MANUFACTURER((token, context, container) -> token.processAsNormalizedString(container::setManufacturer)), + MANUFACTURER((token, context, container) -> token.processAsFreeTextString(container::setManufacturer)), /** Bus model name. */ - BUS_MODEL((token, context, container) -> token.processAsNormalizedString(container::setBusModel)), + BUS_MODEL((token, context, container) -> token.processAsFreeTextString(container::setBusModel)), /** Other space objects this object is docked to. */ - DOCKED_WITH((token, context, container) -> token.processAsNormalizedList(container::setDockedWith)), + DOCKED_WITH((token, context, container) -> token.processAsFreeTextList(container::setDockedWith)), /** Attitude-independent drag cross-sectional area, not already into attitude-dependent area along OEB. */ DRAG_CONST_AREA((token, context, container) -> token.processAsDouble(Units.M2, context.getParsedUnitsBehavior(), @@ -169,10 +169,10 @@ public enum PhysicalPropertiesKey { container::setReflectance)), /** Attitude control mode. */ - ATT_CONTROL_MODE((token, context, container) -> token.processAsNormalizedString(container::setAttitudeControlMode)), + ATT_CONTROL_MODE((token, context, container) -> token.processAsFreeTextString(container::setAttitudeControlMode)), /** Type of actuator for attitude control. */ - ATT_ACTUATOR_TYPE((token, context, container) -> token.processAsNormalizedString(container::setAttitudeActuatorType)), + ATT_ACTUATOR_TYPE((token, context, container) -> token.processAsFreeTextString(container::setAttitudeActuatorType)), /** Accuracy of attitude knowledge. */ ATT_KNOWLEDGE((token, context, container) -> token.processAsDouble(Unit.DEGREE, context.getParsedUnitsBehavior(), container::setAttitudeKnowledgeAccuracy)), @@ -229,7 +229,7 @@ public enum PhysicalPropertiesKey { /** Simple constructor. * @param processor processing method */ - PhysicalPropertiesKey(final TokenProcessor processor) { + OrbitPhysicalPropertiesKey(final TokenProcessor processor) { this.processor = processor; } @@ -239,7 +239,7 @@ public enum PhysicalPropertiesKey { * @param data data to fill * @return true of token was accepted */ - public boolean process(final ParseToken token, final ContextBinding context, final PhysicalProperties data) { + public boolean process(final ParseToken token, final ContextBinding context, final OrbitPhysicalProperties data) { return processor.process(token, context, data); } @@ -251,7 +251,7 @@ interface TokenProcessor { * @param data data to fill * @return true of token was accepted */ - boolean process(ParseToken token, ContextBinding context, PhysicalProperties data); + boolean process(ParseToken token, ContextBinding context, OrbitPhysicalProperties data); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalPropertiesWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalPropertiesWriter.java new file mode 100644 index 0000000000..b3db0e0297 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/OrbitPhysicalPropertiesWriter.java @@ -0,0 +1,138 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.ccsds.ndm.odm.ocm; + +import java.io.IOException; + +import org.hipparchus.linear.RealMatrix; +import org.orekit.files.ccsds.definitions.TimeConverter; +import org.orekit.files.ccsds.definitions.Units; +import org.orekit.files.ccsds.section.AbstractWriter; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.utils.units.Unit; + +/** Writer for physical properties data. + * @author Luc Maisonobe + * @since 11.0 + */ +class OrbitPhysicalPropertiesWriter extends AbstractWriter { + + /** Physical properties block. */ + private final OrbitPhysicalProperties phys; + + /** Converter for dates. */ + private final TimeConverter timeConverter; + + /** Create a writer. + * @param phys physical properties to write + * @param timeConverter converter for dates + */ + OrbitPhysicalPropertiesWriter(final OrbitPhysicalProperties phys, final TimeConverter timeConverter) { + super(OcmDataSubStructureKey.phys.name(), OcmDataSubStructureKey.PHYS.name()); + this.phys = phys; + this.timeConverter = timeConverter; + } + + /** {@inheritDoc} */ + @Override + protected void writeContent(final Generator generator) throws IOException { + + // physical properties block + generator.writeComments(phys.getComments()); + + generator.writeEntry(OrbitPhysicalPropertiesKey.MANUFACTURER.name(), phys.getManufacturer(), null, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.BUS_MODEL.name(), phys.getBusModel(), null, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.DOCKED_WITH.name(), phys.getDockedWith(), false); + + // drag + generator.writeEntry(OrbitPhysicalPropertiesKey.DRAG_CONST_AREA.name(), phys.getDragConstantArea(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.DRAG_COEFF_NOM.name(), phys.getDragCoefficient(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.DRAG_UNCERTAINTY.name(), phys.getDragUncertainty(), Unit.PERCENT, false); + + // mass + generator.writeEntry(OrbitPhysicalPropertiesKey.INITIAL_WET_MASS.name(), phys.getInitialWetMass(), Unit.KILOGRAM, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.WET_MASS.name(), phys.getWetMass(), Unit.KILOGRAM, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.DRY_MASS.name(), phys.getDryMass(), Unit.KILOGRAM, false); + + // Optimally Enclosing Box + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_PARENT_FRAME.name(), phys.getOebParentFrame().getName(), null, false); + if (!phys.getOebParentFrameEpoch().equals(timeConverter.getReferenceDate()) && + phys.getOebParentFrame().asOrbitRelativeFrame() == null && + phys.getOebParentFrame().asSpacecraftBodyFrame() == null) { + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_PARENT_FRAME_EPOCH.name(), timeConverter, phys.getOebParentFrameEpoch(), true, false); + } + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_Q1.name(), phys.getOebQ().getQ1(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_Q2.name(), phys.getOebQ().getQ2(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_Q3.name(), phys.getOebQ().getQ3(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_QC.name(), phys.getOebQ().getQ0(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_MAX.name(), phys.getOebMax(), Unit.METRE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_INT.name(), phys.getOebIntermediate(), Unit.METRE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.OEB_MIN.name(), phys.getOebMin(), Unit.METRE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.AREA_ALONG_OEB_MAX.name(), phys.getOebAreaAlongMax(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.AREA_ALONG_OEB_INT.name(), phys.getOebAreaAlongIntermediate(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.AREA_ALONG_OEB_MIN.name(), phys.getOebAreaAlongMin(), Units.M2, false); + + // collision probability + generator.writeEntry(OrbitPhysicalPropertiesKey.AREA_MIN_FOR_PC.name(), phys.getMinAreaForCollisionProbability(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.AREA_MAX_FOR_PC.name(), phys.getMaxAreaForCollisionProbability(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.AREA_TYP_FOR_PC.name(), phys.getTypAreaForCollisionProbability(), Units.M2, false); + + // radar cross section + generator.writeEntry(OrbitPhysicalPropertiesKey.RCS.name(), phys.getRcs(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.RCS_MIN.name(), phys.getMinRcs(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.RCS_MAX.name(), phys.getMaxRcs(), Units.M2, false); + + // solar radiation pressure + generator.writeEntry(OrbitPhysicalPropertiesKey.SRP_CONST_AREA.name(), phys.getSrpConstantArea(), Units.M2, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.SOLAR_RAD_COEFF.name(), phys.getSrpCoefficient(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.SOLAR_RAD_UNCERTAINTY.name(), phys.getSrpUncertainty(), Unit.PERCENT, false); + + // visual magnitude + generator.writeEntry(OrbitPhysicalPropertiesKey.VM_ABSOLUTE.name(), phys.getVmAbsolute(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.VM_APPARENT_MIN.name(), phys.getVmApparentMin(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.VM_APPARENT.name(), phys.getVmApparent(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.VM_APPARENT_MAX.name(), phys.getVmApparentMax(), Unit.ONE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.REFLECTANCE.name(), phys.getReflectance(), Unit.ONE, false); + + // attitude + generator.writeEntry(OrbitPhysicalPropertiesKey.ATT_CONTROL_MODE.name(), phys.getAttitudeControlMode(), null, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.ATT_ACTUATOR_TYPE.name(), phys.getAttitudeActuatorType(), null, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.ATT_KNOWLEDGE.name(), phys.getAttitudeKnowledgeAccuracy(), Unit.DEGREE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.ATT_CONTROL.name(), phys.getAttitudeControlAccuracy(), Unit.DEGREE, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.ATT_POINTING.name(), phys.getAttitudePointingAccuracy(), Unit.DEGREE, false); + + // maneuvers + generator.writeEntry(OrbitPhysicalPropertiesKey.AVG_MANEUVER_FREQ.name(), phys.getManeuversFrequency(), Units.NB_PER_Y, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.MAX_THRUST.name(), phys.getMaxThrust(), Unit.NEWTON, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.DV_BOL.name(), phys.getBolDv(), Units.KM_PER_S, false); + generator.writeEntry(OrbitPhysicalPropertiesKey.DV_REMAINING.name(), phys.getRemainingDv(), Units.KM_PER_S, false); + + // inertia + final RealMatrix inertia = phys.getInertiaMatrix(); + if (inertia != null) { + generator.writeEntry(OrbitPhysicalPropertiesKey.IXX.name(), inertia.getEntry(0, 0), Units.KG_M2, true); + generator.writeEntry(OrbitPhysicalPropertiesKey.IYY.name(), inertia.getEntry(1, 1), Units.KG_M2, true); + generator.writeEntry(OrbitPhysicalPropertiesKey.IZZ.name(), inertia.getEntry(2, 2), Units.KG_M2, true); + generator.writeEntry(OrbitPhysicalPropertiesKey.IXY.name(), inertia.getEntry(0, 1), Units.KG_M2, true); + generator.writeEntry(OrbitPhysicalPropertiesKey.IXZ.name(), inertia.getEntry(0, 2), Units.KG_M2, true); + generator.writeEntry(OrbitPhysicalPropertiesKey.IYZ.name(), inertia.getEntry(1, 2), Units.KG_M2, true); + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ordering.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ordering.java index 6718fd1c39..393de9a4ee 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ordering.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Ordering.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,7 @@ */ package org.orekit.files.ccsds.ndm.odm.ocm; -/** Keys for {@link Covariance} elements ordering. +/** Keys for {@link OrbitCovariance} elements ordering. * @author Luc Maisonobe * @since 11.0 */ @@ -74,7 +74,7 @@ void update(final CovarianceIndexer indexer) { }, - /** Full symmetrix Matrix. */ + /** Full symmetric Matrix. */ FULL { /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Perturbations.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Perturbations.java index 93320ad273..681b6c3f7e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Perturbations.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/Perturbations.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsKey.java index 05b2b16ffa..7e7eaf755d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -37,7 +37,7 @@ public enum PerturbationsKey { token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), /** Name of atmospheric model. */ - ATMOSPHERIC_MODEL((token, context, container) -> token.processAsNormalizedString(container::setAtmosphericModel)), + ATMOSPHERIC_MODEL((token, context, container) -> token.processAsFreeTextString(container::setAtmosphericModel)), /** Gravity model. */ GRAVITY_MODEL(new GravityProcessor()), @@ -63,16 +63,16 @@ public enum PerturbationsKey { container::setOblateFlattening)), /** Ocean tides model. */ - OCEAN_TIDES_MODEL((token, context, container) -> token.processAsNormalizedString(container::setOceanTidesModel)), + OCEAN_TIDES_MODEL((token, context, container) -> token.processAsFreeTextString(container::setOceanTidesModel)), /** Solid tides model. */ - SOLID_TIDES_MODEL((token, context, container) -> token.processAsNormalizedString(container::setSolidTidesModel)), + SOLID_TIDES_MODEL((token, context, container) -> token.processAsFreeTextString(container::setSolidTidesModel)), /** Reduction theory used for precession and nutation modeling. */ - REDUCTION_THEORY((token, context, container) -> token.processAsNormalizedString(container::setReductionTheory)), + REDUCTION_THEORY((token, context, container) -> token.processAsFreeTextString(container::setReductionTheory)), /** Albedo model. */ - ALBEDO_MODEL((token, context, container) -> token.processAsNormalizedString(container::setAlbedoModel)), + ALBEDO_MODEL((token, context, container) -> token.processAsFreeTextString(container::setAlbedoModel)), /** Albedo grid size. */ ALBEDO_GRID_SIZE((token, context, container) -> token.processAsInteger(container::setAlbedoGridSize)), @@ -85,16 +85,16 @@ public enum PerturbationsKey { context.getDataContext().getCelestialBodies())), /** Solar Radiation Pressure model. */ - SRP_MODEL((token, context, container) -> token.processAsNormalizedString(container::setSrpModel)), + SRP_MODEL((token, context, container) -> token.processAsFreeTextString(container::setSrpModel)), /** Space Weather data source. */ - SW_DATA_SOURCE((token, context, container) -> token.processAsNormalizedString(container::setSpaceWeatherSource)), + SW_DATA_SOURCE((token, context, container) -> token.processAsFreeTextString(container::setSpaceWeatherSource)), /** Epoch of the Space Weather data. */ SW_DATA_EPOCH((token, context, container) -> token.processAsDate(container::setSpaceWeatherEpoch, context)), /** Interpolation method for Space Weather data. */ - SW_INTERP_METHOD((token, context, container) -> token.processAsNormalizedString(container::setInterpMethodSW)), + SW_INTERP_METHOD((token, context, container) -> token.processAsFreeTextString(container::setInterpMethodSW)), /** Fixed (time invariant) value of the planetary 3-hour-range geomagnetic index Kₚ. */ FIXED_GEOMAG_KP((token, context, container) -> token.processAsDouble(Units.NANO_TESLA, context.getParsedUnitsBehavior(), @@ -185,7 +185,7 @@ private static class GravityProcessor implements TokenProcessor { @Override public boolean process(final ParseToken token, final ContextBinding context, final Perturbations container) { if (token.getType() == TokenType.ENTRY) { - final Matcher matcher = GRAVITY_PATTERN.matcher(token.getContentAsNormalizedString()); + final Matcher matcher = GRAVITY_PATTERN.matcher(token.getRawContent()); if (!matcher.matches()) { throw token.generateException(null); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsWriter.java index 749b4cad61..dafff79a92 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PerturbationsWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -103,7 +103,7 @@ protected void writeContent(final Generator generator) throws IOException { // data source generator.writeEntry(PerturbationsKey.SW_DATA_SOURCE.name(), perturbations.getSpaceWeatherSource(), null, false); - generator.writeEntry(PerturbationsKey.SW_DATA_EPOCH.name(), timeConverter, perturbations.getSpaceWeatherEpoch(), false); + generator.writeEntry(PerturbationsKey.SW_DATA_EPOCH.name(), timeConverter, perturbations.getSpaceWeatherEpoch(), true, false); generator.writeEntry(PerturbationsKey.SW_INTERP_METHOD.name(), perturbations.getInterpMethodSW(), null, false); generator.writeEntry(PerturbationsKey.FIXED_GEOMAG_KP.name(), perturbations.getFixedGeomagneticKp(), Units.NANO_TESLA, false); generator.writeEntry(PerturbationsKey.FIXED_GEOMAG_AP.name(), perturbations.getFixedGeomagneticAp(), Units.NANO_TESLA, false); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalPropertiesWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalPropertiesWriter.java deleted file mode 100644 index 4fb1b3af7a..0000000000 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/PhysicalPropertiesWriter.java +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.orekit.files.ccsds.ndm.odm.ocm; - -import java.io.IOException; - -import org.hipparchus.linear.RealMatrix; -import org.orekit.files.ccsds.definitions.TimeConverter; -import org.orekit.files.ccsds.definitions.Units; -import org.orekit.files.ccsds.section.AbstractWriter; -import org.orekit.files.ccsds.utils.generation.Generator; -import org.orekit.utils.units.Unit; - -/** Writer for physical properties data. - * @author Luc Maisonobe - * @since 11.0 - */ -class PhysicalPropertiesWriter extends AbstractWriter { - - /** Physical properties block. */ - private final PhysicalProperties phys; - - /** Converter for dates. */ - private final TimeConverter timeConverter; - - /** Create a writer. - * @param phys physical properties to write - * @param timeConverter converter for dates - */ - PhysicalPropertiesWriter(final PhysicalProperties phys, final TimeConverter timeConverter) { - super(OcmDataSubStructureKey.phys.name(), OcmDataSubStructureKey.PHYS.name()); - this.phys = phys; - this.timeConverter = timeConverter; - } - - /** {@inheritDoc} */ - @Override - protected void writeContent(final Generator generator) throws IOException { - - // physical properties block - generator.writeComments(phys.getComments()); - - generator.writeEntry(PhysicalPropertiesKey.MANUFACTURER.name(), phys.getManufacturer(), null, false); - generator.writeEntry(PhysicalPropertiesKey.BUS_MODEL.name(), phys.getBusModel(), null, false); - generator.writeEntry(PhysicalPropertiesKey.DOCKED_WITH.name(), phys.getDockedWith(), false); - - // drag - generator.writeEntry(PhysicalPropertiesKey.DRAG_CONST_AREA.name(), phys.getDragConstantArea(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.DRAG_COEFF_NOM.name(), phys.getDragCoefficient(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.DRAG_UNCERTAINTY.name(), phys.getDragUncertainty(), Unit.PERCENT, false); - - // mass - generator.writeEntry(PhysicalPropertiesKey.INITIAL_WET_MASS.name(), phys.getInitialWetMass(), Unit.KILOGRAM, false); - generator.writeEntry(PhysicalPropertiesKey.WET_MASS.name(), phys.getWetMass(), Unit.KILOGRAM, false); - generator.writeEntry(PhysicalPropertiesKey.DRY_MASS.name(), phys.getDryMass(), Unit.KILOGRAM, false); - - // Optimally Enclosing Box - generator.writeEntry(PhysicalPropertiesKey.OEB_PARENT_FRAME.name(), phys.getOebParentFrame().getName(), null, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_PARENT_FRAME_EPOCH.name(), timeConverter, phys.getOebParentFrameEpoch(), false); - generator.writeEntry(PhysicalPropertiesKey.OEB_Q1.name(), phys.getOebQ().getQ1(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_Q2.name(), phys.getOebQ().getQ2(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_Q3.name(), phys.getOebQ().getQ3(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_QC.name(), phys.getOebQ().getQ0(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_MAX.name(), phys.getOebMax(), Unit.METRE, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_INT.name(), phys.getOebIntermediate(), Unit.METRE, false); - generator.writeEntry(PhysicalPropertiesKey.OEB_MIN.name(), phys.getOebMin(), Unit.METRE, false); - generator.writeEntry(PhysicalPropertiesKey.AREA_ALONG_OEB_MAX.name(), phys.getOebAreaAlongMax(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.AREA_ALONG_OEB_INT.name(), phys.getOebAreaAlongIntermediate(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.AREA_ALONG_OEB_MIN.name(), phys.getOebAreaAlongMin(), Units.M2, false); - - // collision probability - generator.writeEntry(PhysicalPropertiesKey.AREA_MIN_FOR_PC.name(), phys.getMinAreaForCollisionProbability(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.AREA_MAX_FOR_PC.name(), phys.getMaxAreaForCollisionProbability(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.AREA_TYP_FOR_PC.name(), phys.getTypAreaForCollisionProbability(), Units.M2, false); - - // radar cross section - generator.writeEntry(PhysicalPropertiesKey.RCS.name(), phys.getRcs(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.RCS_MIN.name(), phys.getMinRcs(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.RCS_MAX.name(), phys.getMaxRcs(), Units.M2, false); - - // solar radiation pressure - generator.writeEntry(PhysicalPropertiesKey.SRP_CONST_AREA.name(), phys.getSrpConstantArea(), Units.M2, false); - generator.writeEntry(PhysicalPropertiesKey.SOLAR_RAD_COEFF.name(), phys.getSrpCoefficient(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.SOLAR_RAD_UNCERTAINTY.name(), phys.getSrpUncertainty(), Unit.PERCENT, false); - - // visual magnitude - generator.writeEntry(PhysicalPropertiesKey.VM_ABSOLUTE.name(), phys.getVmAbsolute(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.VM_APPARENT_MIN.name(), phys.getVmApparentMin(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.VM_APPARENT.name(), phys.getVmApparent(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.VM_APPARENT_MAX.name(), phys.getVmApparentMax(), Unit.ONE, false); - generator.writeEntry(PhysicalPropertiesKey.REFLECTANCE.name(), phys.getReflectance(), Unit.ONE, false); - - // attitude - generator.writeEntry(PhysicalPropertiesKey.ATT_CONTROL_MODE.name(), phys.getAttitudeControlMode(), null, false); - generator.writeEntry(PhysicalPropertiesKey.ATT_ACTUATOR_TYPE.name(), phys.getAttitudeActuatorType(), null, false); - generator.writeEntry(PhysicalPropertiesKey.ATT_KNOWLEDGE.name(), phys.getAttitudeKnowledgeAccuracy(), Unit.DEGREE, false); - generator.writeEntry(PhysicalPropertiesKey.ATT_CONTROL.name(), phys.getAttitudeControlAccuracy(), Unit.DEGREE, false); - generator.writeEntry(PhysicalPropertiesKey.ATT_POINTING.name(), phys.getAttitudePointingAccuracy(), Unit.DEGREE, false); - - // maneuvers - generator.writeEntry(PhysicalPropertiesKey.AVG_MANEUVER_FREQ.name(), phys.getManeuversFrequency(), Units.NB_PER_Y, false); - generator.writeEntry(PhysicalPropertiesKey.MAX_THRUST.name(), phys.getMaxThrust(), Unit.NEWTON, false); - generator.writeEntry(PhysicalPropertiesKey.DV_BOL.name(), phys.getBolDv(), Units.KM_PER_S, false); - generator.writeEntry(PhysicalPropertiesKey.DV_REMAINING.name(), phys.getRemainingDv(), Units.KM_PER_S, false); - - // inertia - final RealMatrix inertia = phys.getInertiaMatrix(); - if (inertia != null) { - generator.writeEntry(PhysicalPropertiesKey.IXX.name(), inertia.getEntry(0, 0), Units.KG_M2, true); - generator.writeEntry(PhysicalPropertiesKey.IYY.name(), inertia.getEntry(1, 1), Units.KG_M2, true); - generator.writeEntry(PhysicalPropertiesKey.IZZ.name(), inertia.getEntry(2, 2), Units.KG_M2, true); - generator.writeEntry(PhysicalPropertiesKey.IXY.name(), inertia.getEntry(0, 1), Units.KG_M2, true); - generator.writeEntry(PhysicalPropertiesKey.IXZ.name(), inertia.getEntry(0, 2), Units.KG_M2, true); - generator.writeEntry(PhysicalPropertiesKey.IYZ.name(), inertia.getEntry(1, 2), Units.KG_M2, true); - } - - } - -} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ShadowModel.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ShadowModel.java index e63987df1f..3646c7f44a 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ShadowModel.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/ShadowModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/StreamingOcmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/StreamingOcmWriter.java new file mode 100644 index 0000000000..b1b3770ac8 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/StreamingOcmWriter.java @@ -0,0 +1,307 @@ +/* Contributed in the public domain. + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.odm.ocm; + +import java.io.IOException; +import java.util.Collections; + +import org.hipparchus.exception.LocalizedCoreFormats; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.definitions.FrameFacade; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; +import org.orekit.files.ccsds.section.XmlStructureKey; +import org.orekit.files.ccsds.utils.FileFormat; +import org.orekit.files.ccsds.utils.generation.Generator; +import org.orekit.frames.Frame; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.sampling.OrekitFixedStepHandler; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * A writer for OCM files. + * + *

          Each instance corresponds to a single Orbit Comprehensive Message. + * A new OCM ephemeris trajectory state history block is started by calling + * {@link #newBlock()}. + *

          + * + *

          + * This writer is intended to write only trajectory state history blocks. + * It does not writes physical properties, covariance data, maneuver data, + * perturbations parameters, orbit determination or user-defined parameters. + * If these blocks are needed, then {@link OcmWriter OcmWriter} must be + * used as it handles all OCM data blocks. + *

          + *

          + * The trajectory blocks metadata identifiers ({@code TRAJ_ID}, + * {@code TRAJ_PREV_ID}, {@code TRAJ_NEXT_ID}) are updated automatically + * using {@link TrajectoryStateHistoryMetadata#incrementTrajID(String)}, + * so users should generally only set {@link TrajectoryStateHistoryMetadata#setTrajID(String)} + * in the template. + *

          + * + *

          + * The blocks returned by this class can be used as step handlers for a {@link Propagator}. + *

          + * + *
          {@code
          + * Propagator propagator = ...; // pre-configured propagator
          + * OCMWriter  ocmWriter  = ...; // pre-configured writer
          + *   try (Generator out = ...;  // set-up output stream
          + *        StreamingOcmWriter sw = new StreamingOcmWriter(out, ocmWriter, header, metadata, template)) { // set-up streaming writer
          + *
          + *     // write block 1
          + *     propagator.getMultiplexer().add(step, sw.newBlock());
          + *     propagator.propagate(startDate1, stopDate1);
          + *
          + *     ...
          + *
          + *     // write block n
          + *     propagator.getMultiplexer().clear();
          + *     propagator.getMultiplexer().add(step, sw.newBlock());
          + *     propagator.propagate(startDateN, stopDateN);
          + *
          + *   }
          + * }
          + * + * + * @author Luc Maisonobe + * @see OcmWriter + * @see EphemerisOcmWriter + * @since 12.0 + */ +public class StreamingOcmWriter implements AutoCloseable { + + /** Generator for OCM output. */ + private final Generator generator; + + /** Writer for the OCM message format. */ + private final OcmWriter writer; + + /** Writer for the trajectory data block. */ + private TrajectoryStateHistoryWriter trajectoryWriter; + + /** Header. */ + private final OdmHeader header; + + /** Current metadata. */ + private final OcmMetadata metadata; + + /** Current trajectory metadata. */ + private final TrajectoryStateHistoryMetadata trajectoryMetadata; + + /** If the propagator's frame should be used. */ + private final boolean useAttitudeFrame; + + /** Indicator for writing header. */ + private boolean headerWritePending; + + /** Last Z coordinate seen. */ + private double lastZ; + + /** + * Construct a writer that for each segment uses the reference frame of the + * first state's attitude. + * + * @param generator generator for OCM output + * @param writer writer for the OCM message format + * @param header file header (may be null) + * @param metadata file metadata + * @param template template for trajectory metadata + * @see #StreamingOcmWriter(Generator, OcmWriter, OdmHeader, OcmMetadata, TrajectoryStateHistoryMetadata, boolean) + */ + public StreamingOcmWriter(final Generator generator, final OcmWriter writer, + final OdmHeader header, final OcmMetadata metadata, + final TrajectoryStateHistoryMetadata template) { + this(generator, writer, header, metadata, template, true); + } + + /** + * Simple constructor. + * + * @param generator generator for OCM output + * @param writer writer for the OCM message format + * @param header file header (may be null) + * @param metadata file metadata + * @param template template for trajectory metadata + * @param useAttitudeFrame if {@code true} then the reference frame for + * each segment is taken from the first state's + * attitude. Otherwise the {@code template}'s + * reference frame is used, {@link + * TrajectoryStateHistoryMetadata#getTrajReferenceFrame()}. + */ + public StreamingOcmWriter(final Generator generator, final OcmWriter writer, + final OdmHeader header, final OcmMetadata metadata, + final TrajectoryStateHistoryMetadata template, + final boolean useAttitudeFrame) { + this.generator = generator; + this.writer = writer; + this.header = header; + this.metadata = metadata; + this.trajectoryMetadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); + this.useAttitudeFrame = useAttitudeFrame; + this.headerWritePending = true; + this.lastZ = Double.NaN; + } + + /** + * Create a writer for a new OCM trajectory state history block. + *

          The returned writer can only write a single trajectory state history block in an OCM. + * This method must be called to create a writer for each trajectory state history block. + * @return a new OCM trajectory state history block writer, ready for use. + */ + public BlockWriter newBlock() { + return new BlockWriter(); + } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException { + writer.writeFooter(generator); + } + + /** A writer for a trajectory state history block of an OCM. */ + public class BlockWriter implements OrekitFixedStepHandler { + + /** Reference frame of this segment. */ + private Frame frame; + + /** Elements type. */ + private OrbitElementsType type; + + /** Number of ascending nodes crossings. */ + private int crossings; + + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public BlockWriter() { + // nothing to do + } + + /** + * {@inheritDoc} + * + *

          Writes the header automatically on first segment. + * Sets the {@link OcmMetadataKey#START_TIME} and {@link OcmMetadataKey#STOP_TIME} in this + * block metadata if not already set by the user. + */ + @Override + public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) { + try { + final AbsoluteDate date = s0.getDate(); + if (t.isBefore(date)) { + throw new OrekitException(OrekitMessages.NON_CHRONOLOGICALLY_SORTED_ENTRIES, + date, t, date.durationFrom(t)); + } + + if (headerWritePending) { + // we write the header and metadata only for the first segment + writer.writeHeader(generator, header); + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(XmlStructureKey.segment.name()); + } + new OcmMetadataWriter(metadata, writer.getTimeConverter()).write(generator); + if (generator.getFormat() == FileFormat.XML) { + generator.enterSection(XmlStructureKey.data.name()); + } + headerWritePending = false; + } + + trajectoryMetadata.setTrajNextID(TrajectoryStateHistoryMetadata.incrementTrajID(trajectoryMetadata.getTrajID())); + trajectoryMetadata.setUseableStartTime(date); + trajectoryMetadata.setUseableStopTime(t); + if (useAttitudeFrame) { + frame = s0.getAttitude().getReferenceFrame(); + trajectoryMetadata.setTrajReferenceFrame(FrameFacade.map(frame)); + } else { + frame = trajectoryMetadata.getTrajReferenceFrame().asFrame(); + } + + crossings = 0; + type = trajectoryMetadata.getTrajType(); + + final OneAxisEllipsoid body = trajectoryMetadata.getTrajType() == OrbitElementsType.GEODETIC ? + new OneAxisEllipsoid(writer.getEquatorialRadius(), + writer.getFlattening(), + trajectoryMetadata.getTrajReferenceFrame().asFrame()) : + null; + trajectoryWriter = new TrajectoryStateHistoryWriter(new TrajectoryStateHistory(trajectoryMetadata, + Collections.emptyList(), + body, s0.getMu()), + writer.getTimeConverter()); + trajectoryWriter.enterSection(generator); + trajectoryWriter.writeMetadata(generator); + + } catch (IOException e) { + throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage()); + } + } + + /** {@inheritDoc}. */ + @Override + public void handleStep(final SpacecraftState currentState) { + try { + final TimeStampedPVCoordinates pv = + currentState.getPVCoordinates(frame); + if (lastZ < 0.0 && pv.getPosition().getZ() >= 0.0) { + // we crossed ascending node + ++crossings; + } + lastZ = pv.getPosition().getZ(); + final TrajectoryState state = new TrajectoryState(type, pv.getDate(), + type.toRawElements(pv, frame, + trajectoryWriter.getHistory().getBody(), + currentState.getMu())); + trajectoryWriter.writeState(generator, state, type.getUnits()); + } catch (IOException e) { + throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage()); + } + } + + /** {@inheritDoc}. */ + @Override + public void finish(final SpacecraftState finalState) { + try { + + trajectoryWriter.exitSection(generator); + + // update the trajectory IDs + trajectoryMetadata.setTrajPrevID(trajectoryMetadata.getTrajID()); + trajectoryMetadata.setTrajID(trajectoryMetadata.getTrajNextID()); + + if (trajectoryMetadata.getOrbRevNum() >= 0) { + // update the orbits revolution number + trajectoryMetadata.setOrbRevNum(trajectoryMetadata.getOrbRevNum() + crossings); + } + + } catch (IOException e) { + throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage()); + } + } + + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryState.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryState.java index d69b75d191..1118b68f0f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryState.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.util.List; -import org.orekit.files.ccsds.definitions.ElementsType; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; import org.orekit.utils.CartesianDerivativesFilter; @@ -33,7 +33,7 @@ public class TrajectoryState implements TimeStamped { /** Type of the elements. */ - private final ElementsType type; + private final OrbitElementsType type; /** Entry date. */ private final AbsoluteDate date; @@ -45,10 +45,10 @@ public class TrajectoryState implements TimeStamped { * @param type type of the elements * @param date entry date * @param fields trajectory elements - * @param first index of first field to consider + * @param first index of first field to consider when parsing * @param units units to use for parsing */ - public TrajectoryState(final ElementsType type, final AbsoluteDate date, + public TrajectoryState(final OrbitElementsType type, final AbsoluteDate date, final String[] fields, final int first, final List units) { this.type = type; this.date = date; @@ -58,6 +58,19 @@ public TrajectoryState(final ElementsType type, final AbsoluteDate date, } } + /** Simple constructor. + * @param type type of the elements + * @param date entry date + * @param elements trajectory elements in SI units + * @since 12.0 + */ + public TrajectoryState(final OrbitElementsType type, final AbsoluteDate date, + final double[] elements) { + this.type = type; + this.date = date; + this.elements = elements.clone(); + } + /** {@inheritDoc} */ @Override public AbsoluteDate getDate() { @@ -74,7 +87,7 @@ public double[] getElements() { /** Get the type of the elements. * @return type of the elements */ - public ElementsType getType() { + public OrbitElementsType getType() { return type; } @@ -82,19 +95,21 @@ public ElementsType getType() { * @return a value indicating if the file contains velocity and/or acceleration */ public CartesianDerivativesFilter getAvailableDerivatives() { - return type == ElementsType.CARTP ? + return type == OrbitElementsType.CARTP ? CartesianDerivativesFilter.USE_P : - (type == ElementsType.CARTPVA ? + (type == OrbitElementsType.CARTPVA ? CartesianDerivativesFilter.USE_PVA : CartesianDerivativesFilter.USE_PV); } /** Convert to Cartesian coordinates. + * @param body central body + * (may be null if {@link #getType() type} is not {@link OrbitElementsType#GEODETIC}) * @param mu gravitational parameter in m³/s² * @return Cartesian coordinates */ - public TimeStampedPVCoordinates toCartesian(final double mu) { - return type.toCartesian(date, elements, mu); + public TimeStampedPVCoordinates toCartesian(final OneAxisEllipsoid body, final double mu) { + return type.toCartesian(date, elements, body, mu); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistory.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistory.java index cf77afca6d..74d70b39e7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistory.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.general.EphemerisFile; @@ -44,17 +45,25 @@ public class TrajectoryStateHistory implements EphemerisFile.EphemerisSegmentnot {@link OrbitElementsType#GEODETIC}) * @param mu gravitational parameter in m³/s² */ - TrajectoryStateHistory(final TrajectoryStateHistoryMetadata metadata, - final List states, - final double mu) { + public TrajectoryStateHistory(final TrajectoryStateHistoryMetadata metadata, + final List states, + final OneAxisEllipsoid body, final double mu) { this.metadata = metadata; this.states = states; this.mu = mu; + this.body = body; } /** Get metadata. @@ -77,6 +86,14 @@ public double getMu() { return mu; } + /** Get central body. + * @return central body + * @since 12.0 + */ + public OneAxisEllipsoid getBody() { + return body; + } + /** {@inheritDoc} */ @Override public Frame getFrame() { @@ -115,7 +132,7 @@ public AbsoluteDate getStop() { /** {@inheritDoc} */ @Override public List getCoordinates() { - return states.stream().map(os -> os.toCartesian(mu)).collect(Collectors.toList()); + return states.stream().map(os -> os.toCartesian(body, mu)).collect(Collectors.toList()); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadata.java index 28e6f445a4..b848465047 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,6 @@ import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.BodyFacade; import org.orekit.files.ccsds.definitions.CelestialBodyFrame; -import org.orekit.files.ccsds.definitions.ElementsType; import org.orekit.files.ccsds.definitions.FrameFacade; import org.orekit.files.ccsds.ndm.odm.oem.InterpolationMethod; import org.orekit.files.ccsds.section.CommentsContainer; @@ -37,6 +36,16 @@ */ public class TrajectoryStateHistoryMetadata extends CommentsContainer { + /** Default interpolation method. + * @since 12.0 + */ + public static final InterpolationMethod DEFAULT_INTERPOLATION_METHOD = InterpolationMethod.HERMITE; + + /** Default interpolation degree. + * @since 12.0 + */ + public static final int DEFAULT_INTERPOLATION_DEGREE = 3; + /** Trajectory identification number. */ private String trajID; @@ -87,7 +96,7 @@ public class TrajectoryStateHistoryMetadata extends CommentsContainer { private int orbRevNumBasis; /** Trajectory element set type. */ - private ElementsType trajType; + private OrbitElementsType trajType; /** Type of averaging (Osculating, mean Brouwer, other...). */ private String orbAveraging; @@ -95,16 +104,21 @@ public class TrajectoryStateHistoryMetadata extends CommentsContainer { /** Units of trajectory element set. */ private List trajUnits; + /** Data context. + * @since 12.0 + */ + private final DataContext dataContext; + /** Simple constructor. * @param epochT0 T0 epoch from file metadata * @param dataContext data context */ - TrajectoryStateHistoryMetadata(final AbsoluteDate epochT0, final DataContext dataContext) { + public TrajectoryStateHistoryMetadata(final AbsoluteDate epochT0, final DataContext dataContext) { // we don't call the setXxx() methods in order to avoid // calling refuseFurtherComments as a side effect trajBasis = "PREDICTED"; - interpolationMethod = InterpolationMethod.HERMITE; - interpolationDegree = 3; + interpolationMethod = DEFAULT_INTERPOLATION_METHOD; + interpolationDegree = DEFAULT_INTERPOLATION_DEGREE; orbAveraging = "OSCULATING"; center = new BodyFacade("EARTH", dataContext.getCelestialBodies().getEarth()); @@ -112,27 +126,84 @@ public class TrajectoryStateHistoryMetadata extends CommentsContainer { CelestialBodyFrame.ICRF, null, null, CelestialBodyFrame.ICRF.name()); trajFrameEpoch = epochT0; - trajType = ElementsType.CARTPV; + trajType = OrbitElementsType.CARTPV; orbRevNum = -1; orbRevNumBasis = -1; + + this.dataContext = dataContext; + } /** {@inheritDoc} */ @Override public void validate(final double version) { + checkMandatoryEntriesExceptOrbitsCounter(version); + if (orbRevNum >= 0 && orbRevNumBasis < 0) { + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, + TrajectoryStateHistoryMetadataKey.ORB_REVNUM_BASIS.name()); + } + } + + /** Check is mandatory entries EXCEPT orbits counters have been initialized. + *

          + * This method should throw an exception if some mandatory entry is missing + *

          + * @param version format version + */ + private void checkMandatoryEntriesExceptOrbitsCounter(final double version) { super.validate(version); - if (trajType != ElementsType.CARTP && - trajType != ElementsType.CARTPV && - trajType != ElementsType.CARTPVA) { - checkNotNull(orbAveraging, TrajectoryStateHistoryMetadataKey.ORB_AVERAGING); + if (trajType != OrbitElementsType.CARTP && + trajType != OrbitElementsType.CARTPV && + trajType != OrbitElementsType.CARTPVA) { + checkNotNull(orbAveraging, TrajectoryStateHistoryMetadataKey.ORB_AVERAGING.name()); } if (trajUnits != null) { - trajType.checkUnits(trajUnits); + Unit.ensureCompatible(trajType.toString(), trajType.getUnits(), false, trajUnits); } - if (orbRevNum >= 0 && orbRevNumBasis < 0) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, - TrajectoryStateHistoryMetadataKey.ORB_REVNUM_BASIS.name()); + } + + /** Increments a trajectory ID. + *

          + * The trajectory blocks metadata contains three identifiers ({@code TRAJ_ID}, + * {@code TRAJ_PREV_ID}, {@code TRAJ_NEXT_ID}) that link the various blocks together. + * This helper method allows to update one identifier based on the value of another + * identifier. The update is performed by looking for an integer suffix at the end + * of the {@code original} identifier and incrementing it by one, taking care to use + * at least the same number of digits. If for example the original identifier is set + * to {@code trajectory 037}, then the updated identifier will be {@code trajectory 038}. + *

          + *

          + * This helper function is intended to be used by ephemeris generators like {@link EphemerisOcmWriter} + * and {@link StreamingOcmWriter}, allowing users to call only {@link #setTrajBasisID(String)} + * in the trajectory metadata template. The ephemeris generators call {@code + * template.setTrajNextID(TrajectoryStateHistoryMetadata.incrementTrajID(template.getTrajID()))} + * before generating each trajectory block and call both {@code template.setTrajPrevID(template.getTrajID()))} + * and {@code template.setTrajID(template.getTrajNextID()))} after having generated each block. + *

          + * @param original original ID (may be null) + * @return incremented ID, or null if original was null + */ + public static String incrementTrajID(final String original) { + + if (original == null) { + // no trajectory ID at all + return null; + } + + // split the ID into prefix and numerical index + int end = original.length(); + while (end > 0 && Character.isDigit(original.charAt(end - 1))) { + --end; } + final String prefix = original.substring(0, end); + final int index = end < original.length() ? Integer.parseInt(original.substring(end)) : 0; + + // build offset index, taking care to use at least the same number of digits + final String newIndex = String.format(String.format("%%0%dd", original.length() - end), + index + 1); + + return prefix + newIndex; + } /** Get trajectory identification number. @@ -370,36 +441,36 @@ public void setOrbRevNumBasis(final int orbRevNumBasis) { this.orbRevNumBasis = orbRevNumBasis; } + /** Get type of averaging (Osculating, mean Brouwer, other. + * @return type of averaging (Osculating, mean Brouwer, other) + */ + public String getOrbAveraging() { + return orbAveraging; + } + + /** Set type of averaging (Osculating, mean Brouwer, other. + * @param orbAveraging type of averaging (Osculating, mean Brouwer, other). + */ + public void setOrbAveraging(final String orbAveraging) { + refuseFurtherComments(); + this.orbAveraging = orbAveraging; + } + /** Get trajectory element set type. * @return trajectory element set type */ - public ElementsType getTrajType() { + public OrbitElementsType getTrajType() { return trajType; } /** Set trajectory element set type. * @param trajType trajectory element set type */ - public void setTrajType(final ElementsType trajType) { + public void setTrajType(final OrbitElementsType trajType) { refuseFurtherComments(); this.trajType = trajType; } - /** Get type of averaging (Osculating, mean Brouwer, other. - * @return type of averaging (Osculating, mean Brouwer, other - .). */ - public String getOrbAveraging() { - return orbAveraging; - } - - /** Set type of averaging (Osculating, mean Brouwer, other. - * @param orbAveraging type of averaging (Osculating, mean Brouwer, other - .). */ - public void setOrbAveraging(final String orbAveraging) { - refuseFurtherComments(); - this.orbAveraging = orbAveraging; - } - /** Get trajectory element set units. * @return trajectory element set units */ @@ -415,4 +486,45 @@ public void setTrajUnits(final List trajUnits) { this.trajUnits = trajUnits; } + /** Copy the instance, making sure mandatory fields have been initialized. + *

          + * Dates and orbit counter are not copied. + *

          + * @param version format version + * @return a new copy + * @since 12.0 + */ + public TrajectoryStateHistoryMetadata copy(final double version) { + + checkMandatoryEntriesExceptOrbitsCounter(version); + + // allocate new instance + final TrajectoryStateHistoryMetadata copy = new TrajectoryStateHistoryMetadata(trajFrameEpoch, dataContext); + + // copy comments + for (String comment : getComments()) { + copy.addComment(comment); + } + + // copy metadata + copy.setTrajPrevID(getTrajPrevID()); + copy.setTrajID(getTrajID()); + copy.setTrajNextID(getTrajNextID()); + copy.setTrajBasis(getTrajBasis()); + copy.setTrajBasisID(getTrajBasisID()); + copy.setInterpolationMethod(getInterpolationMethod()); + copy.setInterpolationDegree(getInterpolationDegree()); + copy.setPropagator(getPropagator()); + copy.setCenter(getCenter()); + copy.setTrajReferenceFrame(getTrajReferenceFrame()); + copy.setTrajFrameEpoch(getTrajFrameEpoch()); + copy.setOrbRevNumBasis(getOrbRevNumBasis()); + copy.setOrbAveraging(getOrbAveraging()); + copy.setTrajType(getTrajType()); + copy.setTrajUnits(getTrajUnits()); + + return copy; + + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadataKey.java index 5b804a1085..f8f4404418 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,6 @@ */ package org.orekit.files.ccsds.ndm.odm.ocm; -import org.orekit.files.ccsds.definitions.ElementsType; import org.orekit.files.ccsds.ndm.odm.oem.InterpolationMethod; import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.lexical.ParseToken; @@ -34,19 +33,19 @@ public enum TrajectoryStateHistoryMetadataKey { token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true), /** Trajectory identification number. */ - TRAJ_ID((token, context, container) -> token.processAsNormalizedString(container::setTrajID)), + TRAJ_ID((token, context, container) -> token.processAsFreeTextString(container::setTrajID)), /** Identification number of previous trajectory. */ - TRAJ_PREV_ID((token, context, container) -> token.processAsNormalizedString(container::setTrajPrevID)), + TRAJ_PREV_ID((token, context, container) -> token.processAsFreeTextString(container::setTrajPrevID)), /** Identification number of next trajectory. */ - TRAJ_NEXT_ID((token, context, container) -> token.processAsNormalizedString(container::setTrajNextID)), + TRAJ_NEXT_ID((token, context, container) -> token.processAsFreeTextString(container::setTrajNextID)), /** Basis of this trajectory state time history data. */ - TRAJ_BASIS((token, context, container) -> token.processAsNormalizedString(container::setTrajBasis)), + TRAJ_BASIS((token, context, container) -> token.processAsFreeTextString(container::setTrajBasis)), /** Identification number of the orbit determination or simulation upon which this trajectory is based.*/ - TRAJ_BASIS_ID((token, context, container) -> token.processAsNormalizedString(container::setTrajBasisID)), + TRAJ_BASIS_ID((token, context, container) -> token.processAsFreeTextString(container::setTrajBasisID)), /** Interpolation method to be used. */ INTERPOLATION((token, context, container) -> token.processAsEnum(InterpolationMethod.class, container::setInterpolationMethod)), @@ -57,7 +56,7 @@ public enum TrajectoryStateHistoryMetadataKey { /** Orbit propagator used to generate this trajectory. * @since 11.2 */ - PROPAGATOR((token, context, container) -> token.processAsNormalizedString(container::setPropagator)), + PROPAGATOR((token, context, container) -> token.processAsFreeTextString(container::setPropagator)), /** Origin of the reference frame of the trajectory. */ CENTER_NAME((token, context, container) -> token.processAsCenter(container::setCenter, @@ -82,12 +81,12 @@ public enum TrajectoryStateHistoryMetadataKey { ORB_REVNUM_BASIS((token, context, container) -> token.processAsInteger(container::setOrbRevNumBasis)), /** Type of averaging (Osculating, mean Brouwer, other...). */ - ORB_AVERAGING((token, context, container) -> token.processAsUppercaseString(container::setOrbAveraging)), + ORB_AVERAGING((token, context, container) -> token.processAsFreeTextString(container::setOrbAveraging)), /** Trajectory element set type. - * @see ElementsType + * @see OrbitElementsType */ - TRAJ_TYPE((token, context, container) -> token.processAsEnum(ElementsType.class, container::setTrajType)), + TRAJ_TYPE((token, context, container) -> token.processAsEnum(OrbitElementsType.class, container::setTrajType)), /** SI units for each elements of the trajectory state. */ TRAJ_UNITS((token, context, container) -> token.processAsUnitList(container::setTrajUnits)); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryWriter.java index 92e194f977..20f7a45123 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/TrajectoryStateHistoryWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.List; -import org.orekit.files.ccsds.definitions.ElementsType; import org.orekit.files.ccsds.definitions.TimeConverter; import org.orekit.files.ccsds.section.AbstractWriter; import org.orekit.files.ccsds.utils.FileFormat; @@ -51,11 +50,36 @@ class TrajectoryStateHistoryWriter extends AbstractWriter { this.timeConverter = timeConverter; } + /** Get state history. + * @return state history + * @since 12.0 + */ + TrajectoryStateHistory getHistory() { + return history; + } + /** {@inheritDoc} */ @Override protected void writeContent(final Generator generator) throws IOException { - // trajectory state history block + // metadata + writeMetadata(generator); + + // data + final List units = history.getMetadata().getTrajType().getUnits(); + for (final TrajectoryState state : history.getTrajectoryStates()) { + writeState(generator, state, units); + } + + } + + /** Write trajectory state history metadata. + * @param generator generator to use for producing output + * @throws IOException if any buffer writing operations fails + * @since 12.0 + */ + protected void writeMetadata(final Generator generator) throws IOException { + final TrajectoryStateHistoryMetadata metadata = history.getMetadata(); generator.writeComments(metadata.getComments()); @@ -67,8 +91,11 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(TrajectoryStateHistoryMetadataKey.TRAJ_BASIS_ID.name(), metadata.getTrajBasisID(), null, false); // interpolation - generator.writeEntry(TrajectoryStateHistoryMetadataKey.INTERPOLATION.name(), metadata.getInterpolationMethod(), false); - generator.writeEntry(TrajectoryStateHistoryMetadataKey.INTERPOLATION_DEGREE.name(), metadata.getInterpolationDegree(), false); + if (metadata.getInterpolationMethod() != TrajectoryStateHistoryMetadata.DEFAULT_INTERPOLATION_METHOD || + metadata.getInterpolationDegree() != TrajectoryStateHistoryMetadata.DEFAULT_INTERPOLATION_DEGREE) { + generator.writeEntry(TrajectoryStateHistoryMetadataKey.INTERPOLATION.name(), metadata.getInterpolationMethod(), false); + generator.writeEntry(TrajectoryStateHistoryMetadataKey.INTERPOLATION_DEGREE.name(), metadata.getInterpolationDegree(), false); + } // propagation generator.writeEntry(TrajectoryStateHistoryMetadataKey.PROPAGATOR.name(), metadata.getPropagator(), null, false); @@ -76,41 +103,52 @@ protected void writeContent(final Generator generator) throws IOException { // references generator.writeEntry(TrajectoryStateHistoryMetadataKey.CENTER_NAME.name(), metadata.getCenter().getName(), null, false); generator.writeEntry(TrajectoryStateHistoryMetadataKey.TRAJ_REF_FRAME.name(), metadata.getTrajReferenceFrame().getName(), null, false); - generator.writeEntry(TrajectoryStateHistoryMetadataKey.TRAJ_FRAME_EPOCH.name(), timeConverter, metadata.getTrajFrameEpoch(), false); + if (!metadata.getTrajFrameEpoch().equals(timeConverter.getReferenceDate())) { + generator.writeEntry(TrajectoryStateHistoryMetadataKey.TRAJ_FRAME_EPOCH.name(), timeConverter, metadata.getTrajFrameEpoch(), true, false); + } // time - generator.writeEntry(TrajectoryStateHistoryMetadataKey.USEABLE_START_TIME.name(), timeConverter, metadata.getUseableStartTime(), false); - generator.writeEntry(TrajectoryStateHistoryMetadataKey.USEABLE_STOP_TIME.name(), timeConverter, metadata.getUseableStopTime(), false); + generator.writeEntry(TrajectoryStateHistoryMetadataKey.USEABLE_START_TIME.name(), timeConverter, metadata.getUseableStartTime(), false, false); + generator.writeEntry(TrajectoryStateHistoryMetadataKey.USEABLE_STOP_TIME.name(), timeConverter, metadata.getUseableStopTime(), false, false); // revolution numbers - generator.writeEntry(TrajectoryStateHistoryMetadataKey.ORB_REVNUM.name(), metadata.getOrbRevNum(), false); - generator.writeEntry(TrajectoryStateHistoryMetadataKey.ORB_REVNUM_BASIS.name(), metadata.getOrbRevNumBasis(), false); + if (metadata.getOrbRevNum() > 0) { + generator.writeEntry(TrajectoryStateHistoryMetadataKey.ORB_REVNUM.name(), metadata.getOrbRevNum(), false); + generator.writeEntry(TrajectoryStateHistoryMetadataKey.ORB_REVNUM_BASIS.name(), metadata.getOrbRevNumBasis(), false); + } // elements generator.writeEntry(TrajectoryStateHistoryMetadataKey.TRAJ_TYPE.name(), metadata.getTrajType(), true); - if (metadata.getTrajType() != ElementsType.CARTP && - metadata.getTrajType() != ElementsType.CARTPV && - metadata.getTrajType() != ElementsType.CARTPVA) { + if (metadata.getTrajType() != OrbitElementsType.CARTP && + metadata.getTrajType() != OrbitElementsType.CARTPV && + metadata.getTrajType() != OrbitElementsType.CARTPVA) { generator.writeEntry(TrajectoryStateHistoryMetadataKey.ORB_AVERAGING.name(), metadata.getOrbAveraging(), null, true); } generator.writeEntry(TrajectoryStateHistoryMetadataKey.TRAJ_UNITS.name(), generator.unitsListToString(metadata.getTrajUnits()), null, false); + } - // data - final List units = metadata.getTrajType().getUnits(); - for (final TrajectoryState state : history.getTrajectoryStates()) { - final double[] elements = state.getElements(); - final StringBuilder line = new StringBuilder(); - line.append(generator.dateToString(timeConverter, state.getDate())); - for (int i = 0; i < units.size(); ++i) { - line.append(' '); - line.append(AccurateFormatter.format(units.get(i).fromSI(elements[i]))); - } - if (generator.getFormat() == FileFormat.XML) { - generator.writeEntry(Ocm.TRAJ_LINE, line.toString(), null, true); - } else { - generator.writeRawData(line); - generator.newLine(); - } + /** Write one trajectory state. + * @param generator generator to use for producing output + * @param state state to write + * @param units elements units + * @throws IOException if any buffer writing operations fails + * @since 12.0 + */ + protected void writeState(final Generator generator, final TrajectoryState state, final List units) + throws IOException { + + final double[] elements = state.getElements(); + final StringBuilder line = new StringBuilder(); + line.append(generator.dateToString(timeConverter, state.getDate())); + for (int i = 0; i < units.size(); ++i) { + line.append(' '); + line.append(AccurateFormatter.format(units.get(i).fromSI(elements[i]))); + } + if (generator.getFormat() == FileFormat.XML) { + generator.writeEntry(Ocm.TRAJ_LINE, line.toString(), null, true); + } else { + generator.writeRawData(line); + generator.newLine(); } } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/package-info.java index 67b36176da..deb928c4ea 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/ocm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/EphemerisWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/EphemerisOemWriter.java similarity index 82% rename from src/main/java/org/orekit/files/ccsds/ndm/odm/oem/EphemerisWriter.java rename to src/main/java/org/orekit/files/ccsds/ndm/odm/oem/EphemerisOemWriter.java index c7d1a2b700..754d9dbb6b 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/EphemerisWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/EphemerisOemWriter.java @@ -23,7 +23,7 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.FrameFacade; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.utils.FileFormat; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.files.ccsds.utils.generation.KvnGenerator; @@ -44,13 +44,13 @@ * Data Definitions and Conventions * @see StreamingOemWriter */ -public class EphemerisWriter implements EphemerisFileWriter { +public class EphemerisOemWriter implements EphemerisFileWriter { /** Underlying writer. */ private final OemWriter writer; /** Header. */ - private final Header header; + private final OdmHeader header; /** Current metadata. */ private final OemMetadata metadata; @@ -61,6 +61,11 @@ public class EphemerisWriter implements EphemerisFileWriter { /** Output name for error messages. */ private final String outputName; + /** Maximum offset for relative dates. + * @since 12.0 + */ + private final double maxRelativeOffset; + /** Column number for aligning units. */ private final int unitsColumn; @@ -83,19 +88,22 @@ public class EphemerisWriter implements EphemerisFileWriter { * @param template template for metadata * @param fileFormat file format to use * @param outputName output name for error messages + * @param maxRelativeOffset maximum offset in seconds to use relative dates + * (if a date is too far from reference, it will be displayed as calendar elements) * @param unitsColumn columns number for aligning units (if negative or zero, units are not output) - * @since 11.0 + * @since 12.0 */ - public EphemerisWriter(final OemWriter writer, - final Header header, final OemMetadata template, - final FileFormat fileFormat, final String outputName, - final int unitsColumn) { - this.writer = writer; - this.header = header; - this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); - this.fileFormat = fileFormat; - this.outputName = outputName; - this.unitsColumn = unitsColumn; + public EphemerisOemWriter(final OemWriter writer, + final OdmHeader header, final OemMetadata template, + final FileFormat fileFormat, final String outputName, + final double maxRelativeOffset, final int unitsColumn) { + this.writer = writer; + this.header = header; + this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion()); + this.fileFormat = fileFormat; + this.outputName = outputName; + this.maxRelativeOffset = maxRelativeOffset; + this.unitsColumn = unitsColumn; } /** {@inheritDoc} @@ -105,7 +113,7 @@ public EphemerisWriter(final OemWriter writer, * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation * method and interpolation degree. The missing values (like object name, local spacecraft * body frame...) will be inherited from the template metadata set at writer - * {@link #EphemerisWriter(OemWriter, Header, OemMetadata, FileFormat, String, int) construction}. + * {@link #EphemerisOemWriter(OemWriter, OdmHeader, OemMetadata, FileFormat, String, double, int) construction}. *

          */ @Override @@ -127,7 +135,7 @@ void write(final Appendable appendable, final EphemerisFile ephemerisFile) metadata.getObjectID(), "ephemerisFile"); } - // Get attitude ephemeris segments to output. + // Get ephemeris segments to output. final List segments = satEphem.getSegments(); if (segments.isEmpty()) { // No data -> No output @@ -135,8 +143,10 @@ void write(final Appendable appendable, final EphemerisFile ephemerisFile) } try (Generator generator = fileFormat == FileFormat.KVN ? - new KvnGenerator(appendable, OemWriter.KVN_PADDING_WIDTH, outputName, unitsColumn) : - new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, unitsColumn > 0)) { + new KvnGenerator(appendable, OemWriter.KVN_PADDING_WIDTH, outputName, + maxRelativeOffset, unitsColumn) : + new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, + maxRelativeOffset, unitsColumn > 0, null)) { writer.writeHeader(generator, header); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/InterpolationMethod.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/InterpolationMethod.java index b0d3721cdf..dfdb4f3dd5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/InterpolationMethod.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/InterpolationMethod.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/Oem.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/Oem.java index 1d9681b754..a2b371a8a5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/Oem.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/Oem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,7 +27,7 @@ import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.NdmConstituent; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.general.EphemerisFile; import org.orekit.utils.IERSConventions; import org.orekit.utils.TimeStampedPVCoordinates; @@ -42,7 +42,7 @@ * @author Evan Ward * @since 6.1 */ -public class Oem extends NdmConstituent +public class Oem extends NdmConstituent implements EphemerisFile { /** Root element for XML files. */ @@ -61,7 +61,7 @@ public class Oem extends NdmConstituent * @param dataContext used for creating frames, time scales, etc. * @param mu gravitational coefficient */ - public Oem(final Header header, final List segments, + public Oem(final OdmHeader header, final List segments, final IERSConventions conventions, final DataContext dataContext, final double mu) { super(header, segments, conventions, dataContext); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemData.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemData.java index 74728a2daf..a4b7ff86cc 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemDataSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemDataSubStructureKey.java index b3e38da457..7dbc138a1a 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemDataSubStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemDataSubStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadata.java index 2180c202a8..bbab05ae79 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,14 +17,14 @@ package org.orekit.files.ccsds.ndm.odm.oem; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.time.AbsoluteDate; /** Metadata for Orbit Ephemeris Messages. * @author Luc Maisonobe * @since 11.0 */ -public class OemMetadata extends CommonMetadata { +public class OemMetadata extends OdmCommonMetadata { /** Start of total time span covered by ephemerides data and covariance data. */ private AbsoluteDate startTime; @@ -57,8 +57,8 @@ public OemMetadata(final int defaultInterpolationDegree) { @Override public void validate(final double version) { checkMandatoryEntriesExceptDates(version); - checkNotNull(startTime, OemMetadataKey.START_TIME); - checkNotNull(stopTime, OemMetadataKey.STOP_TIME); + checkNotNull(startTime, OemMetadataKey.START_TIME.name()); + checkNotNull(stopTime, OemMetadataKey.STOP_TIME.name()); } /** Check is mandatory entries EXCEPT DATES have been initialized. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadataKey.java index 1d0666ea3e..d08ddf7864 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemParser.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemParser.java index d076839b85..e98311fcbc 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import java.util.regex.Pattern; import org.orekit.data.DataContext; @@ -28,13 +29,13 @@ import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.ndm.odm.CartesianCovariance; import org.orekit.files.ccsds.ndm.odm.CartesianCovarianceKey; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.files.ccsds.ndm.odm.CommonMetadataKey; -import org.orekit.files.ccsds.ndm.odm.OdmParser; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey; +import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.odm.StateVector; import org.orekit.files.ccsds.ndm.odm.StateVectorKey; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.KvnStructureProcessingState; import org.orekit.files.ccsds.section.MetadataKey; @@ -72,7 +73,7 @@ public class OemParser extends OdmParser implements EphemerisFil private static final Pattern SPLIT_AT_BLANKS = Pattern.compile("\\s+"); /** File header. */ - private Header header; + private OdmHeader header; /** File segments. */ private List segments; @@ -119,13 +120,16 @@ public class OemParser extends OdmParser implements EphemerisFil * @param mu gravitational coefficient * @param defaultInterpolationDegree default interpolation degree * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ public OemParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, final AbsoluteDate missionReferenceDate, final double mu, - final int defaultInterpolationDegree, final ParsedUnitsBehavior parsedUnitsBehavior) { + final int defaultInterpolationDegree, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { super(Oem.ROOT, Oem.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, - missionReferenceDate, mu, parsedUnitsBehavior); + missionReferenceDate, mu, parsedUnitsBehavior, filters); this.defaultInterpolationDegree = defaultInterpolationDegree; } @@ -137,14 +141,14 @@ public Oem parse(final DataSource source) { /** {@inheritDoc} */ @Override - public Header getHeader() { + public OdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(3.0); + header = new OdmHeader(); segments = new ArrayList<>(); metadata = null; context = null; @@ -285,7 +289,7 @@ boolean manageXmlStateVectorSection(final boolean starting) { boolean manageCovarianceSection(final boolean starting) { if (starting) { // save the current metadata for later retrieval of reference frame - final CommonMetadata savedMetadata = metadata; + final OdmCommonMetadata savedMetadata = metadata; currentCovariance = new CartesianCovariance(() -> savedMetadata.getReferenceFrame()); anticipateNext(getFileFormat() == FileFormat.XML ? this::processXmlCovarianceToken : @@ -428,7 +432,7 @@ private boolean processKvnCovarianceToken(final ParseToken token) { if (currentCovariance == null) { // save the current metadata for later retrieval of reference frame - final CommonMetadata savedMetadata = metadata; + final OdmCommonMetadata savedMetadata = metadata; currentCovariance = new CartesianCovariance(() -> savedMetadata.getReferenceFrame()); currentRow = 0; } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSatelliteEphemeris.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSatelliteEphemeris.java index b44f30e95a..f911f9d278 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSatelliteEphemeris.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSatelliteEphemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSegment.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSegment.java index c0c5d3e28c..8970e3f078 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSegment.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemSegment.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemWriter.java index 0b9e396c4d..32ca04a189 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/OemWriter.java @@ -30,9 +30,9 @@ import org.orekit.files.ccsds.ndm.odm.CartesianCovariance; import org.orekit.files.ccsds.ndm.odm.CartesianCovarianceKey; import org.orekit.files.ccsds.ndm.odm.CommonMetadataKey; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey; import org.orekit.files.ccsds.ndm.odm.StateVectorKey; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderKey; import org.orekit.files.ccsds.section.KvnStructureKey; import org.orekit.files.ccsds.section.MetadataKey; @@ -189,7 +189,7 @@ * Data Definitions and Conventions * @see StreamingOemWriter */ -public class OemWriter extends AbstractMessageWriter { +public class OemWriter extends AbstractMessageWriter { /** Version number implemented. **/ public static final double CCSDS_OEM_VERS = 3.0; @@ -236,8 +236,8 @@ public OemWriter(final IERSConventions conventions, final DataContext dataContex /** {@inheritDoc} */ @Override - public void writeSegmentContent(final Generator generator, final double formatVersion, - final OemSegment segment) + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final OemSegment segment) throws IOException { final OemMetadata metadata = segment.getMetadata(); @@ -303,19 +303,19 @@ void writeMetadata(final Generator generator, final OemMetadata metadata) if (metadata.getFrameEpoch() != null) { generator.writeEntry(CommonMetadataKey.REF_FRAME_EPOCH.name(), getTimeConverter(), metadata.getFrameEpoch(), - false); + true, false); } // time generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), metadata.getTimeSystem(), true); - generator.writeEntry(OemMetadataKey.START_TIME.name(), getTimeConverter(), metadata.getStartTime(), true); + generator.writeEntry(OemMetadataKey.START_TIME.name(), getTimeConverter(), metadata.getStartTime(), false, true); if (metadata.getUseableStartTime() != null) { - generator.writeEntry(OemMetadataKey.USEABLE_START_TIME.name(), getTimeConverter(), metadata.getUseableStartTime(), false); + generator.writeEntry(OemMetadataKey.USEABLE_START_TIME.name(), getTimeConverter(), metadata.getUseableStartTime(), false, false); } if (metadata.getUseableStopTime() != null) { - generator.writeEntry(OemMetadataKey.USEABLE_STOP_TIME.name(), getTimeConverter(), metadata.getUseableStopTime(), false); + generator.writeEntry(OemMetadataKey.USEABLE_STOP_TIME.name(), getTimeConverter(), metadata.getUseableStopTime(), false, false); } - generator.writeEntry(OemMetadataKey.STOP_TIME.name(), getTimeConverter(), metadata.getStopTime(), true); + generator.writeEntry(OemMetadataKey.STOP_TIME.name(), getTimeConverter(), metadata.getStopTime(), false, true); // interpolation generator.writeEntry(OemMetadataKey.INTERPOLATION.name(), metadata.getInterpolationMethod(), false); @@ -384,7 +384,7 @@ void writeOrbitEphemerisLine(final Generator generator, final OemMetadata metada generator.enterSection(OemDataSubStructureKey.stateVector.name()); // Epoch - generator.writeEntry(StateVectorKey.EPOCH.name(), getTimeConverter(), coordinates.getDate(), true); + generator.writeEntry(StateVectorKey.EPOCH.name(), getTimeConverter(), coordinates.getDate(), false, true); // Position data in km generator.writeEntry(StateVectorKey.X.name(), coordinates.getPosition().getX(), Unit.KILOMETRE, true); @@ -454,7 +454,7 @@ private void writeCovariance(final Generator generator, final OemMetadata metada } // epoch - generator.writeEntry(CartesianCovarianceKey.EPOCH.name(), getTimeConverter(), covariance.getEpoch(), true); + generator.writeEntry(CartesianCovarianceKey.EPOCH.name(), getTimeConverter(), covariance.getEpoch(), false, true); // reference frame if (covariance.getReferenceFrame() != metadata.getReferenceFrame()) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/StreamingOemWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/StreamingOemWriter.java index 65d8c847b6..81bd053df0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/StreamingOemWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/StreamingOemWriter.java @@ -22,7 +22,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.definitions.FrameFacade; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.utils.generation.Generator; import org.orekit.frames.Frame; import org.orekit.propagation.Propagator; @@ -37,13 +37,15 @@ *

          Each instance corresponds to a single OEM file. A new OEM ephemeris segment is * started by calling {@link #newSegment()}. * - *

          This class can be used as a step handler for a {@link Propagator}. + *

          + * The segments returned by this class can be used as step handlers for a {@link Propagator}. + *

          * *
          {@code
            * Propagator propagator = ...; // pre-configured propagator
            * OEMWriter  aemWriter  = ...; // pre-configured writer
            *   try (Generator out = ...;  // set-up output stream
          - *        StreamingOemWriter sw = new StreamingOemWriter(out, oemWriter)) { // set-up streaming writer
          + *        StreamingOemWriter sw = new StreamingOemWriter(out, oemWriter, header, metadata)) { // set-up streaming writer
            *
            *     // write segment 1
            *     propagator.getMultiplexer().add(step, sw.newSegment());
          @@ -76,7 +78,7 @@ public class StreamingOemWriter implements AutoCloseable {
               private final OemWriter writer;
           
               /** Header. */
          -    private final Header header;
          +    private final OdmHeader header;
           
               /** Current metadata. */
               private final OemMetadata metadata;
          @@ -99,10 +101,10 @@ public class StreamingOemWriter implements AutoCloseable {
                * @param header    file header (may be null)
                * @param template  template for metadata
                * @since 11.0
          -     * @see #StreamingOemWriter(Generator, OemWriter, Header, OemMetadata, boolean)
          +     * @see #StreamingOemWriter(Generator, OemWriter, OdmHeader, OemMetadata, boolean)
                */
               public StreamingOemWriter(final Generator generator, final OemWriter writer,
          -                              final Header header, final OemMetadata template) {
          +                              final OdmHeader header, final OemMetadata template) {
                   this(generator, writer, header, template, true);
               }
           
          @@ -118,12 +120,11 @@ public StreamingOemWriter(final Generator generator, final OemWriter writer,
                *                         segment is taken from the first state's attitude.
                *                         Otherwise the {@code template}'s reference frame
                *                         is used, {@link OemMetadata#getReferenceFrame()}.
          -     * @see #StreamingOemWriter(Generator, OemWriter, Header, OemMetadata,
          -     * boolean, boolean)
          +     * @see #StreamingOemWriter(Generator, OemWriter, OdmHeader, OemMetadata, boolean, boolean)
                * @since 11.1.2
                */
               public StreamingOemWriter(final Generator generator, final OemWriter writer,
          -                              final Header header, final OemMetadata template,
          +                              final OdmHeader header, final OemMetadata template,
                                         final boolean useAttitudeFrame) {
                   this(generator, writer, header, template, useAttitudeFrame, true);
               }
          @@ -146,7 +147,7 @@ public StreamingOemWriter(final Generator generator, final OemWriter writer,
                * @since 11.1.2
                */
               public StreamingOemWriter(final Generator generator, final OemWriter writer,
          -                              final Header header, final OemMetadata template,
          +                              final OdmHeader header, final OemMetadata template,
                                         final boolean useAttitudeFrame,
                                         final boolean includeAcceleration) {
                   this.generator          = generator;
          @@ -180,13 +181,23 @@ public class SegmentWriter implements OrekitFixedStepHandler {
                   /** Reference frame of this segment. */
                   private Frame frame;
           
          +        /** Empty constructor.
          +         * 

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public SegmentWriter() { + // nothing to do + } + /** * {@inheritDoc} * - *

          Sets the {@link OemMetadataKey#START_TIME} and {@link OemMetadataKey#STOP_TIME} in this - * segment's metadata if not already set by the user. Then calls {@link OemWriter#writeHeader(Generator, Header) - * writeHeader} if it is the first segment) and {@link OemWriter#writeMetadata(Generator, OemMetadata)} - * to start the segment. + *

          Writes the header automatically on first segment. + * Sets the {@link OemMetadataKey#START_TIME} and {@link OemMetadataKey#STOP_TIME} in this + * segment's metadata if not already set by the user. */ @Override public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/package-info.java index 4f12dbac47..dacbbebde8 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/oem/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/MeanKeplerianElementsWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/MeanKeplerianElementsWriter.java index 87b7a4deea..695bee0424 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/MeanKeplerianElementsWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/MeanKeplerianElementsWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -65,7 +65,7 @@ protected void writeContent(final Generator generator) throws IOException { // Keplerian elements block generator.writeComments(keplerianElements.getComments()); - generator.writeEntry(KeplerianElementsKey.EPOCH.name(), timeConverter, keplerianElements.getEpoch(), true); + generator.writeEntry(KeplerianElementsKey.EPOCH.name(), timeConverter, keplerianElements.getEpoch(), true, true); if (theoryIsSgpSdp) { generator.writeEntry(KeplerianElementsKey.MEAN_MOTION.name(), keplerianElements.getMeanMotion(), Units.REV_PER_DAY, true); } else { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/Omm.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/Omm.java index 08fe000b31..7b6c8d1c3a 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/Omm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/Omm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,9 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.NdmConstituent; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.files.ccsds.ndm.odm.KeplerianElements; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.section.Segment; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.KeplerianOrbit; @@ -38,7 +38,7 @@ * @author sports * @since 6.1 */ -public class Omm extends NdmConstituent> implements TimeStamped { +public class Omm extends NdmConstituent> implements TimeStamped { /** Root element for XML files. */ public static final String ROOT = "omm"; @@ -52,7 +52,7 @@ public class Omm extends NdmConstituent> i * @param conventions IERS conventions * @param dataContext used for creating frames, time scales, etc. */ - public Omm(final Header header, final List> segments, + public Omm(final OdmHeader header, final List> segments, final IERSConventions conventions, final DataContext dataContext) { super(header, segments, conventions, dataContext); } @@ -97,13 +97,13 @@ public SpacecraftState generateSpacecraftState() { * @return the tle */ public TLE generateTLE() { - final CommonMetadata metadata = getMetadata(); + final OdmCommonMetadata metadata = getMetadata(); final KeplerianElements kep = getData().getKeplerianElementsBlock(); final OmmTle tle = getData().getTLEBlock(); return new TLE(tle.getNoradID(), tle.getClassificationType(), metadata.getLaunchYear(), metadata.getLaunchNumber(), metadata.getLaunchPiece(), tle.getEphemerisType(), tle.getElementSetNumber(), kep.getEpoch(), - kep.getMeanMotion(), tle.getMeanMotionDot(), tle.getMeanMotionDotDot(), + kep.getMeanMotion(), tle.getMeanMotionDot() / 2, tle.getMeanMotionDotDot() / 6, kep.getE(), kep.getI(), kep.getPa(), kep.getRaan(), kep.getAnomaly(), tle.getRevAtEpoch(), tle.getBStar(), getDataContext().getTimeScales().getUTC()); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmData.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmData.java index cdd0a17b8d..fbb19e8510 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -87,11 +87,11 @@ public void validate(final double version) { if (tleBlock == null) { // semi-major axis was not checked above, we do it now keplerianElementsBlock.checkNotNaN(keplerianElementsBlock.getA(), - KeplerianElementsKey.SEMI_MAJOR_AXIS); + KeplerianElementsKey.SEMI_MAJOR_AXIS.name()); } else { // in OMM with TLE block, only mean motion is allowed, not semi-major axis keplerianElementsBlock.checkNotNaN(keplerianElementsBlock.getMeanMotion(), - KeplerianElementsKey.MEAN_MOTION); + KeplerianElementsKey.MEAN_MOTION.name()); tleBlock.validate(version); } if (covarianceBlock != null) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadata.java index 61491ffb79..dc91206f3c 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,13 +19,22 @@ import java.util.regex.Pattern; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; -public class OmmMetadata extends CommonMetadata { +/** Metadata for Orbit Mean Messages. + * @author Luc Maisonobe + * @since 12.0 + */ +public class OmmMetadata extends OdmCommonMetadata { /** Constant for SGP/SGP4 mean elements theory. */ public static final String SGP_SGP4_THEORY = "SGP/SGP4"; + /** Constant for SGP4-XP mean elements theory. + * @since 12.0 + */ + public static final String SGP4_XP_THEORY = "SGP4-XP"; + /** Constant for DSST mean elements theory. */ public static final String DSST_THEORY = "DSST"; @@ -36,6 +45,17 @@ public class OmmMetadata extends CommonMetadata { * to propagate the state. */ private String meanElementTheory; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public OmmMetadata() { + // nothing to do + } + /** Check if mean element theory in SGP or SDP. * @return true if mean element theory in SGP or SDP */ diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataKey.java index 9afa3e1f7d..db4e7e9a98 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataWriter.java index deab322e69..a53e904156 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmMetadataWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmParser.java index ae41135266..89068fd4ca 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,23 +19,24 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Function; import org.hipparchus.util.FastMath; import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.ndm.odm.CartesianCovariance; import org.orekit.files.ccsds.ndm.odm.CartesianCovarianceKey; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.files.ccsds.ndm.odm.CommonMetadataKey; -import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.odm.KeplerianElements; import org.orekit.files.ccsds.ndm.odm.KeplerianElementsKey; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey; +import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.odm.SpacecraftParameters; import org.orekit.files.ccsds.ndm.odm.SpacecraftParametersKey; import org.orekit.files.ccsds.ndm.odm.UserDefined; import org.orekit.files.ccsds.section.CommentsContainer; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.MetadataKey; import org.orekit.files.ccsds.section.Segment; @@ -71,7 +72,7 @@ public class OmmParser extends OdmParser { private final double defaultMass; /** File header. */ - private Header header; + private OdmHeader header; /** File segments. */ private List> segments; @@ -113,12 +114,15 @@ public class OmmParser extends OdmParser { * @param mu gravitational coefficient * @param defaultMass default mass to use if there are no spacecraft parameters block logical block in the file * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ public OmmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, final AbsoluteDate missionReferenceDate, - final double mu, final double defaultMass, final ParsedUnitsBehavior parsedUnitsBehavior) { + final double mu, final double defaultMass, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { super(Omm.ROOT, Omm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, - missionReferenceDate, mu, parsedUnitsBehavior); + missionReferenceDate, mu, parsedUnitsBehavior, filters); this.defaultMass = defaultMass; } @@ -137,14 +141,14 @@ public Map getSpecialXmlElementsBuilders() { /** {@inheritDoc} */ @Override - public Header getHeader() { + public OdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(3.0); + header = new OdmHeader(); segments = new ArrayList<>(); metadata = null; context = null; @@ -442,7 +446,7 @@ private boolean processTLEToken(final ParseToken token) { private boolean processCovarianceToken(final ParseToken token) { if (covarianceBlock == null) { // save the current metadata for later retrieval of reference frame - final CommonMetadata savedMetadata = metadata; + final OdmCommonMetadata savedMetadata = metadata; covarianceBlock = new CartesianCovariance(() -> savedMetadata.getReferenceFrame()); if (moveCommentsIfEmpty(tleBlock, covarianceBlock)) { // get rid of the empty logical block diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTle.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTle.java index 5fe8609d55..6d8031d2d0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTle.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTle.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,8 +25,33 @@ */ public class OmmTle extends CommentsContainer { + /** Constant for EPHEMERIS_TYPE SGP. + * @since 12.0 + */ + public static final int EPHEMERIS_TYPE_SGP = 0; + + /** Constant for EPHEMERIS_TYPE SGP4. + * @since 12.0 + */ + public static final int EPHEMERIS_TYPE_SGP4 = 2; + + /** Constant for EPHEMERIS_TYPE PPT3. + * @since 12.0 + */ + public static final int EPHEMERIS_TYPE_PPT3 = 3; + + /** Constant for EPHEMERIS_TYPE SGP4-XP. + * @since 12.0 + */ + public static final int EPHEMERIS_TYPE_SGP4_XP = 4; + + /** Constant for EPHEMERIS_TYPE Special Perturbations. + * @since 12.0 + */ + public static final int EPHEMERIS_TYPE_SPECIAL_PERTURBATIONS = 6; + /** Ephemeris Type, only required if MEAN_ELEMENT_THEORY = SGP/SGP4. Some sources suggest the coding for - * the EPHEMERIS_TYPE keyword: 1 = SGP, 2 = SGP4, 3 = SDP4, 4 = SGP8, 5 = SDP8. Default value = 0. + * the EPHEMERIS_TYPE keyword: 0 = SGP, 2 = SGP4, 3 = PPT3, 4 = SGP4-XP, 6 = Special Perturbations. Default value = 0. */ private int ephemerisType; @@ -49,34 +74,62 @@ public class OmmTle extends CommentsContainer { /** SGP/SGP4 drag-like coefficient (in units 1/[Earth radii]), only required if MEAN_ELEMENT_THEORY = SGP/SGP4. */ private double bStar; + /** SGP4-XP drag-like coefficient (in m²/kg), only required if MEAN_ELEMENT_THEORY = SGP4-XP. + * @since 12.0 + */ + private double bTerm; + /** First Time Derivative of the Mean Motion, only required if MEAN_ELEMENT_THEORY = SGP. */ private double meanMotionDot; /** Second Time Derivative of Mean Motion, only required if MEAN_ELEMENT_THEORY = SGP. */ private double meanMotionDotDot; + /** SGP4-XP solar radiation pressure-like coefficient Aγ/m (in m²/kg), only required if MEAN_ELEMENT_THEORY = SGP4-XP. + * @since 12.0 + */ + private double agOm; + /** Create an empty data set. */ public OmmTle() { - ephemerisType = 0; + ephemerisType = EPHEMERIS_TYPE_SGP; classificationType = 'U'; noradID = -1; elementSetNo = -1; revAtEpoch = -1; bStar = Double.NaN; + bTerm = Double.NaN; meanMotionDot = Double.NaN; meanMotionDotDot = Double.NaN; + agOm = Double.NaN; } /** {@inheritDoc} */ @Override public void validate(final double version) { super.validate(version); - checkNotNaN(meanMotionDot, OmmTleKey.MEAN_MOTION_DOT); - checkNotNaN(meanMotionDotDot, OmmTleKey.MEAN_MOTION_DDOT); - checkNotNegative(noradID, OmmTleKey.NORAD_CAT_ID); - checkNotNegative(elementSetNo, OmmTleKey.ELEMENT_SET_NO); - checkNotNegative(revAtEpoch, OmmTleKey.REV_AT_EPOCH); + + checkNotNegative(noradID, OmmTleKey.NORAD_CAT_ID.name()); + checkNotNegative(elementSetNo, OmmTleKey.ELEMENT_SET_NO.name()); + checkNotNegative(revAtEpoch, OmmTleKey.REV_AT_EPOCH.name()); + + if (ephemerisType == EPHEMERIS_TYPE_SGP4) { + checkNotNaN(bStar, OmmTleKey.BSTAR.name()); + } else if (ephemerisType == EPHEMERIS_TYPE_SGP4_XP) { + checkNotNaN(bTerm, OmmTleKey.BTERM.name()); + } + + if (ephemerisType == EPHEMERIS_TYPE_SGP || ephemerisType == EPHEMERIS_TYPE_PPT3) { + checkNotNaN(meanMotionDot, OmmTleKey.MEAN_MOTION_DOT.name()); + } + + if (ephemerisType == EPHEMERIS_TYPE_SGP || ephemerisType == EPHEMERIS_TYPE_PPT3) { + checkNotNaN(meanMotionDotDot, OmmTleKey.MEAN_MOTION_DDOT.name()); + } else if (ephemerisType == EPHEMERIS_TYPE_SGP4_XP) { + checkNotNaN(agOm, OmmTleKey.AGOM.name()); + } + } /** Get the ephemeris type. @@ -169,6 +222,23 @@ public void setBStar(final double bstar) { this.bStar = bstar; } + /** Get the SGP4-XP drag-like coefficient. + * @return the SGP4-XP drag-like coefficient + * @since 12.0 + */ + public double getBTerm() { + return bTerm; + } + + /** Set the SGP4-XP drag-like coefficient. + * @param bterm the SGP4-XP drag-like coefficient to be set + * @since 12.0 + */ + public void setBTerm(final double bterm) { + refuseFurtherComments(); + this.bTerm = bterm; + } + /** Get the first time derivative of the mean motion. * @return the first time derivative of the mean motion */ @@ -199,4 +269,21 @@ public void setMeanMotionDotDot(final double meanMotionDotDot) { this.meanMotionDotDot = meanMotionDotDot; } + /** Get the SGP4-XP solar radiation pressure-like coefficient Aγ/m. + * @return the SGP4-XP solar radiation pressure-like coefficient Aγ/m + * @since 12.0 + */ + public double getAGoM() { + return agOm; + } + + /** Set the SGP4-XP solar radiation pressure-like coefficient Aγ/m. + * @param agom the SGP4-XP solar radiation pressure-like coefficient Aγ/m to be set + * @since 12.0 + */ + public void setAGoM(final double agom) { + refuseFurtherComments(); + this.agOm = agom; + } + } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleKey.java index 384bad86ca..9e4b705d4e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -51,13 +51,25 @@ public enum OmmTleKey { BSTAR((token, context, container) -> token.processAsDouble(Units.ONE_PER_ER, context.getParsedUnitsBehavior(), container::setBStar)), + /** SGP4-XP drag-like coefficient. + * @since 12.0 + */ + BTERM((token, context, container) -> token.processAsDouble(Units.M2_PER_KG, context.getParsedUnitsBehavior(), + container::setBTerm)), + /** First time derivative of mean motion. */ MEAN_MOTION_DOT((token, context, container) -> token.processAsDouble(Units.REV_PER_DAY2_SCALED, context.getParsedUnitsBehavior(), container::setMeanMotionDot)), /** Second time derivative of mean motion. */ MEAN_MOTION_DDOT((token, context, container) -> token.processAsDouble(Units.REV_PER_DAY3_SCALED, context.getParsedUnitsBehavior(), - container::setMeanMotionDotDot)); + container::setMeanMotionDotDot)), + + /** SGP4-XP solar radiation pressure-like coefficient. + * @since 12.0 + */ + AGOM((token, context, container) -> token.processAsDouble(Units.M2_PER_KG, context.getParsedUnitsBehavior(), + container::setAGoM)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleWriter.java index 1d62705fed..f7223cdbe5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmTleWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmWriter.java index 07d401d71f..0d76e50262 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/OmmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,9 +22,9 @@ import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.ndm.odm.CartesianCovarianceWriter; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.SpacecraftParametersWriter; import org.orekit.files.ccsds.ndm.odm.UserDefinedWriter; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.Segment; import org.orekit.files.ccsds.section.XmlStructureKey; import org.orekit.files.ccsds.utils.ContextBinding; @@ -41,7 +41,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class OmmWriter extends AbstractMessageWriter, Omm> { +public class OmmWriter extends AbstractMessageWriter, Omm> { /** Version number implemented. **/ public static final double CCSDS_OMM_VERS = 3.0; @@ -71,8 +71,8 @@ public OmmWriter(final IERSConventions conventions, final DataContext dataContex /** {@inheritDoc} */ @Override - public void writeSegmentContent(final Generator generator, final double formatVersion, - final Segment segment) + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final Segment segment) throws IOException { // write the metadata diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/XmlSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/XmlSubStructureKey.java index f946209f40..75dc354f7d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/XmlSubStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/XmlSubStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/package-info.java index 5023d6c645..9ffe326851 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/omm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Maneuver.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Maneuver.java index e8b0714d1d..9f3a8cecff 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Maneuver.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Maneuver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -58,13 +58,13 @@ public Maneuver() { @Override public void validate(final double version) { super.validate(version); - checkNotNull(epochIgnition, ManeuverKey.MAN_EPOCH_IGNITION); - checkNotNull(referenceFrame, ManeuverKey.MAN_REF_FRAME); - checkNotNaN(duration, ManeuverKey.MAN_DURATION); - checkNotNaN(deltaMass, ManeuverKey.MAN_DELTA_MASS); - checkNotNaN(dV[0], ManeuverKey.MAN_DV_1); - checkNotNaN(dV[1], ManeuverKey.MAN_DV_2); - checkNotNaN(dV[2], ManeuverKey.MAN_DV_3); + checkNotNull(epochIgnition, ManeuverKey.MAN_EPOCH_IGNITION.name()); + checkNotNull(referenceFrame, ManeuverKey.MAN_REF_FRAME.name()); + checkNotNaN(duration, ManeuverKey.MAN_DURATION.name()); + checkNotNaN(deltaMass, ManeuverKey.MAN_DELTA_MASS.name()); + checkNotNaN(dV[0], ManeuverKey.MAN_DV_1.name()); + checkNotNaN(dV[1], ManeuverKey.MAN_DV_2.name()); + checkNotNaN(dV[2], ManeuverKey.MAN_DV_3.name()); } /** Get epoch ignition. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverKey.java index 5979239e9c..1a87fb604f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverWriter.java index eab05130eb..5fdf411899 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/ManeuverWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -54,7 +54,7 @@ protected void writeContent(final Generator generator) throws IOException { // maneuver block generator.writeComments(maneuver.getComments()); - generator.writeEntry(ManeuverKey.MAN_EPOCH_IGNITION.name(), timeConverter, maneuver.getEpochIgnition(), true); + generator.writeEntry(ManeuverKey.MAN_EPOCH_IGNITION.name(), timeConverter, maneuver.getEpochIgnition(), false, true); generator.writeEntry(ManeuverKey.MAN_DURATION.name(), maneuver.getDuration(), Unit.SECOND, true); generator.writeEntry(ManeuverKey.MAN_DELTA_MASS.name(), maneuver.getDeltaMass(), Unit.KILOGRAM, true); generator.writeEntry(ManeuverKey.MAN_REF_FRAME.name(), maneuver.getReferenceFrame().getName(), null, true); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Opm.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Opm.java index 370bea0eea..804ab22e17 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Opm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/Opm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,9 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.NdmConstituent; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.files.ccsds.ndm.odm.KeplerianElements; -import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.section.Segment; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.KeplerianOrbit; @@ -37,7 +37,7 @@ * @author sports * @since 6.1 */ -public class Opm extends NdmConstituent> implements TimeStamped { +public class Opm extends NdmConstituent> implements TimeStamped { /** Root element for XML files. */ public static final String ROOT = "opm"; @@ -55,7 +55,7 @@ public class Opm extends NdmConstituent * @param dataContext used for creating frames, time scales, etc. * @param mu gravitational coefficient to use for building Cartesian/Keplerian orbits */ - public Opm(final Header header, final List> segments, + public Opm(final OdmHeader header, final List> segments, final IERSConventions conventions, final DataContext dataContext, final double mu) { super(header, segments, conventions, dataContext); @@ -65,7 +65,7 @@ public Opm(final Header header, final List> seg /** Get the file metadata. * @return file metadata */ - public CommonMetadata getMetadata() { + public OdmCommonMetadata getMetadata() { return getSegments().get(0).getMetadata(); } @@ -131,7 +131,7 @@ public CartesianOrbit generateCartesianOrbit() { * @return generated orbit */ public KeplerianOrbit generateKeplerianOrbit() { - final CommonMetadata metadata = getMetadata(); + final OdmCommonMetadata metadata = getMetadata(); final OpmData data = getData(); final KeplerianElements keplerianElements = data.getKeplerianElementsBlock(); if (keplerianElements != null) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmData.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmData.java index ef302e5ee4..819a1bb475 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmData.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -88,7 +88,7 @@ public void validate(final double version) { keplerianElementsBlock.validate(version); // in OPM, only semi-major axis is allowed, not mean motion keplerianElementsBlock.checkNotNaN(keplerianElementsBlock.getA(), - KeplerianElementsKey.SEMI_MAJOR_AXIS); + KeplerianElementsKey.SEMI_MAJOR_AXIS.name()); } if (spacecraftParametersBlock != null) { spacecraftParametersBlock.validate(version); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmParser.java index c2b4f1b19b..b712b58626 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,24 +19,25 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Function; import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.ndm.odm.CartesianCovariance; import org.orekit.files.ccsds.ndm.odm.CartesianCovarianceKey; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.files.ccsds.ndm.odm.CommonMetadataKey; -import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.odm.KeplerianElements; import org.orekit.files.ccsds.ndm.odm.KeplerianElementsKey; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey; +import org.orekit.files.ccsds.ndm.odm.OdmParser; import org.orekit.files.ccsds.ndm.odm.SpacecraftParameters; import org.orekit.files.ccsds.ndm.odm.SpacecraftParametersKey; import org.orekit.files.ccsds.ndm.odm.StateVector; import org.orekit.files.ccsds.ndm.odm.StateVectorKey; import org.orekit.files.ccsds.ndm.odm.UserDefined; import org.orekit.files.ccsds.section.CommentsContainer; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.MetadataKey; import org.orekit.files.ccsds.section.Segment; @@ -72,13 +73,13 @@ public class OpmParser extends OdmParser { private final double defaultMass; /** File header. */ - private Header header; + private OdmHeader header; /** File segments. */ - private List> segments; + private List> segments; /** OPM metadata being read. */ - private CommonMetadata metadata; + private OdmCommonMetadata metadata; /** Context binding valid for current metadata. */ private ContextBinding context; @@ -120,13 +121,16 @@ public class OpmParser extends OdmParser { * @param mu gravitational coefficient * @param defaultMass default mass to use if there are no spacecraft parameters block logical block in the file * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ public OpmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, final AbsoluteDate missionReferenceDate, final double mu, - final double defaultMass, final ParsedUnitsBehavior parsedUnitsBehavior) { + final double defaultMass, final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { super(Opm.ROOT, Opm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, - missionReferenceDate, mu, parsedUnitsBehavior); + missionReferenceDate, mu, parsedUnitsBehavior, filters); this.defaultMass = defaultMass; } @@ -145,14 +149,14 @@ public Map getSpecialXmlElementsBuilders() { /** {@inheritDoc} */ @Override - public Header getHeader() { + public OdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(3.0); + header = new OdmHeader(); segments = new ArrayList<>(); metadata = null; context = null; @@ -199,7 +203,7 @@ public boolean prepareMetadata() { if (metadata != null) { return false; } - metadata = new CommonMetadata(); + metadata = new OdmCommonMetadata(); context = new ContextBinding(this::getConventions, this::isSimpleEOP, this::getDataContext, this::getParsedUnitsBehavior, this::getMissionReferenceDate, @@ -457,7 +461,7 @@ private boolean processSpacecraftParametersToken(final ParseToken token) { private boolean processCovarianceToken(final ParseToken token) { if (covarianceBlock == null) { // save the current metadata for later retrieval of reference frame - final CommonMetadata savedMetadata = metadata; + final OdmCommonMetadata savedMetadata = metadata; covarianceBlock = new CartesianCovariance(() -> savedMetadata.getReferenceFrame()); if (moveCommentsIfEmpty(spacecraftParametersBlock, covarianceBlock)) { // get rid of the empty logical block diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmWriter.java index df0e68c4cf..cd1c798af7 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OpmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,12 +22,12 @@ import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.ndm.odm.CartesianCovarianceWriter; -import org.orekit.files.ccsds.ndm.odm.CommonMetadata; +import org.orekit.files.ccsds.ndm.odm.OdmCommonMetadata; import org.orekit.files.ccsds.ndm.odm.CommonMetadataWriter; +import org.orekit.files.ccsds.ndm.odm.OdmHeader; import org.orekit.files.ccsds.ndm.odm.SpacecraftParametersWriter; import org.orekit.files.ccsds.ndm.odm.StateVectorWriter; import org.orekit.files.ccsds.ndm.odm.UserDefinedWriter; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.Segment; import org.orekit.files.ccsds.section.XmlStructureKey; import org.orekit.files.ccsds.utils.ContextBinding; @@ -44,7 +44,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class OpmWriter extends AbstractMessageWriter, Opm> { +public class OpmWriter extends AbstractMessageWriter, Opm> { /** Version number implemented. **/ public static final double CCSDS_OPM_VERS = 3.0; @@ -74,13 +74,13 @@ public OpmWriter(final IERSConventions conventions, final DataContext dataContex /** {@inheritDoc} */ @Override - public void writeSegmentContent(final Generator generator, final double formatVersion, - final Segment segment) + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final Segment segment) throws IOException { // write the metadata final ContextBinding oldContext = getContext(); - final CommonMetadata metadata = segment.getMetadata(); + final OdmCommonMetadata metadata = segment.getMetadata(); setContext(new ContextBinding(oldContext::getConventions, oldContext::isSimpleEOP, oldContext::getDataContext, diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OsculationgKeplerianElementsWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OsculationgKeplerianElementsWriter.java index 52d1b8647c..cb2c6a7856 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OsculationgKeplerianElementsWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/OsculationgKeplerianElementsWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,7 @@ import org.orekit.files.ccsds.ndm.odm.KeplerianElementsKey; import org.orekit.files.ccsds.section.AbstractWriter; import org.orekit.files.ccsds.utils.generation.Generator; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.utils.units.Unit; /** Writer for Keplerian elements data in OPM files. @@ -58,7 +58,7 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(KeplerianElementsKey.INCLINATION.name(), keplerianElements.getI(), Unit.DEGREE, true); generator.writeEntry(KeplerianElementsKey.RA_OF_ASC_NODE.name(), keplerianElements.getRaan(), Unit.DEGREE, true); generator.writeEntry(KeplerianElementsKey.ARG_OF_PERICENTER.name(), keplerianElements.getPa(), Unit.DEGREE, true); - generator.writeEntry(keplerianElements.getAnomalyType() == PositionAngle.TRUE ? + generator.writeEntry(keplerianElements.getAnomalyType() == PositionAngleType.TRUE ? KeplerianElementsKey.TRUE_ANOMALY.name() : KeplerianElementsKey.MEAN_ANOMALY.name(), keplerianElements.getAnomaly(), Unit.DEGREE, diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/XmlSubStructureKey.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/XmlSubStructureKey.java index ad37275616..2ecec5538c 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/XmlSubStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/XmlSubStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/package-info.java index ab9c67b537..04850bf4e5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/opm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/odm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/odm/package-info.java index 55a432bab3..fe25e228f5 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/odm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/odm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/package-info.java index 046fc51043..4ded09232d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/AngleType.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/AngleType.java index f022df2ca2..f7dfe807c3 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/AngleType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/AngleType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/CorrectionApplied.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/CorrectionApplied.java index 40f8304266..7b1c55a38d 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/CorrectionApplied.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/CorrectionApplied.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/DataQuality.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/DataQuality.java index d37ab05174..edd4d55a05 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/DataQuality.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/DataQuality.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/IdentityConverter.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/IdentityConverter.java index 90bb9905e4..29b78b782f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/IdentityConverter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/IdentityConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,12 +19,23 @@ import org.orekit.time.AbsoluteDate; /** - * Identity converter for Range Units.

          + * Identity converter for Range Units. * @author Luc Maisonobe * @since 11.0 */ public class IdentityConverter implements RangeUnitsConverter { + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public IdentityConverter() { + // nothing to do + } + /** {@inheritDoc} */ @Override public double ruToMeters(final TdmMetadata metadata, final AbsoluteDate date, final double range) { diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/IntegrationReference.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/IntegrationReference.java index 15b0c46ac7..b82b16d3fa 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/IntegrationReference.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/IntegrationReference.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/Observation.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/Observation.java index 5bc0a1f515..67f5929413 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/Observation.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/Observation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationType.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationType.java index 6d7689b7a2..4daf56f017 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationType.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlock.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlock.java index 154e4b553c..928a545f97 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlock.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlock.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlockWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlockWriter.java index adbd65d12c..22a51ddd9f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlockWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/ObservationsBlockWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -84,7 +84,7 @@ protected void writeContent(final Generator generator) throws IOException { generator.writeEntry(observation.getType().name(), builder.toString(), null, false); } else { generator.enterSection(TdmDataKey.observation.name()); - generator.writeEntry(TdmDataKey.EPOCH.name(), timeConverter, date, true); + generator.writeEntry(TdmDataKey.EPOCH.name(), timeConverter, date, true, true); generator.writeEntry(type.name(), rawValue, Unit.ONE, true); generator.exitSection(); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeMode.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeMode.java index 99982de637..f8848b6d63 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeMode.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeMode.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnits.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnits.java index f2884033e2..cf9fd0ea0f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnits.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnits.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnitsConverter.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnitsConverter.java index 80831218ec..418aa68368 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnitsConverter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/RangeUnitsConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/Tdm.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/Tdm.java index cd05a8410c..12050c3452 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/Tdm.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/Tdm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,6 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.NdmConstituent; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.Segment; import org.orekit.utils.IERSConventions; @@ -36,7 +35,7 @@ * @author Maxime Journot * @since 9.0 */ -public class Tdm extends NdmConstituent> { +public class Tdm extends NdmConstituent> { /** Root element for XML files. */ public static final String ROOT = "tdm"; @@ -50,7 +49,7 @@ public class Tdm extends NdmConstituent> segments, + public Tdm(final TdmHeader header, final List> segments, final IERSConventions conventions, final DataContext dataContext) { super(header, segments, conventions, dataContext); } diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmDataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmDataKey.java index 3734c53295..5e5a8403e1 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmDataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmDataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmHeader.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmHeader.java new file mode 100644 index 0000000000..ac56cc7d88 --- /dev/null +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmHeader.java @@ -0,0 +1,36 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.ccsds.ndm.tdm; + +import org.orekit.files.ccsds.section.Header; + +/** + * Header of a CCSDS Tracking Data Message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class TdmHeader extends Header { + + /** + * Constructor. + */ + public TdmHeader() { + // classification is not yet allowed in TDM + super(2.0, Double.POSITIVE_INFINITY); + } + +} diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadata.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadata.java index 11509f11fc..51f254bb6e 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadata.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataKey.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataKey.java index 3d0f4d05df..9b268a7e75 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataWriter.java index 495e1911ec..a860fec814 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmMetadataWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -74,8 +74,8 @@ protected void writeContent(final Generator generator) throws IOException { // time generator.writeEntry(MetadataKey.TIME_SYSTEM.name(), metadata.getTimeSystem(), true); - generator.writeEntry(TdmMetadataKey.START_TIME.name(), timeConverter, metadata.getStartTime(), false); - generator.writeEntry(TdmMetadataKey.STOP_TIME.name(), timeConverter, metadata.getStopTime(), false); + generator.writeEntry(TdmMetadataKey.START_TIME.name(), timeConverter, metadata.getStartTime(), false, false); + generator.writeEntry(TdmMetadataKey.STOP_TIME.name(), timeConverter, metadata.getStopTime(), false, false); // participants generator.writeEntry(TdmMetadataKey.PARTICIPANT_1.name(), metadata.getParticipants().get(1), null, true); diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmParser.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmParser.java index f3fa9de117..1a7feff646 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmParser.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,10 +18,10 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.HeaderProcessingState; import org.orekit.files.ccsds.section.KvnStructureProcessingState; import org.orekit.files.ccsds.section.MetadataKey; @@ -47,14 +47,16 @@ * and drop it afterwards, or to use a single-thread loop. *

          * - *

          References:

          - * - CCSDS 503.0-B-1 recommended standard ("Tracking Data Message", Blue Book, Issue 1, November 2007).

          - * - CCSDS 505.0-B-1 recommended standard ("XML Specification for Navigation Data Message", Blue Book, Issue 1, December 2010).

          + *

          References:

          + * * * @author Maxime Journot * @since 9.0 */ -public class TdmParser extends AbstractConstituentParser { +public class TdmParser extends AbstractConstituentParser { /** Converter for {@link RangeUnits#RU Range Units} (may be null). */ private final RangeUnitsConverter converter; @@ -69,7 +71,7 @@ public class TdmParser extends AbstractConstituentParser { private ObservationsBlock observationsBlock; /** File header. */ - private Header header; + private TdmHeader header; /** File segments. */ private List> segments; @@ -89,23 +91,26 @@ public class TdmParser extends AbstractConstituentParser { * @param parsedUnitsBehavior behavior to adopt for handling parsed units * @param converter converter for {@link RangeUnits#RU Range Units} (may be null if there * are no range observations in {@link RangeUnits#RU Range Units}) + * @param filters filters to apply to parse tokens + * @since 12.0 */ public TdmParser(final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, - final ParsedUnitsBehavior parsedUnitsBehavior, final RangeUnitsConverter converter) { - super(Tdm.ROOT, Tdm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, parsedUnitsBehavior); + final ParsedUnitsBehavior parsedUnitsBehavior, final RangeUnitsConverter converter, + final Function>[] filters) { + super(Tdm.ROOT, Tdm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, parsedUnitsBehavior, filters); this.converter = converter; } /** {@inheritDoc} */ @Override - public Header getHeader() { + public TdmHeader getHeader() { return header; } /** {@inheritDoc} */ @Override public void reset(final FileFormat fileFormat) { - header = new Header(2.0); + header = new TdmHeader(); segments = new ArrayList<>(); metadata = null; context = null; diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmWriter.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmWriter.java index eb9e150b6d..97d26cb24f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmWriter.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TdmWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,6 @@ import org.orekit.data.DataContext; import org.orekit.files.ccsds.definitions.TimeSystem; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; -import org.orekit.files.ccsds.section.Header; import org.orekit.files.ccsds.section.Segment; import org.orekit.files.ccsds.utils.ContextBinding; import org.orekit.files.ccsds.utils.generation.AbstractMessageWriter; @@ -34,7 +33,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class TdmWriter extends AbstractMessageWriter, Tdm> { +public class TdmWriter extends AbstractMessageWriter, Tdm> { /** Version number implemented. **/ public static final double CCSDS_TDM_VERS = 2.0; @@ -69,8 +68,8 @@ public TdmWriter(final IERSConventions conventions, final DataContext dataContex /** {@inheritDoc} */ @Override - public void writeSegmentContent(final Generator generator, final double formatVersion, - final Segment segment) + protected void writeSegmentContent(final Generator generator, final double formatVersion, + final Segment segment) throws IOException { // write the metadata diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TimetagReference.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TimetagReference.java index 6ad5054baa..19a5a7521f 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TimetagReference.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TimetagReference.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TrackingMode.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TrackingMode.java index f7fcbdac43..b53553b4ad 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/TrackingMode.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/TrackingMode.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/ndm/tdm/package-info.java b/src/main/java/org/orekit/files/ccsds/ndm/tdm/package-info.java index 4f2535ebad..c245ba03c0 100644 --- a/src/main/java/org/orekit/files/ccsds/ndm/tdm/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/ndm/tdm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/package-info.java b/src/main/java/org/orekit/files/ccsds/package-info.java index 12024d7f97..aa60bc4197 100644 --- a/src/main/java/org/orekit/files/ccsds/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/section/AbstractWriter.java b/src/main/java/org/orekit/files/ccsds/section/AbstractWriter.java index 8fc3c4d8d7..d4bbbfe643 100644 --- a/src/main/java/org/orekit/files/ccsds/section/AbstractWriter.java +++ b/src/main/java/org/orekit/files/ccsds/section/AbstractWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -48,27 +48,34 @@ protected AbstractWriter(final String xmlTag, final String kvnTag) { * @throws IOException if any buffer writing operations fails */ public void write(final Generator generator) throws IOException { + enterSection(generator); + writeContent(generator); + exitSection(generator); + } - // enter section - final boolean needsClosing; + /** Enter the section. + * @param generator generator to use for producing output + * @throws IOException if an I/O error occurs. + * @since 12.0 + */ + public void enterSection(final Generator generator) throws IOException { if (generator.getFormat() == FileFormat.XML) { generator.enterSection(xmlTag); - needsClosing = true; } else if (generator.getFormat() == FileFormat.KVN && kvnTag != null) { generator.enterSection(kvnTag); - needsClosing = true; - } else { - needsClosing = false; } + } - // write content - writeContent(generator); - - // exit section - if (needsClosing) { + /** Exit the section. + * @param generator generator to use for producing output + * @throws IOException if an I/O error occurs. + * @since 12.0 + */ + public void exitSection(final Generator generator) throws IOException { + if (generator.getFormat() == FileFormat.XML || + generator.getFormat() == FileFormat.KVN && kvnTag != null) { generator.exitSection(); } - } /** Write the content of the section, excluding surrounding tags. diff --git a/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java b/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java index d07f2281c2..09ae137dd5 100644 --- a/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java +++ b/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -51,9 +51,9 @@ public CommentsContainer() { * @param field field to check * @param key key associated with the field */ - public void checkNotNegative(final int field, final Enum key) { + public void checkNotNegative(final int field, final String key) { if (field < 0) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, key.name()); + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, key); } } @@ -61,9 +61,9 @@ public void checkNotNegative(final int field, final Enum key) { * @param field field to check * @param key key associated with the field */ - public void checkNotNaN(final double field, final Enum key) { + public void checkNotNaN(final double field, final String key) { if (Double.isNaN(field)) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, key.name()); + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, key); } } @@ -71,9 +71,9 @@ public void checkNotNaN(final double field, final Enum key) { * @param field field to check * @param key key associated with the field */ - public void checkNotNull(final Object field, final Enum key) { + public void checkNotNull(final Object field, final String key) { if (field == null) { - throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, key.name()); + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, key); } } @@ -84,11 +84,11 @@ public void checkNotNull(final Object field, final Enum key) { * @param minVersion version at which key started to be allowed * @param maxVersion version at which key started to be forbidden */ - public void checkAllowed(final double version, final Object field, final Enum key, + public void checkAllowed(final double version, final Object field, final String key, final double minVersion, final double maxVersion) { if (field != null && (version < minVersion || version >= maxVersion)) { throw new OrekitException(OrekitMessages.CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION, - key.name(), version); + key, version); } } diff --git a/src/main/java/org/orekit/files/ccsds/section/Data.java b/src/main/java/org/orekit/files/ccsds/section/Data.java index f6d34bfdf5..778c0ff560 100644 --- a/src/main/java/org/orekit/files/ccsds/section/Data.java +++ b/src/main/java/org/orekit/files/ccsds/section/Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/section/Header.java b/src/main/java/org/orekit/files/ccsds/section/Header.java index ddf1246074..7e403a8a5d 100644 --- a/src/main/java/org/orekit/files/ccsds/section/Header.java +++ b/src/main/java/org/orekit/files/ccsds/section/Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,10 @@ public class Header extends CommentsContainer { /** CCSDS Format version. */ private double formatVersion; - /** File creation date and time in UTC. */ + /** Classification. */ + private String classification; + + /** Message creation date and time in UTC. */ private AbsoluteDate creationDate; /** Creating agency or operator. */ @@ -40,22 +43,31 @@ public class Header extends CommentsContainer { /** Minimum version for {@link HeaderKey#MESSAGE_ID}. */ private final double minVersionMessageId; + /** Minimum version for {@link HeaderKey#CLASSIFICATION}. */ + private final double minVersionClassification; + /** * Constructor. * @param minVersionMessageId minimum version for {@link HeaderKey#MESSAGE_ID} + * @param minVersionClassification minimum version for {@link HeaderKey#CLASSIFICATION} */ - public Header(final double minVersionMessageId) { - this.formatVersion = Double.NaN; - this.minVersionMessageId = minVersionMessageId; + public Header(final double minVersionMessageId, + final double minVersionClassification) { + this.formatVersion = Double.NaN; + this.minVersionMessageId = minVersionMessageId; + this.minVersionClassification = minVersionClassification; } /** {@inheritDoc} */ @Override public void validate(final double version) { super.validate(version); - checkNotNull(creationDate, HeaderKey.CREATION_DATE); - checkNotNull(originator, HeaderKey.ORIGINATOR); - checkAllowed(version, messageId, HeaderKey.MESSAGE_ID, minVersionMessageId, Double.POSITIVE_INFINITY); + checkNotNull(creationDate, HeaderKey.CREATION_DATE.name()); + checkNotNull(originator, HeaderKey.ORIGINATOR.name()); + checkAllowed(version, messageId, HeaderKey.MESSAGE_ID.name(), + minVersionMessageId, Double.POSITIVE_INFINITY); + checkAllowed(version, classification, HeaderKey.CLASSIFICATION.name(), + minVersionClassification, Double.POSITIVE_INFINITY); } /** @@ -75,15 +87,32 @@ public void setFormatVersion(final double formatVersion) { } /** - * Get the file creation date and time in UTC. - * @return the file creation date and time in UTC. + * Get the classification/caveats. + * @return classification/caveats. + */ + public String getClassification() { + return classification; + } + + /** + * Set the classification/caveats. + * @param classification classification/caveats to be set + */ + public void setClassification(final String classification) { + refuseFurtherComments(); + this.classification = classification; + } + + /** + * Get the message creation date and time in UTC. + * @return the message creation date and time in UTC. */ public AbsoluteDate getCreationDate() { return creationDate; } /** - * Set the file creation date and time in UTC. + * Set the message creation date and time in UTC. * @param creationDate the creation date to be set */ public void setCreationDate(final AbsoluteDate creationDate) { @@ -92,15 +121,15 @@ public void setCreationDate(final AbsoluteDate creationDate) { } /** - * Get the file originator. - * @return originator the file originator. + * Get the message originator. + * @return originator the message originator. */ public String getOriginator() { return originator; } /** - * Set the file originator. + * Set the message originator. * @param originator the originator to be set */ public void setOriginator(final String originator) { diff --git a/src/main/java/org/orekit/files/ccsds/section/HeaderKey.java b/src/main/java/org/orekit/files/ccsds/section/HeaderKey.java index 3f8b9feb63..4325edf622 100644 --- a/src/main/java/org/orekit/files/ccsds/section/HeaderKey.java +++ b/src/main/java/org/orekit/files/ccsds/section/HeaderKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,14 +30,19 @@ public enum HeaderKey { COMMENT((token, context, header) -> token.getType() == TokenType.ENTRY ? header.addComment(token.getContentAsNormalizedString()) : true), + /** Classification. + * @since 12.0 + */ + CLASSIFICATION((token, context, header) -> token.processAsFreeTextString(header::setClassification)), + /** Creation date. */ CREATION_DATE((token, context, header) -> token.processAsDate(header::setCreationDate, context)), /** Creating agency or operator. */ - ORIGINATOR((token, context, header) -> token.processAsUppercaseString(header::setOriginator)), + ORIGINATOR((token, context, header) -> token.processAsFreeTextString(header::setOriginator)), /** ID that uniquely identifies a message from a given originator. */ - MESSAGE_ID((token, context, header) -> token.processAsUppercaseString(header::setMessageId)); + MESSAGE_ID((token, context, header) -> token.processAsFreeTextString(header::setMessageId)); /** Processing method. */ private final TokenProcessor processor; diff --git a/src/main/java/org/orekit/files/ccsds/section/HeaderProcessingState.java b/src/main/java/org/orekit/files/ccsds/section/HeaderProcessingState.java index da1d647fec..ac45215fa5 100644 --- a/src/main/java/org/orekit/files/ccsds/section/HeaderProcessingState.java +++ b/src/main/java/org/orekit/files/ccsds/section/HeaderProcessingState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -35,12 +35,12 @@ public class HeaderProcessingState implements ProcessingState { private final ContextBinding context; /** Parser for the complete message. */ - private final AbstractConstituentParser parser; + private final AbstractConstituentParser parser; /** Simple constructor. * @param parser parser for the complete message */ - public HeaderProcessingState(final AbstractConstituentParser parser) { + public HeaderProcessingState(final AbstractConstituentParser parser) { this.context = new ContextBinding( parser::getConventions, parser::isSimpleEOP, parser::getDataContext, parser::getParsedUnitsBehavior, () -> null, diff --git a/src/main/java/org/orekit/files/ccsds/section/KvnStructureKey.java b/src/main/java/org/orekit/files/ccsds/section/KvnStructureKey.java index 18501891dd..b4f2294974 100644 --- a/src/main/java/org/orekit/files/ccsds/section/KvnStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/section/KvnStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -62,7 +62,7 @@ public enum KvnStructureKey { * @param parser file parser * @return true of token was accepted */ - public boolean process(final ParseToken token, final AbstractConstituentParser parser) { + public boolean process(final ParseToken token, final AbstractConstituentParser parser) { return processor.process(token, parser); } @@ -73,7 +73,7 @@ interface TokenProcessor { * @param parser file parser * @return true of token was accepted */ - boolean process(ParseToken token, AbstractConstituentParser parser); + boolean process(ParseToken token, AbstractConstituentParser parser); } } diff --git a/src/main/java/org/orekit/files/ccsds/section/KvnStructureProcessingState.java b/src/main/java/org/orekit/files/ccsds/section/KvnStructureProcessingState.java index 9d71bead77..8566f2db3f 100644 --- a/src/main/java/org/orekit/files/ccsds/section/KvnStructureProcessingState.java +++ b/src/main/java/org/orekit/files/ccsds/section/KvnStructureProcessingState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,12 +28,12 @@ public class KvnStructureProcessingState implements ProcessingState { /** Parser for the complete message. */ - private final AbstractConstituentParser parser; + private final AbstractConstituentParser parser; /** Simple constructor. * @param parser parser for the complete message */ - public KvnStructureProcessingState(final AbstractConstituentParser parser) { + public KvnStructureProcessingState(final AbstractConstituentParser parser) { this.parser = parser; } diff --git a/src/main/java/org/orekit/files/ccsds/section/Metadata.java b/src/main/java/org/orekit/files/ccsds/section/Metadata.java index 549a680816..ad5ce55cc6 100644 --- a/src/main/java/org/orekit/files/ccsds/section/Metadata.java +++ b/src/main/java/org/orekit/files/ccsds/section/Metadata.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -47,7 +47,7 @@ protected Metadata(final TimeSystem defaultTimeSystem) { @Override public void validate(final double version) { super.validate(version); - checkNotNull(timeSystem, MetadataKey.TIME_SYSTEM); + checkNotNull(timeSystem, MetadataKey.TIME_SYSTEM.name()); } /** Get the Time System that: for OPM, is used for metadata, state vector, diff --git a/src/main/java/org/orekit/files/ccsds/section/MetadataKey.java b/src/main/java/org/orekit/files/ccsds/section/MetadataKey.java index 8c15b8e685..9889bf8a4b 100644 --- a/src/main/java/org/orekit/files/ccsds/section/MetadataKey.java +++ b/src/main/java/org/orekit/files/ccsds/section/MetadataKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/section/Section.java b/src/main/java/org/orekit/files/ccsds/section/Section.java index e6cb704cf9..c55fbcfe25 100644 --- a/src/main/java/org/orekit/files/ccsds/section/Section.java +++ b/src/main/java/org/orekit/files/ccsds/section/Section.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/section/Segment.java b/src/main/java/org/orekit/files/ccsds/section/Segment.java index 927aa58599..0c40334b58 100644 --- a/src/main/java/org/orekit/files/ccsds/section/Segment.java +++ b/src/main/java/org/orekit/files/ccsds/section/Segment.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,11 +20,13 @@ * NDM segments are ({@link Metadata}, {@link Data}) pairs. * @author Luc Maisonobe * @since 11.0 + * @param type of the metadata + * @param type of the data */ public class Segment { /** Metadata. */ - private final M metadata; + private M metadata; /** Data. */ private final D data; @@ -46,6 +48,13 @@ public M getMetadata() { return metadata; } + /** Set the segment metadata. + * @param metadata the segment metadata + */ + public void setMetadata(final M metadata) { + this.metadata = metadata; + } + /** Get the segment data. * @return segment data */ diff --git a/src/main/java/org/orekit/files/ccsds/section/XmlStructureKey.java b/src/main/java/org/orekit/files/ccsds/section/XmlStructureKey.java index 760bb67f19..d3545023a0 100644 --- a/src/main/java/org/orekit/files/ccsds/section/XmlStructureKey.java +++ b/src/main/java/org/orekit/files/ccsds/section/XmlStructureKey.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -78,7 +78,7 @@ public enum XmlStructureKey { * @param parser file parser * @return true of token was accepted */ - public boolean process(final ParseToken token, final AbstractConstituentParser parser) { + public boolean process(final ParseToken token, final AbstractConstituentParser parser) { return processor.process(token, parser); } @@ -89,7 +89,7 @@ interface TokenProcessor { * @param parser file parser * @return true of token was accepted */ - boolean process(ParseToken token, AbstractConstituentParser parser); + boolean process(ParseToken token, AbstractConstituentParser parser); } } diff --git a/src/main/java/org/orekit/files/ccsds/section/XmlStructureProcessingState.java b/src/main/java/org/orekit/files/ccsds/section/XmlStructureProcessingState.java index 2121b685e1..235d36e215 100644 --- a/src/main/java/org/orekit/files/ccsds/section/XmlStructureProcessingState.java +++ b/src/main/java/org/orekit/files/ccsds/section/XmlStructureProcessingState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,13 +34,13 @@ public class XmlStructureProcessingState implements ProcessingState { private final String root; /** Parser for the complete message. */ - private final AbstractConstituentParser parser; + private final AbstractConstituentParser parser; /** Simple constructor. * @param root name of the root element * @param parser parser for the complete message */ - public XmlStructureProcessingState(final String root, final AbstractConstituentParser parser) { + public XmlStructureProcessingState(final String root, final AbstractConstituentParser parser) { this.root = root; this.parser = parser; } diff --git a/src/main/java/org/orekit/files/ccsds/section/package-info.java b/src/main/java/org/orekit/files/ccsds/section/package-info.java index 202abc987d..bb2c61d2a6 100644 --- a/src/main/java/org/orekit/files/ccsds/section/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/section/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/ContextBinding.java b/src/main/java/org/orekit/files/ccsds/utils/ContextBinding.java index 0d118be5d8..0d1a403f67 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/ContextBinding.java +++ b/src/main/java/org/orekit/files/ccsds/utils/ContextBinding.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/FileFormat.java b/src/main/java/org/orekit/files/ccsds/utils/FileFormat.java index 3d07ec35d7..9e9b98d390 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/FileFormat.java +++ b/src/main/java/org/orekit/files/ccsds/utils/FileFormat.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractGenerator.java b/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractGenerator.java index d9d2539c44..d61c951a2c 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractGenerator.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -51,6 +51,11 @@ public abstract class AbstractGenerator implements Generator { /** Output name for error messages. */ private final String outputName; + /** Maximum offset for relative dates. + * @since 12.0 + */ + private final double maxRelativeOffset; + /** Flag for writing units. */ private final boolean writeUnits; @@ -63,14 +68,18 @@ public abstract class AbstractGenerator implements Generator { /** Simple constructor. * @param output destination of generated output * @param outputName output name for error messages + * @param maxRelativeOffset maximum offset in seconds to use relative dates + * (if a date is too far from reference, it will be displayed as calendar elements) * @param writeUnits if true, units must be written */ - public AbstractGenerator(final Appendable output, final String outputName, final boolean writeUnits) { - this.output = output; - this.outputName = outputName; - this.writeUnits = writeUnits; - this.sections = new ArrayDeque<>(); - this.siToCcsds = new HashMap<>(); + public AbstractGenerator(final Appendable output, final String outputName, + final double maxRelativeOffset, final boolean writeUnits) { + this.output = output; + this.outputName = outputName; + this.maxRelativeOffset = maxRelativeOffset; + this.writeUnits = writeUnits; + this.sections = new ArrayDeque<>(); + this.siToCcsds = new HashMap<>(); } /** {@inheritDoc} */ @@ -134,9 +143,17 @@ public void writeEntry(final String key, final Enum value, final boolean mand /** {@inheritDoc} */ @Override - public void writeEntry(final String key, final TimeConverter converter, final AbsoluteDate date, final boolean mandatory) + public void writeEntry(final String key, final TimeConverter converter, final AbsoluteDate date, + final boolean forceCalendar, final boolean mandatory) throws IOException { - writeEntry(key, date == null ? (String) null : dateToString(converter, date), null, mandatory); + if (date == null) { + writeEntry(key, (String) null, null, mandatory); + } else { + writeEntry(key, + forceCalendar ? dateToCalendarString(converter, date) : dateToString(converter, date), + null, + mandatory); + } } /** {@inheritDoc} */ @@ -206,6 +223,23 @@ public String doubleToString(final double value) { /** {@inheritDoc} */ @Override public String dateToString(final TimeConverter converter, final AbsoluteDate date) { + + if (converter.getReferenceDate() != null) { + final double relative = date.durationFrom(converter.getReferenceDate()); + if (FastMath.abs(relative) <= maxRelativeOffset) { + // we can use a relative date + return AccurateFormatter.format(relative); + } + } + + // display the date as calendar elements + return dateToCalendarString(converter, date); + + } + + /** {@inheritDoc} */ + @Override + public String dateToCalendarString(final TimeConverter converter, final AbsoluteDate date) { final DateTimeComponents dt = converter.components(date); return dateToString(dt.getDate().getYear(), dt.getDate().getMonth(), dt.getDate().getDay(), dt.getTime().getHour(), dt.getTime().getMinute(), dt.getTime().getSecond()); @@ -332,7 +366,13 @@ private void appendBase(final StringBuilder builder, final CharSequence base) { private void appendExponent(final StringBuilder builder, final Fraction exponent) { if (!exponent.equals(Fraction.ONE)) { builder.append("**"); - builder.append(exponent.equals(Fraction.ONE_HALF) ? "0.5" : exponent); + if (exponent.equals(Fraction.ONE_HALF)) { + builder.append("0.5"); + } else if (exponent.getNumerator() == 3 && exponent.getDenominator() == 2) { + builder.append("1.5"); + } else { + builder.append(exponent); + } } } diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractMessageWriter.java b/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractMessageWriter.java index 849f247a37..1ee34443eb 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractMessageWriter.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/AbstractMessageWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -154,9 +154,14 @@ public void writeHeader(final Generator generator, final H header) throws IOExce generator.enterSection(XmlStructureKey.header.name()); } - // comments are optional if (header != null) { + + // comments are optional generator.writeComments(header.getComments()); + + // classification is optional + generator.writeEntry(HeaderKey.CLASSIFICATION.name(), header.getClassification(), null, false); + } // creation date is informational only, but mandatory and always in UTC @@ -214,7 +219,7 @@ public void writeSegment(final Generator generator, final S segment) throws IOEx * @param segment segment to write * @throws IOException if any buffer writing operations fails */ - public abstract void writeSegmentContent(Generator generator, double formatVersion, S segment) throws IOException; + protected abstract void writeSegmentContent(Generator generator, double formatVersion, S segment) throws IOException; /** {@inheritDoc} */ @Override @@ -225,4 +230,22 @@ public void writeFooter(final Generator generator) throws IOException { generator.endMessage(root); } + /** {@inheritDoc} */ + @Override + public String getRoot() { + return root; + } + + /** {@inheritDoc} */ + @Override + public String getFormatVersionKey() { + return formatVersionKey; + } + + /** {@inheritDoc} */ + @Override + public double getVersion() { + return version; + } + } diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/Generator.java b/src/main/java/org/orekit/files/ccsds/utils/generation/Generator.java index 050f323ce9..72065510b4 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/Generator.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/Generator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -89,10 +89,11 @@ public interface Generator extends AutoCloseable { * @param key the keyword to write * @param converter converter to use for dates * @param date the date to write + * @param forceCalendar if true, the date is forced to calendar format * @param mandatory if true, null values triggers exception, otherwise they are silently ignored * @throws IOException if an I/O error occurs. */ - void writeEntry(String key, TimeConverter converter, AbsoluteDate date, boolean mandatory) throws IOException; + void writeEntry(String key, TimeConverter converter, AbsoluteDate date, boolean forceCalendar, boolean mandatory) throws IOException; /** Write a single key/value entry. * @param key the keyword to write @@ -165,10 +166,18 @@ public interface Generator extends AutoCloseable { /** Convert a date to string value with high precision. * @param converter converter for dates * @param date date to write - * @return date as a string + * @return date as a string (may be either a relative date or a calendar date) */ String dateToString(TimeConverter converter, AbsoluteDate date); + /** Convert a date to calendar string value with high precision. + * @param converter converter for dates + * @param date date to write + * @return date as a calendar string + * @since 12.0 + */ + String dateToCalendarString(TimeConverter converter, AbsoluteDate date); + /** Convert a date to string value with high precision. * @param year year * @param month month diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/KvnGenerator.java b/src/main/java/org/orekit/files/ccsds/utils/generation/KvnGenerator.java index 3dececad75..6c69a0969d 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/KvnGenerator.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/KvnGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -53,6 +53,8 @@ public class KvnGenerator extends AbstractGenerator { * @param paddingWidth padding width for aligning the '=' sign * (not counting the extra blank added before the '=' sign) * @param outputName output name for error messages + * @param maxRelativeOffset maximum offset in seconds to use relative dates + * (if a date is too far from reference, it will be displayed as calendar elements) * @param unitsColumn columns number for aligning units (if negative or zero, units are not output) * @see org.orekit.files.ccsds.ndm.tdm.TdmWriter#KVN_PADDING_WIDTH TdmWriter.KVN_PADDING_WIDTH * @see org.orekit.files.ccsds.ndm.adm.aem.AemWriter#KVN_PADDING_WIDTH AemWriter.KVN_PADDING_WIDTH @@ -63,8 +65,9 @@ public class KvnGenerator extends AbstractGenerator { * @see org.orekit.files.ccsds.ndm.odm.ocm.OcmWriter#KVN_PADDING_WIDTH OcmWriter.KVN_PADDING_WIDTH */ public KvnGenerator(final Appendable output, final int paddingWidth, - final String outputName, final int unitsColumn) { - super(output, outputName, unitsColumn > 0); + final String outputName, final double maxRelativeOffset, + final int unitsColumn) { + super(output, outputName, maxRelativeOffset, unitsColumn > 0); kvFormat = "%-" + FastMath.max(1, paddingWidth) + "s = %s"; final StringBuilder builder = new StringBuilder(COMMENT); builder.append(' '); diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/MessageWriter.java b/src/main/java/org/orekit/files/ccsds/utils/generation/MessageWriter.java index 5f303be045..2e91a5c315 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/MessageWriter.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/MessageWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -66,4 +66,22 @@ default void writeMessage(final Generator generator, final F message) */ void writeFooter(Generator generator) throws IOException; + /** Get root element for XML files. + * @return root element for XML files + * @since 12.0 + */ + String getRoot(); + + /** Get key for format version. + * @return key for format version + * @since 12.0 + */ + String getFormatVersionKey(); + + /** Get current format version. + * @return current format version + * @since 12.0 + */ + double getVersion(); + } diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/XmlGenerator.java b/src/main/java/org/orekit/files/ccsds/utils/generation/XmlGenerator.java index 156f536fbd..dcbf72b769 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/XmlGenerator.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/XmlGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -35,14 +35,38 @@ public class XmlGenerator extends AbstractGenerator { /** Name of the units attribute. */ public static final String UNITS = "units"; + /** NDM/XML version 3 location. + * @since 12.0 + */ + public static final String NDM_XML_V3_SCHEMA_LOCATION = "https://sanaregistry.org/r/ndmxml_unqualified/ndmxml-3.0.0-master-3.0.xsd"; + /** XML prolog. */ private static final String PROLOG = "%n"; - /** Root element start tag. */ - private static final String ROOT_START = "<%s id=\"%s\" version=\"%.1f\">%n"; + /** Root element start tag, with schema. + * @since 12.0 + */ + private static final String ROOT_START_WITH_SCHEMA = "<%s xmlns:xsi=\"%s\" xsi:noNamespaceSchemaLocation=\"%s\" id=\"%s\" version=\"%.1f\">%n"; - /** Element end tag. */ - private static final String START_TAG = "<%s>%n"; + /** Root element start tag, without schema. + * @since 12.0 + */ + private static final String ROOT_START_WITHOUT_SCHEMA = "<%s id=\"%s\" version=\"%.1f\">%n"; + + /** Constant for XML schema instance. + * @since 12.0 + */ + private static final String XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"; + + /** Element end tag. + * @since 12.0 + */ + private static final String START_TAG_WITH_SCHEMA = "<%s xmlns:xsi=\"%s\" xsi:noNamespaceSchemaLocation=\"%s\">%n"; + + /** Element end tag. + * @since 12.0 + */ + private static final String START_TAG_WITHOUT_SCHEMA = "<%s>%n"; /** Element end tag. */ private static final String END_TAG = "%n"; @@ -59,6 +83,9 @@ public class XmlGenerator extends AbstractGenerator { /** Comment key. */ private static final String COMMENT = "COMMENT"; + /** Schema location. */ + private final String schemaLocation; + /** Indentation size. */ private final int indentation; @@ -69,15 +96,21 @@ public class XmlGenerator extends AbstractGenerator { * @param output destination of generated output * @param indentation number of space for each indentation level * @param outputName output name for error messages + * @param maxRelativeOffset maximum offset in seconds to use relative dates + * (if a date is too far from reference, it will be displayed as calendar elements) * @param writeUnits if true, units must be written + * @param schemaLocation schema location to use, may be null * @see #DEFAULT_INDENT + * @see #NDM_XML_V3_SCHEMA_LOCATION * @throws IOException if an I/O error occurs. */ public XmlGenerator(final Appendable output, final int indentation, - final String outputName, final boolean writeUnits) throws IOException { - super(output, outputName, writeUnits); - this.indentation = indentation; - this.level = 0; + final String outputName, final double maxRelativeOffset, + final boolean writeUnits, final String schemaLocation) throws IOException { + super(output, outputName, maxRelativeOffset, writeUnits); + this.schemaLocation = schemaLocation; + this.indentation = indentation; + this.level = 0; writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, PROLOG)); } @@ -91,8 +124,13 @@ public FileFormat getFormat() { @Override public void startMessage(final String root, final String messageTypeKey, final double version) throws IOException { indent(); - writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, ROOT_START, - root, messageTypeKey, version)); + if (schemaLocation == null || level > 0) { + writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, ROOT_START_WITHOUT_SCHEMA, + root, messageTypeKey, version)); + } else { + writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, ROOT_START_WITH_SCHEMA, + root, XMLNS_XSI, schemaLocation, messageTypeKey, version)); + } ++level; } @@ -169,7 +207,13 @@ public void writeEntry(final String key, final String value, final Unit unit, fi @Override public void enterSection(final String name) throws IOException { indent(); - writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, START_TAG, name)); + if (schemaLocation != null && level == 0) { + // top level tag for ndm messages (it is called before enterMessage) + writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, START_TAG_WITH_SCHEMA, + name, XMLNS_XSI, schemaLocation)); + } else { + writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, START_TAG_WITHOUT_SCHEMA, name)); + } ++level; super.enterSection(name); } diff --git a/src/main/java/org/orekit/files/ccsds/utils/generation/package-info.java b/src/main/java/org/orekit/files/ccsds/utils/generation/package-info.java index eb309b834a..c99aeabe25 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/generation/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/utils/generation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/KvnLexicalAnalyzer.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/KvnLexicalAnalyzer.java index e5c1a741c2..1708005402 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/KvnLexicalAnalyzer.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/KvnLexicalAnalyzer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -52,10 +52,7 @@ public class KvnLexicalAnalyzer implements LexicalAnalyzer { private static final String STOP_KEY = "([A-Z][A-Z_0-9]*)_STOP"; /** Regular expression matching a value that must be stored in the matcher. */ - private static final String VALUE = "(\\p{Graph}.*?)"; - - /** Regular expression matching a value that must be stored in the matcher. */ - private static final String OPTIONAL_VALUE = "((?:(\\p{Graph}.*?)?))"; + private static final String OPTIONAL_VALUE = "((?:(?:\\p{Graph}.*?)?))"; /** Operators allowed in units specifications. */ private static final String UNITS_OPERATORS = "-+*×.·/⁄^√⁺⁻"; @@ -87,8 +84,12 @@ public class KvnLexicalAnalyzer implements LexicalAnalyzer { /** Regular expression matching comment entry. */ private static final Pattern COMMENT_ENTRY = Pattern.compile(LINE_START + COMMENT_KEY + OPTIONAL_VALUE + LINE_END); - /** Regular expression matching non-comment entry with optional units. */ - private static final Pattern NON_COMMENT_ENTRY = Pattern.compile(LINE_START + NON_COMMENT_KEY + VALUE + UNITS + LINE_END); + /** Regular expression matching non-comment entry with optional units. + *

          + * Note than since 12.0, we allow empty values at lexical analysis level and detect them at parsing level + *

          + */ + private static final Pattern NON_COMMENT_ENTRY = Pattern.compile(LINE_START + NON_COMMENT_KEY + OPTIONAL_VALUE + UNITS + LINE_END); /** Regular expression matching no-value entry starting a block. */ private static final Pattern START_ENTRY = Pattern.compile(LINE_START + START_KEY + LINE_END); diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzer.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzer.java index d70d1e7fd4..7ec48e2c81 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzer.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzerSelector.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzerSelector.java index ff197f83f3..3fca7e2f00 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzerSelector.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/LexicalAnalyzerSelector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageParser.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageParser.java index fb7f416353..13a3576abf 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageParser.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -59,4 +59,10 @@ public interface MessageParser { */ T build(); + /** Get the file format of the last message parsed. + * @return file format of the last message parsed + * @since 12.0 + */ + FileFormat getFileFormat(); + } diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageVersionXmlTokenBuilder.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageVersionXmlTokenBuilder.java index 9a89084ec0..9a052f2b10 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageVersionXmlTokenBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/MessageVersionXmlTokenBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,9 +19,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.orekit.utils.units.Unit; -import org.xml.sax.Attributes; /** Builder for the root element with CCSDS message version. *

          @@ -51,15 +51,26 @@ public class MessageVersionXmlTokenBuilder implements XmlTokenBuilder { /** Attribute name for version. */ private static final String VERSION = "version"; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public MessageVersionXmlTokenBuilder() { + // nothing to do + } + /** {@inheritDoc} */ @Override - public List buildTokens(final boolean startTag, final String qName, - final String content, final Attributes attributes, + public List buildTokens(final boolean startTag, final boolean isLeaf, final String qName, + final String content, final Map attributes, final int lineNumber, final String fileName) { if (startTag) { // we replace the start tag with the message version specification - final String id = attributes.getValue(ID); - final String version = attributes.getValue(VERSION); + final String id = attributes.get(ID); + final String version = attributes.get(VERSION); final ParseToken start = new ParseToken(TokenType.START, qName, null, Unit.NONE, lineNumber, fileName); final ParseToken entry = new ParseToken(TokenType.ENTRY, id, version, Unit.NONE, lineNumber, fileName); return Arrays.asList(start, entry); diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/ParseToken.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/ParseToken.java index 8e6b776ba7..6933a14a61 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/ParseToken.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/ParseToken.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.bodies.CelestialBodies; import org.orekit.bodies.CelestialBody; @@ -146,8 +147,16 @@ public String getContentAsNormalizedString() { } /** Get the content of the entry as a list of free-text strings. + * @return content of the entry as a list of free-test strings + * @since 12.0 + */ + public List getContentAsFreeTextList() { + return Arrays.asList(SPLIT_AT_COMMAS.split(getRawContent())); + } + + /** Get the content of the entry as a list of normalized strings. *

          - * Free-text strings are normalized by replacing all occurrences + * Normalization is performed by replacing all occurrences * of '_' with space, and collapsing several spaces as one space only. *

          * @return content of the entry as a list of free-test strings @@ -325,6 +334,18 @@ public boolean processAsIndexedUppercaseString(final int index, final IndexedStr return true; } + /** Process the content as a list of free-text strings. + * @param consumer consumer of the free-text strings list + * @return always returns {@code true} + * @since 12.0 + */ + public boolean processAsFreeTextList(final StringListConsumer consumer) { + if (type == TokenType.ENTRY) { + consumer.accept(getContentAsFreeTextList()); + } + return true; + } + /** Process the content as a list of normalized strings. * @param consumer consumer of the normalized strings list * @return always returns {@code true} @@ -395,6 +416,19 @@ public boolean processAsInteger(final IntConsumer consumer) { return true; } + /** Process the content as an indexed integer. + * @param index index + * @param consumer consumer of the integer + * @return always returns {@code true} + * @since 12.0 + */ + public boolean processAsIndexedInteger(final int index, final IndexedIntConsumer consumer) { + if (type == TokenType.ENTRY) { + consumer.accept(index, getContentAsInt()); + } + return true; + } + /** Process the content as an array of integers. No spaces between commas are allowed. * @param consumer consumer of the array * @return always returns {@code true} @@ -423,7 +457,7 @@ public boolean processAsIntegerArrayNoSpace(final IntegerArrayConsumer consumer) public boolean processAsIntegerArray(final IntegerArrayConsumer consumer) { try { if (type == TokenType.ENTRY) { - final String[] fields = SPLIT_AT_COMMAS.split(getRawContent().replace(" ", ",")); + final String[] fields = SPACE.split(getRawContent()); final int[] integers = new int[fields.length]; for (int i = 0; i < fields.length; ++i) { integers[i] = Integer.parseInt(fields[i]); @@ -461,7 +495,7 @@ public boolean processAsDouble(final Unit standard, final ParsedUnitsBehavior be return true; } - /** Process the content as an labeled double. + /** Process the content as a labeled double. * @param label label * @param standard units of parsed content as specified by CCSDS standard * @param behavior behavior to adopt for parsed unit @@ -510,13 +544,63 @@ public boolean processAsDoublyIndexedDouble(final int i, final int j, return true; } + /** Process the content as an array of doubles. + * @param standard units of parsed content as specified by CCSDS standard + * @param behavior behavior to adopt for parsed unit + * @param consumer consumer of the array + * @return always returns {@code true} + * @since 12.0 + */ + public boolean processAsDoubleArray(final Unit standard, final ParsedUnitsBehavior behavior, + final DoubleArrayConsumer consumer) { + try { + if (type == TokenType.ENTRY) { + final String[] fields = SPACE.split(getRawContent()); + final double[] doubles = new double[fields.length]; + for (int i = 0; i < fields.length; ++i) { + doubles[i] = behavior.select(getUnits(), standard).toSI(Double.parseDouble(fields[i])); + } + consumer.accept(doubles); + } + return true; + } catch (NumberFormatException nfe) { + throw generateException(nfe); + } + } + + /** Process the content as an indexed double array. + * @param index index + * @param standard units of parsed content as specified by CCSDS standard + * @param behavior behavior to adopt for parsed unit + * @param consumer consumer of the indexed double array + * @return always returns {@code true} + * @since 12.0 + */ + public boolean processAsIndexedDoubleArray(final int index, + final Unit standard, final ParsedUnitsBehavior behavior, + final IndexedDoubleArrayConsumer consumer) { + if (type == TokenType.ENTRY) { + final String[] fields = SPACE.split(content); + final double[] values = new double[fields.length]; + for (int i = 0; i < fields.length; ++i) { + values[i] = behavior.select(getUnits(), standard).toSI(Double.parseDouble(fields[i])); + } + consumer.accept(index, values); + } + return true; + } + /** Process the content as a vector. + * @param standard units of parsed content as specified by CCSDS standard + * @param behavior behavior to adopt for parsed unit * @param consumer consumer of the vector * @return always returns {@code true} (or throws an exception) */ - public boolean processAsVector(final VectorConsumer consumer) { + public boolean processAsVector(final Unit standard, final ParsedUnitsBehavior behavior, + final VectorConsumer consumer) { if (type == TokenType.ENTRY) { - consumer.accept(getContentAsVector()); + final double scale = behavior.select(getUnits(), standard).getScale(); + consumer.accept(getContentAsVector().scalarMultiply(scale)); } return true; } @@ -604,6 +688,26 @@ public boolean processAsCenterList(final CenterListConsumer consumer, final Cele return true; } + /** Process the content as a rotation sequence. + * @param consumer consumer of the rotation sequence + * @return always returns {@code true} + * @since 12.0 + */ + public boolean processAsRotationOrder(final RotationOrderConsumer consumer) { + if (type == TokenType.ENTRY) { + try { + consumer.accept(RotationOrder.valueOf(getContentAsUppercaseString(). + replace('1', 'X'). + replace('2', 'Y'). + replace('3', 'Z'))); + } catch (IllegalArgumentException iae) { + throw new OrekitException(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE, + getContentAsUppercaseString(), getLineNumber(), getFileName()); + } + } + return true; + } + /** Process the content as a list of units. * @param consumer consumer of the time scale * @return always returns {@code true} (or throws an exception) @@ -638,26 +742,6 @@ public boolean processAsFreeTextString(final StringConsumer consumer) { return true; } - /** Process the content as an array of doubles. - * @param consumer consumer of the array - * @return always returns {@code true} - */ - public boolean processAsDoubleArray(final DoubleArrayConsumer consumer) { - try { - if (type == TokenType.ENTRY) { - final String[] fields = SPLIT_AT_COMMAS.split(getRawContent().replace(" ", ",")); - final double[] doubles = new double[fields.length]; - for (int i = 0; i < fields.length; ++i) { - doubles[i] = Double.parseDouble(fields[i]); - } - consumer.accept(doubles); - } - return true; - } catch (NumberFormatException nfe) { - throw generateException(nfe); - } - } - /** Process the content of the Maneuvrable enum. * @param consumer consumer of the enum * @return always returns {@code true} @@ -789,6 +873,17 @@ public interface IntConsumer { void accept(int value); } + /** Interface representing instance methods that consume indexed integer values. + * @since 12.0 + */ + public interface IndexedIntConsumer { + /** Consume an integer. + * @param index index + * @param value value to consume + */ + void accept(int index, int value); + } + /** Interface representing instance methods that consume integer array. */ public interface IntegerArrayConsumer { /** Consume an array of integers. @@ -841,6 +936,25 @@ public interface DoublyIndexedDoubleConsumer { void accept(int i, int j, double value); } + /** Interface representing instance methods that consume double array. */ + public interface DoubleArrayConsumer { + /** Consume an array of doubles. + * @param doubles array of doubles + */ + void accept(double[] doubles); + } + + /** Interface representing instance methods that consume indexed double array values. + * @since 12.0 + */ + public interface IndexedDoubleArrayConsumer { + /** Consume an indexed double array. + * @param index index + * @param value array value to consume + */ + void accept(int index, double[] value); + } + /** Interface representing instance methods that consume vector values. */ public interface VectorConsumer { /** Consume a vector. @@ -889,6 +1003,16 @@ public interface CenterListConsumer { void accept(List bodyFacades); } + /** Interface representing instance methods that consume otation order values. + * @since 12.0 + */ + public interface RotationOrderConsumer { + /** Consume a data. + * @param value value to consume + */ + void accept(RotationOrder value); + } + /** Interface representing instance methods that consume units lists values. */ public interface UnitListConsumer { /** Consume a list of units. @@ -897,14 +1021,6 @@ public interface UnitListConsumer { void accept(List value); } - /** Interface representing instance methods that consume double array. */ - public interface DoubleArrayConsumer { - /** Consume an array of doubles. - * @param doubles array of doubles - */ - void accept(double[] doubles); - } - /** Interface representing instance methods that consume Maneuvrable values. */ public interface ManeuvrableConsumer { /** Consume a Maneuvrable. diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/RegularXmlTokenBuilder.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/RegularXmlTokenBuilder.java index ed4f5cbe31..18c6dd89e5 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/RegularXmlTokenBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/RegularXmlTokenBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,13 @@ */ package org.orekit.files.ccsds.utils.lexical; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import org.orekit.utils.units.Unit; import org.orekit.utils.units.UnitsCache; -import org.xml.sax.Attributes; /** Regular builder using XML elements names and content for tokens. *

          @@ -48,22 +49,22 @@ public RegularXmlTokenBuilder() { /** {@inheritDoc} */ @Override - public List buildTokens(final boolean startTag, final String qName, - final String content, final Attributes attributes, + public List buildTokens(final boolean startTag, final boolean isLeaf, final String qName, + final String content, final Map attributes, final int lineNumber, final String fileName) { - // elaborate the token type - final TokenType tokenType = (content == null) ? - (startTag ? TokenType.START : TokenType.STOP) : - TokenType.ENTRY; - - // get units - final Unit units = cache.getUnits(attributes.getValue(UNITS)); - - // final build - final ParseToken token = new ParseToken(tokenType, qName, content, units, lineNumber, fileName); - - return Collections.singletonList(token); + if (startTag) { + return Collections.singletonList(new ParseToken(TokenType.START, qName, content, Unit.NONE, lineNumber, fileName)); + } else { + final List built = new ArrayList<>(2); + if (isLeaf) { + // get units + final Unit units = cache.getUnits(attributes.get(UNITS)); + built.add(new ParseToken(TokenType.ENTRY, qName, content, units, lineNumber, fileName)); + } + built.add(new ParseToken(TokenType.STOP, qName, null, Unit.NONE, lineNumber, fileName)); + return built; + } } diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/TokenType.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/TokenType.java index 14ba683ab6..3ef843e65a 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/TokenType.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/TokenType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/UserDefinedXmlTokenBuilder.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/UserDefinedXmlTokenBuilder.java index b25ce06a4d..2b57d35799 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/UserDefinedXmlTokenBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/UserDefinedXmlTokenBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,13 @@ */ package org.orekit.files.ccsds.utils.lexical; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import org.orekit.files.ccsds.ndm.odm.UserDefined; import org.orekit.utils.units.Unit; -import org.xml.sax.Attributes; /** Builder for user-defined parameters. *

          @@ -41,25 +42,37 @@ */ public class UserDefinedXmlTokenBuilder implements XmlTokenBuilder { + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public UserDefinedXmlTokenBuilder() { + // nothing to do + } + /** {@inheritDoc} */ @Override - public List buildTokens(final boolean startTag, final String qName, - final String content, final Attributes attributes, + public List buildTokens(final boolean startTag, final boolean isLeaf, final String qName, + final String content, final Map attributes, final int lineNumber, final String fileName) { - // elaborate the token type - final TokenType tokenType = (content == null) ? - (startTag ? TokenType.START : TokenType.STOP) : - TokenType.ENTRY; - - // final build - final String name = attributes.getValue(UserDefined.USER_DEFINED_XML_ATTRIBUTE); - final ParseToken token = new ParseToken(tokenType, - UserDefined.USER_DEFINED_PREFIX + name, - content, Unit.NONE, - lineNumber, fileName); + // elaborate name + final String name = UserDefined.USER_DEFINED_PREFIX + + attributes.get(UserDefined.USER_DEFINED_XML_ATTRIBUTE); - return Collections.singletonList(token); + if (startTag) { + return Collections.singletonList(new ParseToken(TokenType.START, name, content, Unit.NONE, lineNumber, fileName)); + } else { + final List built = new ArrayList<>(2); + if (isLeaf) { + built.add(new ParseToken(TokenType.ENTRY, name, content, Unit.NONE, lineNumber, fileName)); + } + built.add(new ParseToken(TokenType.STOP, name, null, Unit.NONE, lineNumber, fileName)); + return built; + } } diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlLexicalAnalyzer.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlLexicalAnalyzer.java index 4bccefb80e..024098d6fa 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlLexicalAnalyzer.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlLexicalAnalyzer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; @@ -120,7 +122,17 @@ private class XMLHandler extends DefaultHandler { private String currentContent; /** Attributes of the current element. */ - private Attributes currentAttributes; + private Map currentAttributes; + + /** Last processed token qualified name. + * @since 12.0 + */ + private String lastQname; + + /** Last processed token start/end indicator. + * @since 12.0 + */ + private boolean lastWasStart; /** Simple constructor. * @param messageParser CCSDS Message parser to use @@ -129,6 +141,8 @@ private class XMLHandler extends DefaultHandler { this.messageParser = messageParser; this.regularBuilder = new RegularXmlTokenBuilder(); this.specialElements = messageParser.getSpecialXmlElementsBuilders(); + this.lastQname = ""; + this.lastWasStart = false; } /** Get a builder for the current element. @@ -164,7 +178,7 @@ public void characters(final char[] ch, final int start, final int length) throw // it is the end tag of a leaf element, so we just store the characters // and will either use them or drop them when this next tag is seen currentLineNumber = locator.getLineNumber(); - currentContent = new String(ch, start, length); + this.currentContent = this.currentContent + new String(ch, start, length); } } @@ -173,36 +187,57 @@ public void characters(final char[] ch, final int start, final int length) throw public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) { currentElementName = qName; - currentAttributes = attributes; currentLineNumber = locator.getLineNumber(); - currentContent = null; + currentContent = ""; + + // save attributes in separate map, to avoid overriding during parsing + if (attributes.getLength() == 0) { + currentAttributes = Collections.emptyMap(); + } else { + currentAttributes = new HashMap<>(attributes.getLength()); + for (int i = 0; i < attributes.getLength(); ++i) { + currentAttributes.put(attributes.getQName(i), attributes.getValue(i)); + } + } for (final ParseToken token : getBuilder(qName). - buildTokens(true, qName, currentContent, currentAttributes, - currentLineNumber, source.getName())) { + buildTokens(true, false, qName, getContent(), currentAttributes, + currentLineNumber, source.getName())) { messageParser.process(token); } + lastQname = qName; + lastWasStart = true; } + private String getContent() { + return currentContent.isEmpty() ? null : currentContent; + } + /** {@inheritDoc} */ @Override public void endElement(final String uri, final String localName, final String qName) { - if (currentContent == null) { + if (currentContent == null || currentContent.isEmpty()) { // for an end tag without content, we keep the line number of the end tag itself currentLineNumber = locator.getLineNumber(); } + // check if we are parsing the end tag of a leaf element + final boolean isLeaf = lastWasStart && qName.equals(lastQname); + for (final ParseToken token : getBuilder(qName). - buildTokens(false, qName, currentContent, currentAttributes, + buildTokens(false, isLeaf, qName, getContent(), currentAttributes, currentLineNumber, source.getName())) { messageParser.process(token); } + lastQname = qName; + lastWasStart = true; currentElementName = null; + currentAttributes = null; currentLineNumber = -1; - currentContent = null; + currentContent = ""; } diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlTokenBuilder.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlTokenBuilder.java index 545189f3ed..e05537ec71 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlTokenBuilder.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/XmlTokenBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,7 @@ package org.orekit.files.ccsds.utils.lexical; import java.util.List; - -import org.xml.sax.Attributes; +import java.util.Map; /** Builder for building {@link ParseToken} from XML elements. *

          @@ -39,15 +38,17 @@ public interface XmlTokenBuilder { /** Create a list of parse tokens. * @param startTag if true we are parsing the start tag from an XML element + * @param isLeaf if true and startTag is false, we are processing the end tag of a leaf XML element * @param qName element qualified name * @param content element content * @param attributes element attributes * @param lineNumber number of the line in the CCSDS data message * @param fileName name of the file * @return list of parse tokens + * @since 12.0 */ - List buildTokens(boolean startTag, String qName, - String content, Attributes attributes, + List buildTokens(boolean startTag, boolean isLeaf, String qName, + String content, Map attributes, int lineNumber, String fileName); } diff --git a/src/main/java/org/orekit/files/ccsds/utils/lexical/package-info.java b/src/main/java/org/orekit/files/ccsds/utils/lexical/package-info.java index e3916a2a9f..e256a78eed 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/lexical/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/utils/lexical/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/package-info.java b/src/main/java/org/orekit/files/ccsds/utils/package-info.java index 1e3a60a60d..96fb4bda23 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/utils/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractConstituentParser.java b/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractConstituentParser.java index bc182d88f4..78d0a302c0 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractConstituentParser.java +++ b/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractConstituentParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,10 +16,14 @@ */ package org.orekit.files.ccsds.utils.parsing; +import java.util.List; +import java.util.function.Function; + import org.orekit.data.DataContext; import org.orekit.files.ccsds.ndm.NdmConstituent; import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; import org.orekit.files.ccsds.section.Header; +import org.orekit.files.ccsds.utils.lexical.ParseToken; import org.orekit.utils.IERSConventions; /** Parser for CCSDS messages. @@ -32,12 +36,13 @@ * way to use parsers is to either dedicate one parser for each message * and drop it afterwards, or to use a single-thread loop. *

          + * @param type of the header * @param type of the file * @param

          type of the parser * @author Luc Maisonobe * @since 11.0 */ -public abstract class AbstractConstituentParser, P extends AbstractConstituentParser> +public abstract class AbstractConstituentParser, P extends AbstractConstituentParser> extends AbstractMessageParser { /** IERS Conventions. */ @@ -59,14 +64,17 @@ public abstract class AbstractConstituentParser, * @param simpleEOP if true, tidal effects are ignored when interpolating EOP * @param dataContext used to retrieve frames and time scales * @param parsedUnitsBehavior behavior to adopt for handling parsed units + * @param filters filters to apply to parse tokens + * @since 12.0 */ protected AbstractConstituentParser(final String root, final String formatVersionKey, final IERSConventions conventions, final boolean simpleEOP, final DataContext dataContext, - final ParsedUnitsBehavior parsedUnitsBehavior) { - super(root, formatVersionKey); + final ParsedUnitsBehavior parsedUnitsBehavior, + final Function>[] filters) { + super(root, formatVersionKey, filters); this.conventions = conventions; this.simpleEOP = simpleEOP; this.dataContext = dataContext; @@ -104,7 +112,7 @@ public DataContext getDataContext() { /** Get file header to fill. * @return file header to fill */ - public abstract Header getHeader(); + public abstract H getHeader(); /** Prepare header for parsing. * @return true if parser was able to perform the action diff --git a/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractMessageParser.java b/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractMessageParser.java index d53580bae1..5f90a2b91a 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractMessageParser.java +++ b/src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractMessageParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,18 +17,24 @@ package org.orekit.files.ccsds.utils.parsing; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Function; import org.hipparchus.exception.LocalizedCoreFormats; import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; +import org.orekit.errors.OrekitMessages; import org.orekit.files.ccsds.utils.FileFormat; import org.orekit.files.ccsds.utils.lexical.LexicalAnalyzerSelector; import org.orekit.files.ccsds.utils.lexical.MessageParser; import org.orekit.files.ccsds.utils.lexical.MessageVersionXmlTokenBuilder; import org.orekit.files.ccsds.utils.lexical.ParseToken; +import org.orekit.files.ccsds.utils.lexical.TokenType; import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder; /** Parser for CCSDS messages. @@ -47,6 +53,11 @@ */ public abstract class AbstractMessageParser implements MessageParser { + /** Comment key. + * @since 12.0 + */ + private static final String COMMENT = "COMMENT"; + /** Safety limit for loop over processing states. */ private static final int MAX_LOOP = 100; @@ -56,6 +67,9 @@ public abstract class AbstractMessageParser implements MessageParser { /** Key for format version. */ private final String formatVersionKey; + /** Filters for parse tokens. */ + private final Function>[] filters; + /** Anticipated next processing state. */ private ProcessingState next; @@ -74,10 +88,14 @@ public abstract class AbstractMessageParser implements MessageParser { /** Simple constructor. * @param root root element for XML files * @param formatVersionKey key for format version + * @param filters filters to apply to parse tokens + * @since 12.0 */ - protected AbstractMessageParser(final String root, final String formatVersionKey) { + protected AbstractMessageParser(final String root, final String formatVersionKey, + final Function>[] filters) { this.root = root; this.formatVersionKey = formatVersionKey; + this.filters = filters.clone(); this.current = null; setFallback(new ErrorState()); } @@ -125,10 +143,9 @@ public ProcessingState getCurrent() { return current; } - /** Get the file format. - * @return file format - */ - protected FileFormat getFileFormat() { + /** {@inheritDoc} */ + @Override + public FileFormat getFileFormat() { return format; } @@ -175,13 +192,47 @@ public void anticipateNext(final ProcessingState anticipated) { @Override public void process(final ParseToken token) { - // loop over the various states until one really processes the token - for (int i = 0; i < MAX_LOOP; ++i) { - if (current.processToken(token)) { - return; + // loop over the filters + List filtered = Collections.singletonList(token); + for (Function> filter : filters) { + final ArrayList newFiltered = new ArrayList<>(); + for (final ParseToken original : filtered) { + newFiltered.addAll(filter.apply(original)); + } + filtered = newFiltered; + } + if (filtered.isEmpty()) { + return; + } + + int remaining = filtered.size(); + for (final ParseToken filteredToken : filtered) { + + if (filteredToken.getType() == TokenType.ENTRY && + !COMMENT.equals(filteredToken.getName()) && + (filteredToken.getRawContent() == null || filteredToken.getRawContent().isEmpty())) { + // value is empty, which is forbidden by CCSDS standards + throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, filteredToken.getName()); } - current = next; - next = fallback; + + // loop over the various states until one really processes the token + for (int i = 0; i < MAX_LOOP; ++i) { + if (current.processToken(filteredToken)) { + // filtered token was properly processed + if (--remaining == 0) { + // we have processed all filtered tokens + return; + } else { + // we need to continue processing the remaining filtered tokens + break; + } + } else { + // filtered token was not processed by current processing state, switch to next one + current = next; + next = fallback; + } + } + } // this should never happen diff --git a/src/main/java/org/orekit/files/ccsds/utils/parsing/ErrorState.java b/src/main/java/org/orekit/files/ccsds/utils/parsing/ErrorState.java index 7d714c3036..46f208fa12 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/parsing/ErrorState.java +++ b/src/main/java/org/orekit/files/ccsds/utils/parsing/ErrorState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,17 @@ */ public class ErrorState implements ProcessingState { + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public ErrorState() { + // nothing to do + } + /** {@inheritDoc} *

          * This method always generate an error, as no data is expected in this state. diff --git a/src/main/java/org/orekit/files/ccsds/utils/parsing/ProcessingState.java b/src/main/java/org/orekit/files/ccsds/utils/parsing/ProcessingState.java index 1c9b7fe212..d5531e3cfb 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/parsing/ProcessingState.java +++ b/src/main/java/org/orekit/files/ccsds/utils/parsing/ProcessingState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ccsds/utils/parsing/package-info.java b/src/main/java/org/orekit/files/ccsds/utils/parsing/package-info.java index f257488ffa..4baf28175b 100644 --- a/src/main/java/org/orekit/files/ccsds/utils/parsing/package-info.java +++ b/src/main/java/org/orekit/files/ccsds/utils/parsing/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/general/EphemerisFile.java b/src/main/java/org/orekit/files/general/EphemerisFile.java index efdfc7a331..1c0572686d 100644 --- a/src/main/java/org/orekit/files/general/EphemerisFile.java +++ b/src/main/java/org/orekit/files/general/EphemerisFile.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.propagation.BoundedPropagator; @@ -83,7 +85,7 @@ interface SatelliteEphemerisIn order to view the ephemeris for this satellite as a {@link Propagator} + *

          + * In order to view the ephemeris for this satellite as a {@link Propagator} * several conditions must be met. An Orekit {@link Frame} must be constructable * from the frame specification in the ephemeris file. This condition is met when * {@link EphemerisSegment#getFrame()} return normally for all {@link @@ -130,15 +133,47 @@ interface SatelliteEphemeris + *

          + * The {@link AttitudeProvider attitude provider} used is a {@link FrameAlignedProvider} + * aligned with the {@link EphemerisSegment#getInertialFrame() inertial frame} from the first segment. + *

          * - *

          Each call to this method creates a new propagator. + *

          Each call to this method creates a new propagator.

          * * @return a propagator for all the data in this ephemeris file. */ default BoundedPropagator getPropagator() { + return getPropagator(new FrameAlignedProvider(getSegments().get(0).getInertialFrame())); + } + + /** + * View this ephemeris as a propagator, combining data from all {@link + * #getSegments() segments}. + * + *

          + * In order to view the ephemeris for this satellite as a {@link Propagator} + * several conditions must be met. An Orekit {@link Frame} must be constructable + * from the frame specification in the ephemeris file. This condition is met when + * {@link EphemerisSegment#getFrame()} return normally for all {@link + * #getSegments() segments}. If there are multiple segments they must be adjacent + * such that there are no duplicates or gaps in the ephemeris. The definition of + * adjacent depends on the ephemeris format as some formats define usable start + * and stop times that are different from the ephemeris data start and stop times. + * If these conditions are not met an {@link OrekitException} may be thrown by + * this method or by one of the methods of the returned {@link Propagator}. + *

          + * + *

          Each call to this method creates a new propagator.

          + * + * @param attitudeProvider provider for attitude computation + * @return a propagator for all the data in this ephemeris file. + * @since 12.0 + */ + default BoundedPropagator getPropagator(final AttitudeProvider attitudeProvider) { final List propagators = new ArrayList<>(); for (final EphemerisSegment segment : this.getSegments()) { - propagators.add(segment.getPropagator()); + propagators.add(segment.getPropagator(attitudeProvider)); } return new AggregateBoundedPropagator(propagators); } @@ -161,7 +196,7 @@ interface EphemerisSegment { /** * Get the standard gravitational parameter for the satellite. * - * @return the gravitational parameter used in {@link #getPropagator()}, in m³/s². + * @return the gravitational parameter used in {@link #getPropagator(AttitudeProvider)}, in m³/s². */ double getMu(); @@ -175,7 +210,7 @@ interface EphemerisSegment { /** * Get the inertial reference frame for this ephemeris segment. Defines the - * propagation frame for {@link #getPropagator()}. + * propagation frame for {@link #getPropagator(AttitudeProvider)}. * *

          The default implementation returns {@link #getFrame()} if it is inertial. * Otherwise it returns {@link Frame#getRoot()}. Implementors are encouraged to @@ -247,20 +282,49 @@ default Frame getInertialFrame() { /** * View this ephemeris segment as a propagator. * - *

          In order to view the ephemeris for this satellite as a {@link Propagator} + *

          + * In order to view the ephemeris for this satellite as a {@link Propagator} * several conditions must be met. An Orekit {@link Frame} must be constructable * from the frame specification in the ephemeris file. This condition is met when * {@link EphemerisSegment#getFrame()} return normally. Additionally, * {@link #getMu()} must return a valid value. If these conditions are not met an * {@link OrekitException} may be thrown by this method or by one of the methods * of the returned {@link Propagator}. + *

          + *

          + * The {@link AttitudeProvider attitude provider} used is a {@link FrameAlignedProvider} + * aligned with the {@link #getInertialFrame() inertial frame} + *

          * - *

          Each call to this method creates a new propagator. + *

          Each call to this method creates a new propagator.

          * * @return a propagator for this ephemeris segment. */ default BoundedPropagator getPropagator() { - return new EphemerisSegmentPropagator<>(this); + return new EphemerisSegmentPropagator<>(this, new FrameAlignedProvider(getInertialFrame())); + } + + /** + * View this ephemeris segment as a propagator. + * + *

          + * In order to view the ephemeris for this satellite as a {@link Propagator} + * several conditions must be met. An Orekit {@link Frame} must be constructable + * from the frame specification in the ephemeris file. This condition is met when + * {@link EphemerisSegment#getFrame()} return normally. Additionally, + * {@link #getMu()} must return a valid value. If these conditions are not met an + * {@link OrekitException} may be thrown by this method or by one of the methods + * of the returned {@link Propagator}. + *

          + * + *

          Each call to this method creates a new propagator.

          + * + * @param attitudeProvider provider for attitude computation + * @return a propagator for this ephemeris segment. + * @since 12.0 + */ + default BoundedPropagator getPropagator(final AttitudeProvider attitudeProvider) { + return new EphemerisSegmentPropagator<>(this, attitudeProvider); } } diff --git a/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java b/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java index 589e4b7391..516d81b8a1 100644 --- a/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java +++ b/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java @@ -17,9 +17,10 @@ package org.orekit.files.general; import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.general.EphemerisFile.EphemerisSegment; @@ -31,8 +32,10 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeInterpolator; import org.orekit.utils.ImmutableTimeStampedCache; import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; /** * A {@link Propagator} based on a {@link EphemerisSegment}. @@ -65,9 +68,11 @@ class EphemerisSegmentPropagator extends Abs * Create a {@link Propagator} from an ephemeris segment. * * @param ephemeris segment containing the data for this propagator. + * @param attitudeProvider provider for attitude computation */ - EphemerisSegmentPropagator(final EphemerisSegment ephemeris) { - super(new InertialProvider(ephemeris.getInertialFrame())); + EphemerisSegmentPropagator(final EphemerisSegment ephemeris, + final AttitudeProvider attitudeProvider) { + super(attitudeProvider); this.cache = new ImmutableTimeStampedCache<>( ephemeris.getInterpolationSamples(), ephemeris.getCoordinates()); @@ -96,8 +101,18 @@ class EphemerisSegmentPropagator extends Abs @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { final Stream neighbors = this.cache.getNeighbors(date); - final TimeStampedPVCoordinates point = - TimeStampedPVCoordinates.interpolate(date, ephemeris.getAvailableDerivatives(), neighbors); + + // cast stream to super type + final Stream castedNeighbors = neighbors.map(neighbor -> (TimeStampedPVCoordinates) neighbor); + + // convert to list + final List castedNeighborsList = castedNeighbors.collect(Collectors.toList()); + + // create interpolator + final TimeInterpolator interpolator = + new TimeStampedPVCoordinatesHermiteInterpolator(castedNeighborsList.size(), ephemeris.getAvailableDerivatives()); + + final TimeStampedPVCoordinates point = interpolator.interpolate(date, castedNeighborsList); return ephemerisFrame.getTransformTo(frame, date).transformPVCoordinates(point); } diff --git a/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java b/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java index 16ba930535..bd940a22f3 100644 --- a/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java +++ b/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -164,7 +164,7 @@ public OrekitAttitudeEphemerisSegment addNewSegment(final List } if (interpolationSamples < minimumSampleSize) { - throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION, + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, interpolationSamples); } @@ -192,6 +192,7 @@ public OrekitAttitudeEphemerisSegment addNewSegment(final List } } + /** Ephemeris segment. */ public static class OrekitAttitudeEphemerisSegment implements AttitudeEphemerisFile.AttitudeEphemerisSegment { diff --git a/src/main/java/org/orekit/files/general/OrekitEphemerisFile.java b/src/main/java/org/orekit/files/general/OrekitEphemerisFile.java index 4e1b14a46a..cd645b1f6b 100644 --- a/src/main/java/org/orekit/files/general/OrekitEphemerisFile.java +++ b/src/main/java/org/orekit/files/general/OrekitEphemerisFile.java @@ -242,7 +242,7 @@ public OrekitEphemerisSegment addNewSegment(final List states, } if (interpolationSampleSize < minimumSampleSize) { - throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION, + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, interpolationSampleSize); } @@ -272,6 +272,7 @@ public OrekitEphemerisSegment addNewSegment(final List states, } } + /** Ephemeris segment. */ public static class OrekitEphemerisSegment implements EphemerisFile.EphemerisSegment { diff --git a/src/main/java/org/orekit/files/general/package-info.java b/src/main/java/org/orekit/files/general/package-info.java index 1b5b98d0c1..76b16a46f8 100644 --- a/src/main/java/org/orekit/files/general/package-info.java +++ b/src/main/java/org/orekit/files/general/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ilrs/CPF.java b/src/main/java/org/orekit/files/ilrs/CPF.java index 9a0afc07dc..12f1507787 100644 --- a/src/main/java/org/orekit/files/ilrs/CPF.java +++ b/src/main/java/org/orekit/files/ilrs/CPF.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.attitudes.AttitudeProvider; import org.orekit.files.general.EphemerisFile; import org.orekit.frames.Frame; import org.orekit.propagation.BoundedPropagator; @@ -106,19 +107,6 @@ public List getComments() { return comments; } - /** - * Adds a new P/V coordinate to the satellite. - *

          - * If the header has not been read, the {@link #DEFAULT_ID} is used. - *

          - * @param coord the P/V coordinate of the satellite - * @deprecated as of 11.0.1, replaced by {@link CPF#addSatelliteCoordinate(String, CPFCoordinate)} - */ - @Deprecated - public void addSatelliteCoordinate(final CPFCoordinate coord) { - addSatelliteCoordinate(DEFAULT_ID, coord); - } - /** * Adds a set of P/V coordinates to the satellite. * @param id satellite ILRS identifier @@ -214,15 +202,6 @@ public class CPFEphemeris /** Ephemeris Data. */ private final List coordinates; - /** - * Constructor. - * @deprecated as of 11.0.1, replaced by - */ - @Deprecated - public CPFEphemeris() { - this(null); - } - /** * Constructor. * @param id satellite ID @@ -293,6 +272,12 @@ public BoundedPropagator getPropagator() { return EphemerisSegment.super.getPropagator(); } + /** {@inheritDoc} */ + @Override + public BoundedPropagator getPropagator(final AttitudeProvider attitudeProvider) { + return EphemerisSegment.super.getPropagator(attitudeProvider); + } + /** Get the list of Ephemerides data lines. * @return a reference to the internal list of Ephemerides data lines */ diff --git a/src/main/java/org/orekit/files/ilrs/CPFHeader.java b/src/main/java/org/orekit/files/ilrs/CPFHeader.java index 779ae77065..87fc18c37d 100644 --- a/src/main/java/org/orekit/files/ilrs/CPFHeader.java +++ b/src/main/java/org/orekit/files/ilrs/CPFHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -70,6 +70,17 @@ public class CPFHeader extends ILRSHeader { /** Approximate center of mass to reflector offset [m]. */ private double centerOfMassOffset; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public CPFHeader() { + // nothing to do + } + /** * Get the ephemeris source. * @return the ephemeris source diff --git a/src/main/java/org/orekit/files/ilrs/CPFParser.java b/src/main/java/org/orekit/files/ilrs/CPFParser.java index 4a579816bb..8f159d7b09 100644 --- a/src/main/java/org/orekit/files/ilrs/CPFParser.java +++ b/src/main/java/org/orekit/files/ilrs/CPFParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,9 +19,9 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; -import java.util.Optional; +import java.util.Arrays; +import java.util.Collections; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -132,29 +132,36 @@ public CPF parse(final DataSource source) { final ParseInfo pi = new ParseInfo(); int lineNumber = 0; - Stream parsers = Stream.of(LineParser.H1); - for (String line = br.readLine(); line != null; line = br.readLine()) { - ++lineNumber; - final String l = line; - final Optional selected = parsers.filter(p -> p.canHandle(l)).findFirst(); - if (selected.isPresent()) { - try { - selected.get().parse(line, pi); - } catch (StringIndexOutOfBoundsException | NumberFormatException e) { - throw new OrekitException(e, - OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, source.getName(), line); + Iterable candidateParsers = Collections.singleton(LineParser.H1); + nextLine: + for (String line = br.readLine(); line != null; line = br.readLine()) { + ++lineNumber; + for (final LineParser candidate : candidateParsers) { + if (candidate.canHandle(line)) { + try { + + candidate.parse(line, pi); + + if (pi.done) { + pi.file.setFilter(pi.hasVelocityEntries ? + CartesianDerivativesFilter.USE_PV : + CartesianDerivativesFilter.USE_P); + // Return file + return pi.file; + } + + candidateParsers = candidate.allowedNext(); + continue nextLine; + + } catch (StringIndexOutOfBoundsException | NumberFormatException e) { + throw new OrekitException(e, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, source.getName(), line); + } + } } - parsers = selected.get().allowedNext(); - } - if (pi.done) { - pi.file.setFilter(pi.hasVelocityEntries ? - CartesianDerivativesFilter.USE_PV : - CartesianDerivativesFilter.USE_P); - // Return file - return pi.file; + } - } // We never reached the EOF marker throw new OrekitException(OrekitMessages.CPF_UNEXPECTED_END_OF_FILE, lineNumber); @@ -276,8 +283,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H2, ZERO); + public Iterable allowedNext() { + return Arrays.asList(H2, ZERO); } }, @@ -360,8 +367,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H3, H4, H5, H9, ZERO); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, H9, ZERO); } }, @@ -377,8 +384,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H4, H5, H9, ZERO); + public Iterable allowedNext() { + return Arrays.asList(H4, H5, H9, ZERO); } }, @@ -408,8 +415,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H5, H9, ZERO); + public Iterable allowedNext() { + return Arrays.asList(H5, H9, ZERO); } }, @@ -429,8 +436,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H9, ZERO); + public Iterable allowedNext() { + return Arrays.asList(H9, ZERO); } }, @@ -446,8 +453,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, ZERO); + public Iterable allowedNext() { + return Arrays.asList(TEN, ZERO); } }, @@ -484,8 +491,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -512,8 +519,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -529,8 +536,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -546,8 +553,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -563,8 +570,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -580,8 +587,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -597,8 +604,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -618,9 +625,9 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H1, H2, H3, H4, H5, H9, - TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); + public Iterable allowedNext() { + return Arrays.asList(H1, H2, H3, H4, H5, H9, + TEN, TWENTY, THIRTY, FORTY, FIFTY, SIXTY, SEVENTY, ZERO, EOF); } }, @@ -635,8 +642,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(EOF); + public Iterable allowedNext() { + return Collections.singleton(EOF); } }; @@ -672,7 +679,7 @@ public String getIdentifier() { /** Get the allowed parsers for next line. * @return allowed parsers for next line */ - public abstract Stream allowedNext(); + public abstract Iterable allowedNext(); /** Check if parser can handle line. * @param line line to parse diff --git a/src/main/java/org/orekit/files/ilrs/CPFWriter.java b/src/main/java/org/orekit/files/ilrs/CPFWriter.java index f55ac34911..04ccacfb41 100644 --- a/src/main/java/org/orekit/files/ilrs/CPFWriter.java +++ b/src/main/java/org/orekit/files/ilrs/CPFWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ilrs/CRD.java b/src/main/java/org/orekit/files/ilrs/CRD.java index e03462586f..143ed0dee7 100644 --- a/src/main/java/org/orekit/files/ilrs/CRD.java +++ b/src/main/java/org/orekit/files/ilrs/CRD.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,10 +21,14 @@ import java.util.List; import java.util.SortedSet; import java.util.TreeSet; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; import org.orekit.time.AbsoluteDate; import org.orekit.time.ChronologicalComparator; +import org.orekit.time.TimeScalesFactory; import org.orekit.time.TimeStamped; import org.orekit.utils.ImmutableTimeStampedCache; @@ -32,10 +36,20 @@ * This class stores all the information of the Consolidated laser ranging Data Format (CRD) parsed * by CRDParser. It contains the header and a list of data records. * @author Bryan Cazabonne + * @author Rongwang Li * @since 10.3 */ public class CRD { + /** Value of 'not available' or 'not applicable' or 'no information'. */ + public static final String STR_VALUE_NOT_AVAILABLE = "na"; + + /** String of "NaN". */ + public static final String STR_NAN = "NaN"; + + /** Pattern of "NaN". */ + public static final Pattern PATTERN_NAN = Pattern.compile(STR_NAN); + /** List of comments contained in the file. */ private List comments; @@ -51,6 +65,27 @@ public CRD() { this.dataBlocks = new ArrayList<>(); } + /** + * Format the integer value as a string, or the string VALUE_NOT_AVAILABLE. + * @param value the value + * @param valueNotAvailable the value means not available + * @return a string + * @since 12.0 + */ + public static String formatIntegerOrNaN(final int value, final int valueNotAvailable) { + return value == valueNotAvailable ? STR_VALUE_NOT_AVAILABLE : String.format("%d", value); + } + + /** + * Replace all " NaN" with " na". + * @param crdString the original string + * @return the string + * @since 12.0 + */ + public static String handleNaN(final String crdString) { + return PATTERN_NAN.matcher(crdString).replaceAll(STR_VALUE_NOT_AVAILABLE); + } + /** * Add a data block to the current list of data blocks. * @param dataBlock data block to add @@ -99,6 +134,18 @@ public static class CRDDataBlock { /** Pointing angles records. */ private List anglesData; + /** RangeSupplement records. */ + private List rangeSupplementData; + + /** Session statistics record(s). */ + private List sessionStatisticsData; + + /** Calibration Record(s). */ + private List calibrationData; + + /** Calibration detail record(s). */ + private List calibrationDetailData; + /** * Constructor. */ @@ -107,6 +154,10 @@ public CRDDataBlock() { this.rangeData = new ArrayList<>(); this.meteoData = new TreeSet<>(new ChronologicalComparator()); this.anglesData = new ArrayList<>(); + this.rangeSupplementData = new ArrayList<>(); + this.sessionStatisticsData = new ArrayList<>(); + this.calibrationData = new ArrayList<>(); + this.calibrationDetailData = new ArrayList<>(); } /** @@ -189,6 +240,190 @@ public Meteo getMeteoData() { return new Meteo(meteoData); } + /** + * Add an entry to the list of range supplement data. + * @param rangeSupplement entry to add + * @since 12.0 + */ + public void addRangeSupplementData(final RangeSupplement rangeSupplement) { + rangeSupplementData.add(rangeSupplement); + } + + /** + * Get the range supplement data for the data block. + * @return an unmodifiable list of range supplement data + * @since 12.0 + */ + public List getRangeSupplementData() { + return Collections.unmodifiableList(rangeSupplementData); + } + + /** + * Add an entry to the list of session statistics data. + * @param sessionStatistics entry to add + * @since 12.0 + */ + public void addSessionStatisticsData(final SessionStatistics sessionStatistics) { + sessionStatisticsData.add(sessionStatistics); + } + + /** + * Get the session statistics data for the data block. + * @return an unmodifiable list of session statistics data + * @since 12.0 + */ + public List getSessionStatisticsData() { + return Collections.unmodifiableList(sessionStatisticsData); + } + + /** + * Get the default (the first if there are many records) SessionStat record. + * @return the default (the first if there are many records) session statistics record + * @since 12.0 + */ + public SessionStatistics getSessionStatisticsRecord() { + return getSessionStatisticsRecord(null); + } + + /** + * Get the session statistics record related to the systemConfigurationId. + * @param systemConfigurationId system configuration ID + * @return the session statistics record + * @since 12.0 + */ + public SessionStatistics getSessionStatisticsRecord(final String systemConfigurationId) { + if (sessionStatisticsData.isEmpty()) { + return null; + } + + if (systemConfigurationId == null) { + // default (the first one) + return sessionStatisticsData.get(0); + } + + // Loop to find the appropriate one + for (SessionStatistics sessionStatistics : sessionStatisticsData) { + if (systemConfigurationId.equalsIgnoreCase(sessionStatistics.getSystemConfigurationId())) { + return sessionStatistics; + } + } + + return null; + } + + /** + * Add an entry to the list of calibration data. + * @param cal entry to add + * @since 12.0 + */ + public void addCalibrationData(final Calibration cal) { + calibrationData.add(cal); + } + + /** + * Get the calibration data for the data block. + * @return an unmodifiable list of calibration data + * @since 12.0 + */ + public List getCalibrationData() { + return Collections.unmodifiableList(calibrationData); + } + + /** + * Get the Calibration record(s) related to the default system configuration id. + * @return the Calibration record(s) related to the default system configuration id + * @since 12.0 + */ + public List getCalibrationRecords() { + return getCalibrationRecords(null); + } + + /** + * Get the Calibration record(s) related to the given systemConfigurationId. + * @param systemConfigurationId system configuration ID + * @return the Calibration record(s) + * @since 12.0 + */ + public List getCalibrationRecords(final String systemConfigurationId) { + if (calibrationData.isEmpty()) { + return null; + } + + final String systemConfigId = systemConfigurationId == null ? getConfigurationRecords().getSystemRecord().getConfigurationId() : systemConfigurationId; + + final List list = new ArrayList(); + // Loop to find the appropriate one + for (Calibration calibration : calibrationData) { + if (systemConfigId.equalsIgnoreCase(calibration.getSystemConfigurationId())) { + list.add(calibration); + } + } + + return list; + } + + /** + * Add an entry to the list of calibration detail data. + * @param cal entry to add + * @since 12.0 + */ + public void addCalibrationDetailData(final CalibrationDetail cal) { + calibrationDetailData.add(cal); + } + + /** + * Get the calibration detail data for the data block. + * @return an unmodifiable list of calibration detail data + * @since 12.0 + */ + public List getCalibrationDetailData() { + return Collections.unmodifiableList(calibrationDetailData); + } + + /** + * Get the CalibrationDetail record(s) related to the default system configuration id. + * @return the CalibrationDetail record(s) related to the default system configuration id + * @since 12.0 + */ + public List getCalibrationDetailRecords() { + return getCalibrationDetailRecords(null); + } + + /** + * Get the CalibrationDetail record(s) related to the given systemConfigurationId. + * @param systemConfigurationId system configuration ID + * @return the CalibrationDetail record(s) + * @since 12.0 + */ + public List getCalibrationDetailRecords(final String systemConfigurationId) { + if (calibrationDetailData.isEmpty()) { + return null; + } + + final String systemConfigId = systemConfigurationId == null ? getConfigurationRecords().getSystemRecord().getConfigurationId() : systemConfigurationId; + + final List list = new ArrayList(); + // Loop to find the appropriate one + for (CalibrationDetail calibration : calibrationDetailData) { + if (systemConfigId.equalsIgnoreCase(calibration.getSystemConfigurationId())) { + list.add(calibration); + } + } + + return list; + } + + /** + * Get the wavelength related to the given RangeMeasurement. + * + * @param range a RangeMeasurement + * @return the wavelength related to the given RangeMeasurement. + * @since 12.0 + */ + public double getWavelength(final RangeMeasurement range) { + return getConfigurationRecords().getSystemRecord(range.getSystemConfigurationId()).getWavelength(); + } + } /** Range record. */ @@ -200,6 +435,9 @@ public static class RangeMeasurement implements TimeStamped { /** Time of flight [s]. */ private final double timeOfFlight; + /** System configuration ID. */ + private final String systemConfigurationId; + /** Time event reference indicator. * 0 = ground receive time (at SRP) (two-way) * 1 = spacecraft bounce time (two-way) @@ -237,10 +475,27 @@ public RangeMeasurement(final AbsoluteDate date, public RangeMeasurement(final AbsoluteDate date, final double timeOfFlight, final int epochEvent, final double snr) { - this.date = date; - this.timeOfFlight = timeOfFlight; - this.epochEvent = epochEvent; - this.snr = snr; + this(date, timeOfFlight, epochEvent, snr, null); + } + + /** + * Constructor. + * @param date data epoch + * @param timeOfFlight time of flight in seconds + * @param epochEvent indicates the time event reference + * @param snr signal to noise ratio (can be Double.NaN if unkonwn) + * @param systemConfigurationId system configuration id + * @since 12.0 + */ + public RangeMeasurement(final AbsoluteDate date, + final double timeOfFlight, final int epochEvent, + final double snr, + final String systemConfigurationId) { + this.date = date; + this.timeOfFlight = timeOfFlight; + this.epochEvent = epochEvent; + this.snr = snr; + this.systemConfigurationId = systemConfigurationId; } /** @@ -283,6 +538,459 @@ public AbsoluteDate getDate() { return date; } + /** + * Get the system configuration id. + * @return the system configuration id + * @since 12.0 + */ + public String getSystemConfigurationId() { + return systemConfigurationId; + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + * @since 12.0 + */ + public String toCrdString() { + return "00 not supported. use NptRangeMeasurement or FrRangeMeasurement instead."; + } + } + + /** + * Range record -- Full rate, Sampled Engineering/Quicklook. + * @since 12.0 + */ + public static class FrRangeMeasurement extends RangeMeasurement { + + /** Filter flag. **/ + private final int filterFlag; + + /** Detector channel. **/ + private final int detectorChannel; + + /** Stop number (in multiple-stop system). **/ + private final int stopNumber; + + /** Receive amplitude - a positive linear scale value. **/ + private final int receiveAmplitude; + + /** Transmit amplitude - a positive linear scale value. **/ + private final int transmitAmplitude; + + /** + * Constructor. + * @param date data epoch + * @param timeOfFlight time of flight in seconds + * @param epochEvent indicates the time event reference + * @param systemConfigurationId system configuration id + * @param filterFlag filter flag + * @param detectorChannel detector channel + * @param stopNumber stop number + * @param receiveAmplitude receive amplitude + * @param transmitAmplitude transmit amplitude + */ + public FrRangeMeasurement(final AbsoluteDate date, + final double timeOfFlight, + final int epochEvent, + final String systemConfigurationId, + final int filterFlag, + final int detectorChannel, + final int stopNumber, + final int receiveAmplitude, + final int transmitAmplitude) { + super(date, timeOfFlight, epochEvent, Double.NaN, systemConfigurationId); + this.filterFlag = filterFlag; + this.detectorChannel = detectorChannel; + this.stopNumber = stopNumber; + this.receiveAmplitude = receiveAmplitude; + this.transmitAmplitude = transmitAmplitude; + } + + /** + * Get the filter flag. + * @return the filter flag + */ + public int getFilterFlag() { + return filterFlag; + } + + /** + * Get the detector channel. + * @return the detector channel + */ + public int getDetectorChannel() { + return detectorChannel; + } + + /** + * Get the stop number. + * @return the stop number + */ + public int getStopNumber() { + return stopNumber; + } + + /** + * Get the receive amplitude. + * @return the receive amplitude, -1 if not measured + */ + public int getReceiveAmplitude() { + return receiveAmplitude; + } + + /** + * Get the transmit amplitude. + * @return the transmit amplitude, -1 if not measured + */ + public int getTransmitAmplitude() { + return transmitAmplitude; + } + + /** {@inheritDoc} */ + @Override + @DefaultDataContext + public String toCrdString() { + return String.format("10 %s", toString()); + } + + @Override + @DefaultDataContext + public String toString() { + // CRD suggested format, excluding the record type + // 'local' is already utc. + // Seconds of day (sod) is typically to 1 milllisec precision. + // receiveAmplitude, transmitAmplitude: -1 if not available + final double sod = getDate() + .getComponents(TimeScalesFactory.getUTC()).getTime() + .getSecondsInLocalDay(); + + final String str = String.format( + "%18.12f %18.12f %4s %1d %1d %1d %1d %5s %5s", sod, + getTimeOfFlight(), getSystemConfigurationId(), + getEpochEvent(), filterFlag, detectorChannel, stopNumber, + formatIntegerOrNaN(receiveAmplitude, -1), + formatIntegerOrNaN(transmitAmplitude, -1)); + return handleNaN(str).replace(',', '.'); + } + + } + + /** + * Range record -- Normal Point. + * @since 12.0 + */ + public static class NptRangeMeasurement extends RangeMeasurement { + + /** Normal point window length [s]. */ + private final double windowLength; + + /** Number of raw ranges (after editing) compressed into the normal point. */ + private final int numberOfRawRanges; + + /** Bin RMS from the mean of raw accepted time-of-flight values minus the trend function. */ + private final double binRms; + + /** Bin skew from the mean of raw accepted time-of-flight values minus the trend function. */ + private final double binSkew; + + /** Bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function. */ + private final double binKurtosis; + + /** Bin peak - mean value. */ + private final double binPeakMinusMean; + + /** Return rate [%]. */ + private final double returnRate; + + /** Detector channel. */ + private final int detectorChannel; + + /** + * Constructor. + * @param date data epoch + * @param timeOfFlight time of flight in seconds + * @param epochEvent indicates the time event reference + * @param snr signal to noise ratio (can be Double.NaN if unkonwn) + * @param systemConfigurationId System configuration id + */ + public NptRangeMeasurement(final AbsoluteDate date, + final double timeOfFlight, + final int epochEvent, final double snr, + final String systemConfigurationId) { + this(date, timeOfFlight, epochEvent, snr, systemConfigurationId, -1, + -1, Double.NaN, Double.NaN, Double.NaN, Double.NaN, + Double.NaN, 0); + } + + /** + * Constructor. + * @param date data epoch + * @param timeOfFlight time of flight in seconds + * @param epochEvent indicates the time event reference + * @param snr signal to noise ratio (can be Double.NaN if unkonwn) + * @param systemConfigurationId System configuration id + * @param windowLength normal point window length + * @param numberOfRawRanges number of raw ranges (after editing) compressed into the normal point + * @param binRms Bin RMS from the mean of raw accepted time-of-flight values minus the trend function + * @param binSkew Bin skew from the mean of raw accepted time-of-flight values minus the trend function + * @param binKurtosis Bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function + * @param binPeakMinusMean Bin peak - mean value + * @param returnRate Return rate [%] + * @param detectorChannel detector channel + */ + public NptRangeMeasurement(final AbsoluteDate date, + final double timeOfFlight, + final int epochEvent, final double snr, + final String systemConfigurationId, + final double windowLength, + final int numberOfRawRanges, + final double binRms, final double binSkew, + final double binKurtosis, + final double binPeakMinusMean, + final double returnRate, + final int detectorChannel) { + super(date, timeOfFlight, epochEvent, snr, systemConfigurationId); + + this.windowLength = windowLength; + this.numberOfRawRanges = numberOfRawRanges; + this.binSkew = binSkew; + this.binKurtosis = binKurtosis; + this.binPeakMinusMean = binPeakMinusMean; + this.detectorChannel = detectorChannel; + this.binRms = binRms == -1.0e-12 ? Double.NaN : binRms; // -1=na, ps --> s + this.returnRate = returnRate == -1 ? Double.NaN : returnRate; // -1=na + } + + /** + * Get the normal point window length. + * @return the normal point window length + */ + public double getWindowLength() { + return windowLength; + } + + /** + * Get the umber of raw ranges (after editing) compressed into the normal point. + * @return the umber of raw ranges + */ + public int getNumberOfRawRanges() { + return numberOfRawRanges; + } + + /** + * Get the bin RMS from the mean of raw accepted time-of-flight values minus the trend function. + * @return the bin RMS + */ + public double getBinRms() { + return binRms; + } + + /** + * Get the bin skew from the mean of raw accepted time-of-flight values minus the trend function. + * @return the bin skew + */ + public double getBinSkew() { + return binSkew; + } + + /** + * Get the bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function. + * @return the bin kurtosis + */ + public double getBinKurtosis() { + return binKurtosis; + } + + /** + * Get the bin peak - mean value. + * @return the bin peak - mean value + */ + public double getBinPeakMinusMean() { + return binPeakMinusMean; + } + + /** + * Get the return rate. + * @return the return rate + */ + public double getReturnRate() { + return returnRate; + } + + /** + * Get the detector channel. + * @return the detector channel + */ + public int getDetectorChannel() { + return detectorChannel; + } + + /** {@inheritDoc} */ + @Override + @DefaultDataContext + public String toCrdString() { + return String.format("11 %s", toString()); + } + + @Override + @DefaultDataContext + public String toString() { + // CRD suggested format, excluding the record type + // binRms, binPeakMinusMean: s --> ps + // 'local' is already utc. + // Seconds of day (sod) is typically to 1 milllisec precision. + final double sod = getDate() + .getComponents(TimeScalesFactory.getUTC()).getTime() + .getSecondsInLocalDay(); + + final String str = String.format( + "%18.12f %18.12f %4s %1d %6.1f %6d %9.1f %7.3f %7.3f %9.1f %5.2f %1d %5.1f", + sod, getTimeOfFlight(), getSystemConfigurationId(), + getEpochEvent(), windowLength, numberOfRawRanges, + binRms * 1e12, binSkew, binKurtosis, + binPeakMinusMean * 1e12, returnRate, detectorChannel, + getSnr()); + return handleNaN(str).replace(',', '.'); + } + + } + + /** + * Range Supplement Record. + * @since 12.0 + */ + public static class RangeSupplement implements TimeStamped { + + /** Data epoch. */ + private AbsoluteDate date; + + /** System configuration ID. */ + private final String systemConfigurationId; + + /** Tropospheric refraction correction (one-way). */ + private final double troposphericRefractionCorrection; + + /** Target center of mass correction (one-way). */ + private final double centerOfMassCorrection; + + /** Neutral density (ND) filter value. */ + private final double ndFilterValue; + + /** Time bias applied. */ + private final double timeBiasApplied; + + /** Range rate. */ + private final double rangeRate; + + /** + * Constructor. + * @param date data epoch + * @param systemConfigurationId system configuration ID + * @param troposphericRefractionCorrection tropospheric refraction correction (one-way) + * @param centerOfMassCorrection target center of mass correction (one-way) + * @param ndFilterValue Neutral density (ND) filter value + * @param timeBiasApplied Time bias applied + * @param rangeRate Range rate + */ + public RangeSupplement(final AbsoluteDate date, + final String systemConfigurationId, + final double troposphericRefractionCorrection, + final double centerOfMassCorrection, + final double ndFilterValue, + final double timeBiasApplied, + final double rangeRate) { + this.date = date; + this.systemConfigurationId = systemConfigurationId; + this.troposphericRefractionCorrection = troposphericRefractionCorrection; + this.centerOfMassCorrection = centerOfMassCorrection; + this.ndFilterValue = ndFilterValue; + this.timeBiasApplied = timeBiasApplied; + this.rangeRate = rangeRate; + } + + @Override + public AbsoluteDate getDate() { + return date; + } + + /** + * Get the system configuration id. + * @return the system configuration id + */ + public String getSystemConfigurationId() { + return systemConfigurationId; + } + + /** + * Get the tropospheric refraction correction. + * @return the tropospheric refraction correction + */ + public double getTroposphericRefractionCorrection() { + return troposphericRefractionCorrection; + } + + /** + * Get the target center of mass. + * @return the target center of mass + */ + public double getCenterOfMassCorrection() { + return centerOfMassCorrection; + } + + /** + * Get the neutral density (ND) filter value. + * @return the neutral density (ND) filter value + */ + public double getNdFilterValue() { + return ndFilterValue; + } + + /** + * Get the time bias applied. + * @return the time bias applied + */ + public double getTimeBiasApplied() { + return timeBiasApplied; + } + + /** + * Get the range rate. + * @return the range rate + */ + public double getRangeRate() { + return rangeRate; + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + */ + @DefaultDataContext + public String toCrdString() { + return String.format("12 %s", toString()); + } + + @Override + @DefaultDataContext + public String toString() { + // CRD suggested format, excluding the record type + // troposphericRefractionCorrection: s --> ps + // 'local' is already utc. + // Seconds of day (sod) is typically to 1 milllisec precision. + final double sod = getDate() + .getComponents(TimeScalesFactory.getUTC()).getTime() + .getSecondsInLocalDay(); + + final String str = String.format( + "%18.12f %4s %6.1f %6.4f %5.2f %8.4f %f", sod, + getSystemConfigurationId(), + troposphericRefractionCorrection * 1e12, + centerOfMassCorrection, ndFilterValue, timeBiasApplied, + rangeRate); + return handleNaN(str).replace(',', '.'); + } + } /** This data record contains a minimal set of meteorological data. */ @@ -300,6 +1008,11 @@ public static class MeteorologicalMeasurement implements TimeStamped { /** Relative humidity at the surface [%]. */ private final double humidity; + /** Origin of values. + * 0=measured values, 1=interpolated values + */ + private final int originOfValues; + /** * Constructor. * @param date data epoch @@ -310,10 +1023,24 @@ public static class MeteorologicalMeasurement implements TimeStamped { public MeteorologicalMeasurement(final AbsoluteDate date, final double pressure, final double temperature, final double humidity) { - this.date = date; - this.pressure = pressure; - this.temperature = temperature; - this.humidity = humidity; + this(date, pressure, temperature, humidity, 0); + } + + /** + * Constructor. + * @param date data epoch + * @param pressure the surface pressure in bars + * @param temperature the surface temperature in degrees Kelvin + * @param humidity the relative humidity at the surface in percents + * @param originOfValues Origin of values + */ + public MeteorologicalMeasurement(final AbsoluteDate date, final double pressure, final double temperature, + final double humidity, final int originOfValues) { + this.date = date; + this.pressure = pressure; + this.temperature = temperature; + this.humidity = humidity; + this.originOfValues = originOfValues; } /** @@ -346,6 +1073,41 @@ public AbsoluteDate getDate() { return date; } + /** Get the origin of values. + * 0=measure values + * 1=interpolated values + * @return the origin of values + * @since 12.0 + */ + public int getOriginOfValues() { + return originOfValues; + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + * @since 12.0 + */ + @DefaultDataContext + public String toCrdString() { + return String.format("20 %s", toString()); + } + + @Override + @DefaultDataContext + public String toString() { + // CRD suggested format, excluding the record type + // pressure: bar --> mbar + // 'local' is already utc. + // Seconds of day (sod) is typically to 1 milllisec precision. + final double sod = getDate() + .getComponents(TimeScalesFactory.getUTC()).getTime() + .getSecondsInLocalDay(); + + final String str = String.format("%9.3f %7.2f %6.2f %4.0f %1d", sod, + pressure * 1e3, temperature, humidity, originOfValues); + return handleNaN(str).replace(',', '.'); + } } /** Pointing angles record. */ @@ -480,6 +1242,36 @@ public AbsoluteDate getDate() { return date; } + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + * @since 12.0 + */ + @DefaultDataContext + public String toCrdString() { + return String.format("30 %s", toString()); + } + + @Override + @DefaultDataContext + public String toString() { + // CRD suggested format, excluding the record type + // azimuth, elevation: rad --> deg + // azimuthRate, elevationRate: rad/s --> deg/s + // 'local' is already utc. + // Seconds of day (sod) is typically to 1 milllisec precision. + final double sod = getDate() + .getComponents(TimeScalesFactory.getUTC()).getTime() + .getSecondsInLocalDay(); + + final String str = String.format( + "%9.3f %8.4f %8.4f %1d %1d %1d %10.7f %10.7f", sod, + FastMath.toDegrees(azimuth), FastMath.toDegrees(elevation), + directionFlag, originIndicator, refractionCorrected ? 1 : 0, + FastMath.toDegrees(azimuthRate), + FastMath.toDegrees(elevationRate)); + return handleNaN(str).replace(',', '.'); + } } /** Meteorological data. */ @@ -552,7 +1344,7 @@ public List getData() { public MeteorologicalMeasurement getMeteo(final AbsoluteDate date) { // Check if meteorological data are available - if (meteo.getNeighborsSize() == 0) { + if (meteo.getMaxNeighborsSize() == 0) { return null; } @@ -626,4 +1418,548 @@ private double getLinearInterpolation(final AbsoluteDate date, } + /** + * Calibration Record. + * @since 12.0 + */ + public static class Calibration implements TimeStamped { + + /** Data epoch. */ + private final AbsoluteDate date; + + /** + * Type of data. + * + * 0=station combined transmit and receive calibration (“normal” SLR/LLR) + * 1=station transmit calibration (e.g., one-way ranging to transponders) + * 2=station receive calibration + * 3=target combined transmit and receive calibrations + * 4=target transmit calibration + * 5=target receive calibration + */ + private final int typeOfData; + + /** System configuration ID. */ + private final String systemConfigurationId; + + /** Number of data points recorded. */ + private final int numberOfPointsRecorded; + + /** Number of data points used. */ + private final int numberOfPointsUsed; + + /** One-way target distance (meters, nominal). */ + private final double oneWayDistance; + + /** Calibration System Delay. */ + private final double systemDelay; + + /** Calibration Delay Shift - a measure of calibration stability. */ + private final double delayShift; + + /** RMS of raw system delay. */ + private final double rms; + + /** Skew of raw system delay values from the mean. */ + private final double skew; + + /** Kurtosis of raw system delay values from the mean. */ + private final double kurtosis; + + /** System delay peak – mean value. */ + private final double peakMinusMean; + + /** + * Calibration Type Indicator. + * + * 0=not used or undefined + * 1=nominal (from once off assessment) + * 2=external calibrations + * 3=internal calibrations – telescope + * 4=internal calibrations – building + * 5=burst calibrations + * 6=other + */ + private final int typeIndicator; + + /** + * Calibration Shift Type Indicator. + * + * 0=not used or undefined + * 1=nominal (from once off assessment) + * 2=pre- to post- Shift + * 3=minimum to maximum + * 4=other + */ + private final int shiftTypeIndicator; + + /** Detector Channel. + * + * 0=not applicable or “all” + * 1-4 for quadrant + * 1-n for many channels + */ + private final int detectorChannel; + + /** + * Calibration Span. + * + * 0 = not applicable (e.g. Calibration type indicator is “nominal”) + * 1 = Pre-calibration only + * 2 = Post-calibration only + * 3 = Combined (pre- and post-calibrations or multiple) + * 4 = Real-time calibration (data taken while ranging to a satellite) + */ + private final int span; + + /** Return Rate (%). */ + private final double returnRate; + + /** + * Constructor. + * @param date data epoch + * @param typeOfData type of data + * @param systemConfigurationId system configuration id + * @param numberOfPointsRecorded number of data points recorded + * @param numberOfPointsUsed number of data points used + * @param oneWayDistance one-way target distance (nominal) + * @param systemDelay calibration system delay + * @param delayShift calibration delay shift - a measure of calibration stability + * @param rms RMS of raw system delay + * @param skew skew of raw system delay values from the mean. + * @param kurtosis kurtosis of raw system delay values from the mean. + * @param peakMinusMean system delay peak – mean value + * @param typeIndicator calibration type indicator + * @param shiftTypeIndicator calibration shift type indicator + * @param detectorChannel detector channel + * @param span calibration span + * @param returnRate return rate (%) + */ + public Calibration(final AbsoluteDate date, final int typeOfData, + final String systemConfigurationId, + final int numberOfPointsRecorded, + final int numberOfPointsUsed, + final double oneWayDistance, + final double systemDelay, final double delayShift, + final double rms, final double skew, + final double kurtosis, final double peakMinusMean, + final int typeIndicator, final int shiftTypeIndicator, + final int detectorChannel, final int span, + final double returnRate) { + this.date = date; + this.typeOfData = typeOfData; + this.systemConfigurationId = systemConfigurationId; + this.numberOfPointsRecorded = numberOfPointsRecorded; + this.numberOfPointsUsed = numberOfPointsUsed; + this.systemDelay = systemDelay; + this.delayShift = delayShift; + this.rms = rms; + this.skew = skew; + this.kurtosis = kurtosis; + this.peakMinusMean = peakMinusMean; + this.typeIndicator = typeIndicator; + this.shiftTypeIndicator = shiftTypeIndicator; + this.detectorChannel = detectorChannel; + this.span = span; + this.returnRate = returnRate; + this.oneWayDistance = oneWayDistance == -1 ? Double.NaN : oneWayDistance; // -1=na + } + + @Override + public AbsoluteDate getDate() { + return date; + } + + /** + * Get the type of data. + * + *
            + *
          • 0=station combined transmit and receive calibration (“normal” SLR/LLR) + *
          • 1=station transmit calibration (e.g., one-way ranging to transponders) + *
          • 2=station receive calibration + *
          • 3=target combined transmit and receive calibrations + *
          • 4=target transmit calibration + *
          • 5=target receive calibration + *
          + * @return the type of data + */ + public int getTypeOfData() { + return typeOfData; + } + + /** + * Get the system configuration id. + * @return the system configuration id + */ + public String getSystemConfigurationId() { + return systemConfigurationId; + } + + /** + * Get the number of data points recorded. + * @return the number of data points recorded, -1 if no information + */ + public int getNumberOfPointsRecorded() { + return numberOfPointsRecorded; + } + + /** + * Get the number of data points used. + * @return the number of data points used, -1 if no information + */ + public int getNumberOfPointsUsed() { + return numberOfPointsUsed; + } + + /** + * Get the one-way target distance (nominal). + * @return the one-way target distance (nominal) + */ + public double getOneWayDistance() { + return oneWayDistance; + } + + /** + * Get the calibration system delay. + * @return the calibration system delay + */ + public double getSystemDelay() { + return systemDelay; + } + + /** + * Get the calibration delay shift. + * @return the calibration delay shift + */ + public double getDelayShift() { + return delayShift; + } + + /** + * Get the rms of raw system delay. + * @return the rms of raw system delay + */ + public double getRms() { + return rms; + } + + /** + * Get the skew of raw system delay values from the mean. + * @return the skew of raw system delay values from the mean. + */ + public double getSkew() { + return skew; + } + + /** + * Get the kurtosis of raw system delay values from the mean. + * @return the kurtosis of raw system delay values from the mean. + */ + public double getKurtosis() { + return kurtosis; + } + + /** + * Get the system delay peak – mean value. + * @return the system delay peak – mean value + */ + public double getPeakMinusMean() { + return peakMinusMean; + } + + /** + * Get the calibration type indicator. + * + *
            + *
          • 0=not used or undefined + *
          • 1=nominal (from once off assessment) + *
          • 2=external calibrations + *
          • 3=internal calibrations – telescope + *
          • 4=internal calibrations – building + *
          • 5=burst calibrations + *
          • 6=other + *
          + * @return the calibration type indicator + */ + public int getTypeIndicator() { + return typeIndicator; + } + + /** + * Get the calibration shift type indicator. + * + *
            + *
          • 0=not used or undefined + *
          • 1=nominal (from once off assessment) + *
          • 2=pre- to post- Shift + *
          • 3=minimum to maximum + *
          • 4=other + *
          + * @return the calibration shift type indicator + */ + public int getShiftTypeIndicator() { + return shiftTypeIndicator; + } + + /** + * Get the detector channel. + * + *
            + *
          • 0=not applicable or “all” + *
          • 1-4 for quadrant + *
          • 1-n for many channels + *
          + * @return the detector channel + */ + public int getDetectorChannel() { + return detectorChannel; + } + + /** + * Get the calibration span. + * + *
            + *
          • 0 = not applicable (e.g. Calibration type indicator is “nominal”) + *
          • 1 = Pre-calibration only + *
          • 2 = Post-calibration only + *
          • 3 = Combined (pre- and post-calibrations or multiple) + *
          • 4 = Real-time calibration (data taken while ranging to a satellite) + *
          + * @return the calibration span + */ + public int getSpan() { + return span; + } + + /** + * Get the return rate. + * @return the return rate + */ + public double getReturnRate() { + return returnRate; + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + */ + @DefaultDataContext + public String toCrdString() { + return String.format("40 %s", toString()); + } + + @Override + @DefaultDataContext + public String toString() { + // CRD suggested format, excluding the record type + // systemDelay, delayShift: s --> ps + // rms, peakMinusMean: s --> ps + // 'local' is already utc. + // Seconds of day (sod) is typically to 1 milllisec precision. + final double sod = getDate() + .getComponents(TimeScalesFactory.getUTC()).getTime() + .getSecondsInLocalDay(); + + final String str = String.format( + "%18.12f %1d %4s %8s %8s %8.4f %10.1f %8.1f %6.1f %7.3f %7.3f %6.1f %1d %1d %1d %1d %5.1f", + sod, typeOfData, systemConfigurationId, + formatIntegerOrNaN(numberOfPointsRecorded, -1), + formatIntegerOrNaN(numberOfPointsUsed, -1), oneWayDistance, + systemDelay * 1e12, delayShift * 1e12, rms * 1e12, skew, + kurtosis, peakMinusMean * 1e12, typeIndicator, + shiftTypeIndicator, detectorChannel, span, returnRate); + return handleNaN(str).replace(',', '.'); + } + + } + + /** + * Calibration Detail Record. + * @since 12.0 + */ + public static class CalibrationDetail extends Calibration { + // same as Calibration record except that the record type is '41' rather than '40'. + + /** + * Constructor. + * @param date data epoch + * @param typeOfData type of data + * @param systemConfigurationId system configuration id + * @param numberOfPointsRecorded number of data points recorded + * @param numberOfPointsUsed number of data points used + * @param oneWayDistance one-way target distance (nominal) + * @param systemDelay calibration system delay + * @param delayShift calibration delay shift - a measure of calibration stability + * @param rms RMS of raw system delay + * @param skew skew of raw system delay values from the mean. + * @param kurtosis kurtosis of raw system delay values from the mean. + * @param peakMinusMean system delay peak – mean value + * @param typeIndicator calibration type indicator + * @param shiftTypeIndicator calibration shift type indicator + * @param detectorChannel detector channel + * @param span calibration span + * @param returnRate return rate (%) + */ + public CalibrationDetail(final AbsoluteDate date, final int typeOfData, + final String systemConfigurationId, + final int numberOfPointsRecorded, + final int numberOfPointsUsed, final double oneWayDistance, + final double systemDelay, final double delayShift, + final double rms, final double skew, final double kurtosis, + final double peakMinusMean, final int typeIndicator, + final int shiftTypeIndicator, final int detectorChannel, + final int span, final double returnRate) { + super(date, typeOfData, systemConfigurationId, numberOfPointsRecorded, + numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew, + kurtosis, peakMinusMean, typeIndicator, shiftTypeIndicator, + detectorChannel, span, returnRate); + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + */ + @DefaultDataContext + public String toCrdString() { + return String.format("41 %s", toString()); + } + + } + + /** + * Session (Pass) Statistics Record. + * @since 12.0 + */ + public static class SessionStatistics { + + /** System configuration ID. */ + private final String systemConfigurationId; + + /** Session RMS from the mean of raw accepted time-of-flight values minus the trend function. */ + private final double rms; + + /** Session skewness from the mean of raw accepted time-of-flight values minus the trend function. */ + private final double skewness; + + /** Session kurtosis from the mean of raw accepted time-of-flight values minus the trend function. */ + private final double kurtosis; + + /** Session peak – mean value. */ + private final double peakMinusMean; + + /** + * Data quality assessment indicator. + *
            + *
          • 0=undefined or no comment
          • + *
          • 1=clear, easily filtered data, with little or no noise
          • + *
          • 2=clear data with some noise; filtering is slightly compromised by noise level
          • + *
          • 3=clear data with a significant amount of noise, or weak data with little noise. Data are certainly + * present, but filtering is difficult.
          • + *
          • 4=unclear data; data appear marginally to be present, but are very difficult to separate from noise + * during filtering. Signal to noise ratio can be less than 1:1.
          • + *
          • 5=no data apparent
          • + *
          + */ + private final int dataQulityIndicator; + + /** + * Constructor. + * @param systemConfigurationId system configuration ID + * @param rms session RMS from the mean of raw accepted time-of-flight values minus the trend function + * @param skewness session skewness from the mean of raw accepted time-of-flight values minus the trend function + * @param kurtosis session kurtosis from the mean of raw accepted time-of-flight values minus the trend function + * @param peakMinusMean session peak – mean value + * @param dataQulityIndicator data quality assessment indicator + */ + public SessionStatistics(final String systemConfigurationId, + final double rms, final double skewness, + final double kurtosis, + final double peakMinusMean, + final int dataQulityIndicator) { + this.systemConfigurationId = systemConfigurationId; + this.rms = rms; + this.skewness = skewness; + this.kurtosis = kurtosis; + this.peakMinusMean = peakMinusMean; + this.dataQulityIndicator = dataQulityIndicator; + } + + /** + * Get system configuration id. + * @return the system configuration id + */ + public String getSystemConfigurationId() { + return systemConfigurationId; + } + + /** + * Get the session RMS from the mean of raw accepted time-of-flight values minus the trend function. + * @return the session RMS + */ + public double getRms() { + return rms; + } + + /** + * Get the session skewness from the mean of raw accepted time-of-flight values minus the trend function. + * @return the session skewness + */ + public double getSkewness() { + return skewness; + } + + /** + * Get the session kurtosis from the mean of raw accepted time-of-flight values minus the trend function. + * @return the session kurtosis + */ + public double getKurtosis() { + return kurtosis; + } + + /** + * Get the session peak – mean value. + * @return the session peak – mean value + */ + public double getPeakMinusMean() { + return peakMinusMean; + } + + /** + * Get the data quality assessment indicator + *
            + *
          • 0=undefined or no comment
          • + *
          • 1=clear, easily filtered data, with little or no noise
          • + *
          • 2=clear data with some noise; filtering is slightly compromised by noise level
          • + *
          • 3=clear data with a significant amount of noise, or weak data with little noise. Data are certainly + * present, but filtering is difficult.
          • + *
          • 4=unclear data; data appear marginally to be present, but are very difficult to separate from noise + * during filtering. Signal to noise ratio can be less than 1:1.
          • + *
          • 5=no data apparent
          • + *
          + * @return the data quality assessment indicator + */ + public int getDataQulityIndicator() { + return dataQulityIndicator; + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + */ + public String toCrdString() { + return String.format("50 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + // rms, peakMinusMean: s --> ps + final String str = String.format("%4s %6.1f %7.3f %7.3f %6.1f %1d", + systemConfigurationId, rms * 1e12, skewness, kurtosis, + peakMinusMean * 1e12, dataQulityIndicator); + return handleNaN(str).replace(',', '.'); + } + + } + } diff --git a/src/main/java/org/orekit/files/ilrs/CRDConfiguration.java b/src/main/java/org/orekit/files/ilrs/CRDConfiguration.java index e25bf72e8d..e04984cfe2 100644 --- a/src/main/java/org/orekit/files/ilrs/CRDConfiguration.java +++ b/src/main/java/org/orekit/files/ilrs/CRDConfiguration.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,48 +16,51 @@ */ package org.orekit.files.ilrs; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + /** * Container for Consolidated laser ranging Data Format (CDR) configuration records. * @author Bryan Cazabonne + * @author Rongwang Li * @since 10.3 */ public class CRDConfiguration { - /** System configuration record. */ - private SystemConfiguration systemRecord; - - /** Laser configuration record. */ - private LaserConfiguration laserRecord; - - /** Detector configuration record. */ - private DetectorConfiguration detectorRecord; - - /** Timing system configuration record. */ - private TimingSystemConfiguration timingRecord; + /** Dict of configuration record. **/ + private Map mapConfigurationRecords; - /** Transponder configuration record. */ - private TransponderConfiguration transponderRecord; + /** List of system configuration. **/ + private List systemConfigurationRecords; - /** Software configuration record. */ - private SoftwareConfiguration softwareRecord; - - /** Meteorological configuration record. */ - private MeteorologicalConfiguration meteorologicalRecord; + /** + * Constructor. + */ + public CRDConfiguration() { + systemConfigurationRecords = new ArrayList<>(); + mapConfigurationRecords = new Hashtable<>(); + } /** * Get the system configuration record. * @return the system configuration record */ public SystemConfiguration getSystemRecord() { - return systemRecord; + return systemConfigurationRecords.isEmpty() ? null : systemConfigurationRecords.get(0); } /** - * Set the system configuration record. - * @param systemRecord the record to set + * Get the system configuration record. + * @return the system configuration record */ - public void setSystemRecord(final SystemConfiguration systemRecord) { - this.systemRecord = systemRecord; + public SystemConfiguration getLastSystemRecord() { + return systemConfigurationRecords.isEmpty() ? null : systemConfigurationRecords.get(systemConfigurationRecords.size() - 1); } /** @@ -65,105 +68,305 @@ public void setSystemRecord(final SystemConfiguration systemRecord) { * @return the laser configuration record */ public LaserConfiguration getLaserRecord() { - return laserRecord; + return getLaserRecord(getSystemRecord()); } /** - * Set the laser configuration record. - * @param laserRecord the record to set + * Get the detector configuration record. + * @return the detector configuration record */ - public void setLaserRecord(final LaserConfiguration laserRecord) { - this.laserRecord = laserRecord; + public DetectorConfiguration getDetectorRecord() { + return getDetectorRecord(getSystemRecord()); } /** - * Get the detector configuration record. - * @return the detector configuration record + * Get the timing system configuration record. + * @return the timing system configuration record */ - public DetectorConfiguration getDetectorRecord() { - return detectorRecord; + public TimingSystemConfiguration getTimingRecord() { + return getTimingRecord(getSystemRecord()); } /** - * Set the detector configuration record. - * @param detectorRecord the record to set + * Get the transponder configuration record. + * @return the transponder configuration record */ - public void setDetectorRecord(final DetectorConfiguration detectorRecord) { - this.detectorRecord = detectorRecord; + public TransponderConfiguration getTransponderRecord() { + return getTransponderRecord(getSystemRecord()); } + /** - * Get the timing system configuration record. - * @return the timing system configuration record + * Get the software configuration record. + * @return the software configuration record */ - public TimingSystemConfiguration getTimingRecord() { - return timingRecord; + public SoftwareConfiguration getSoftwareRecord() { + return getSoftwareRecord(getSystemRecord()); + } + + /** + * Get the meteorological record. + * @return the meteorological record + */ + public MeteorologicalConfiguration getMeteorologicalRecord() { + return getMeteorologicalRecord(getSystemRecord()); } /** - * Set the timing system configuration record. - * @param timingRecord the record to set + * Add a configuration record, such as SystemConfiguation, LaserConfiguration, DetectorConfiguration, etc. + * @param config the configuration record + * @since 12.0 */ - public void setTimingRecord(final TimingSystemConfiguration timingRecord) { - this.timingRecord = timingRecord; + public void addConfigurationRecord(final BaseConfiguration config) { + if (config == null) { + return; + } + + mapConfigurationRecords.put(config.getConfigurationId(), config); + + if (config instanceof SystemConfiguration) { + // Add to the list systemConfigurationRecords if it is a SystemConfiguration + systemConfigurationRecords.add((SystemConfiguration) config); + } } /** - * Get the transponder configuration record. - * @return the transponder configuration record + * Get the configuration records map. + * @return the configuration records map + * @since 12.0 */ - public TransponderConfiguration getTransponderRecord() { - return transponderRecord; + public Map getConfigurationRecordMap() { + return Collections.unmodifiableMap(mapConfigurationRecords); } /** - * Set the transponder configuration record. - * @param transponderRecord the record to set + * Get configuration record corresponding to the configId. + * @param configId the id of configuration + * @return the configuration with configId, or null + * @since 12.0 */ - public void setTransponderRecord(final TransponderConfiguration transponderRecord) { - this.transponderRecord = transponderRecord; + public BaseConfiguration getConfigurationRecord(final String configId) { + return mapConfigurationRecords.get(configId); } /** - * Get the software configuration record. - * @return the software configuration record + * Get a set of configuration ids. + * @return an unmodifiable set of configuration ids + * @since 12.0 */ - public SoftwareConfiguration getSoftwareRecord() { - return softwareRecord; + public Set getSystemConfigurationIds() { + return Collections.unmodifiableSet(mapConfigurationRecords.keySet()); } /** - * Set the software configuration record. - * @param softwareRecord the record to set + * Get a list of system configurations. + * @return an unmodifiable list of system configurations + * @since 12.0 */ - public void setSoftwareRecord(final SoftwareConfiguration softwareRecord) { - this.softwareRecord = softwareRecord; + public List getSystemConfigurationRecords() { + return Collections.unmodifiableList(systemConfigurationRecords); } /** - * Get the meteorological record. - * @return the meteorological record + * Get configuration record related to systemRecord and the given class. + * @param systemRecord system configuration record + * @param c the class, such as LaserConfiguration, DetectorConfiguration, TimingSystemConfiguration, etc + * @return the configuration record + * @since 12.0 */ - public MeteorologicalConfiguration getMeteorologicalRecord() { - return meteorologicalRecord; + private BaseConfiguration getRecord(final SystemConfiguration systemRecord, + final Class c) { + BaseConfiguration config; + for (final String configId : systemRecord.getComponents()) { + config = getConfigurationRecord(configId); + if (config != null && config.getClass() == c) { + return config; + } + } + + return null; + } + + /** + * Get system configuration record. If configId is null, the default(first system configuration record) is returned. + * @param configId system configuration id, it can be null. + * @return the system configuration record + * @since 12.0 + */ + public SystemConfiguration getSystemRecord(final String configId) { + if (configId == null) { + // default + return getSystemRecord(); + } + + final BaseConfiguration config = mapConfigurationRecords.get(configId); + return config == null ? null : (SystemConfiguration) config; + } + + /** + * Get laser configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the laser configuration record related the the systemRecord + * @since 12.0 + */ + public LaserConfiguration getLaserRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, LaserConfiguration.class); + return config == null ? null : (LaserConfiguration) config; } /** - * Set the meteorological record. - * @param meteorologicalRecord the meteorological record to set + * Get detector configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the detector configuration record related the the systemRecord + * @since 12.0 */ - public void setMeteorologicalRecord(final MeteorologicalConfiguration meteorologicalRecord) { - this.meteorologicalRecord = meteorologicalRecord; + public DetectorConfiguration getDetectorRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, DetectorConfiguration.class); + return config == null ? null : (DetectorConfiguration) config; + } + + /** + * Get timing system configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the timing system configuration record related the the systemRecord + * @since 12.0 + */ + public TimingSystemConfiguration getTimingRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, TimingSystemConfiguration.class); + return config == null ? null : (TimingSystemConfiguration) config; + } + + /** + * Get transponder configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the transponder configuration record related the the systemRecord + * @since 12.0 + */ + public TransponderConfiguration getTransponderRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, TransponderConfiguration.class); + return config == null ? null : (TransponderConfiguration) config; + } + + /** + * Get software configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the software configuration record related the the systemRecord + * @since 12.0 + */ + public SoftwareConfiguration getSoftwareRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, SoftwareConfiguration.class); + return config == null ? null : (SoftwareConfiguration) config; + } + + /** + * Get meteorological configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the meteorological configuration record related the the systemRecord + * @since 12.0 + */ + public MeteorologicalConfiguration getMeteorologicalRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, MeteorologicalConfiguration.class); + return config == null ? null : (MeteorologicalConfiguration) config; + } + + /** + * Get calibration target configuration record related to the systemRecord. + * @param systemRecord the system configuration + * @return the calibration target configuration record related the the systemRecord + * @since 12.0 + */ + public CalibrationTargetConfiguration getCalibrationTargetRecord(final SystemConfiguration systemRecord) { + final BaseConfiguration config = getRecord(systemRecord, CalibrationTargetConfiguration.class); + return config == null ? null : (CalibrationTargetConfiguration) config; + } + + /** + * Get the calibration target configuration record. + * @return the calibration target configuration record + * @since 12.0 + */ + public CalibrationTargetConfiguration getCalibrationTargetRecord() { + return getCalibrationTargetRecord(getSystemRecord()); + } + + /** + * Base class for configuration record. + * @since 12.0 + */ + public abstract static class BaseConfiguration { + + /** Configuration ID. */ + private String configurationId; + + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public BaseConfiguration() { + // nothing to do + } + + /** + * Get the configuration ID. + * @return the configuration ID + */ + public String getConfigurationId() { + return configurationId; + } + + /** + * Set the configuration ID. + * @param configurationId the configuration ID to set + */ + public void setConfigurationId(final String configurationId) { + this.configurationId = configurationId; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(final Object record) { + if (record == null) { + return false; + } + + if (record == this) { + return true; + } + + return toString().equals(record.toString()); + + } + + /** + * Get a string representation of the instance in the CRD format. + * @return a string representation of the instance, in the CRD format. + * @since 12.0 + */ + public abstract String toCrdString(); } /** Container for system configuration record. */ - public static class SystemConfiguration { + public static class SystemConfiguration extends BaseConfiguration { /** Transmit Wavelength [m]. */ private double wavelength; - /** System configuration ID. */ - private String systemId; + /** List of components. **/ + private List components; + + /** + * Constructor. + */ + public SystemConfiguration() { + this.components = new ArrayList<>(); + } /** * Get the transmit wavelength. @@ -186,7 +389,7 @@ public void setWavelength(final double wavelength) { * @return the system configuration ID */ public String getSystemId() { - return systemId; + return getConfigurationId(); } /** @@ -194,16 +397,47 @@ public String getSystemId() { * @param systemId the system configuration ID to set */ public void setSystemId(final String systemId) { - this.systemId = systemId; + setConfigurationId(systemId); + } + + /** + * Get the components (config ids) for system configuration. + * @return an unmodifiable list of components + * @since 12.0 + */ + public List getComponents() { + return Collections.unmodifiableList(components); } + /** + * Set the components (config ids) for system configuration. + * @param components the components (config ids) + * @since 12.0 + */ + public void setComponents(final String[] components) { + this.components = Arrays.asList(components); + } + + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C0 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + final StringBuilder sb = new StringBuilder(); + sb.append(String.format("%10.3f %s", wavelength * 1e9, getConfigurationId())); + for (final String comp : components) { + sb.append(String.format(" %s", comp)); + } + return sb.toString().replace(',', '.'); + } } /** Container for laser configuration record. */ - public static class LaserConfiguration { - - /** Laser configuration ID. */ - private String laserId; + public static class LaserConfiguration extends BaseConfiguration { /** Laser Type. */ private String laserType; @@ -217,7 +451,7 @@ public static class LaserConfiguration { /** Pulse Energy [mJ]. */ private double pulseEnergy; - /** Pulse Width. */ + /** Pulse Width [ps]. */ private double pulseWidth; /** Bean divergence [arcsec]. */ @@ -226,13 +460,23 @@ public static class LaserConfiguration { /** Number of pulses in outgoing semi-train. */ private int pulseInOutgoingSemiTrain; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public LaserConfiguration() { + // nothing to do + } /** * Get the laser configuration ID. * @return the laser configuration ID */ public String getLaserId() { - return laserId; + return getConfigurationId(); } /** @@ -240,7 +484,7 @@ public String getLaserId() { * @param laserId the laser configuration ID to set */ public void setLaserId(final String laserId) { - this.laserId = laserId; + setConfigurationId(laserId); } /** @@ -317,7 +561,7 @@ public double getPulseWidth() { /** * Set the pulse width. - * @param pulseWidth the pulse width to set + * @param pulseWidth the pulse width to set, ps */ public void setPulseWidth(final double pulseWidth) { this.pulseWidth = pulseWidth; @@ -355,13 +599,28 @@ public void setPulseInOutgoingSemiTrain(final int pulseInOutgoingSemiTrain) { this.pulseInOutgoingSemiTrain = pulseInOutgoingSemiTrain; } + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C1 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + // primaryWavelength: m --> nm + final String str = String.format( + "%s %s %.2f %.2f %.2f %.1f %.2f %d", getConfigurationId(), + laserType, primaryWavelength * 1e9, nominalFireRate, + pulseEnergy, pulseWidth, beamDivergence, + pulseInOutgoingSemiTrain); + return CRD.handleNaN(str).replace(',', '.'); + } + } /** Container for detector configuration record. */ - public static class DetectorConfiguration { - - /** Detector configuration ID. */ - private String detectorId; + public static class DetectorConfiguration extends BaseConfiguration { /** Detector Type. */ private String detectorType; @@ -405,12 +664,23 @@ public static class DetectorConfiguration { /** Amplifier In Use. */ private String amplifierInUse; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public DetectorConfiguration() { + // nothing to do + } + /** * Get the detector configuration ID. * @return the detector configuration ID */ public String getDetectorId() { - return detectorId; + return getConfigurationId(); } /** @@ -418,7 +688,7 @@ public String getDetectorId() { * @param detectorId the detector configuration ID to set */ public void setDetectorId(final String detectorId) { - this.detectorId = detectorId; + setConfigurationId(detectorId); } /** @@ -466,7 +736,12 @@ public double getQuantumEfficiency() { * @param quantumEfficiency the efficiency to set in percents */ public void setQuantumEfficiency(final double quantumEfficiency) { - this.quantumEfficiency = quantumEfficiency; + // NOTE: The quantumEfficiency may be -1.0, which means 'not available' + if (quantumEfficiency == -1.0) { + this.quantumEfficiency = Double.NaN; + } else { + this.quantumEfficiency = quantumEfficiency; + } } /** @@ -482,7 +757,12 @@ public double getAppliedVoltage() { * @param appliedVoltage the applied voltage to set in Volts */ public void setAppliedVoltage(final double appliedVoltage) { - this.appliedVoltage = appliedVoltage; + // NOTE: The quantumEfficiency may be -1.0, which means 'not available' or 'not applicable' + if (appliedVoltage == -1.0) { + this.appliedVoltage = Double.NaN; + } else { + this.appliedVoltage = appliedVoltage; + } } /** @@ -498,7 +778,12 @@ public double getDarkCount() { * @param darkCount the dark count to set in Hz */ public void setDarkCount(final double darkCount) { - this.darkCount = darkCount; + // NOTE: The quantumEfficiency may be -1.0, which means 'not available' + if (darkCount == -1.0e3) { // -1=na, kHz --> Hz + this.darkCount = Double.NaN; + } else { + this.darkCount = darkCount; + } } /** @@ -530,7 +815,12 @@ public double getOutputPulseWidth() { * @param outputPulseWidth the output pulse width to set in ps */ public void setOutputPulseWidth(final double outputPulseWidth) { - this.outputPulseWidth = outputPulseWidth; + // NOTE: The quantumEfficiency may be -1.0, which means 'not available' or 'not applicable' + if (outputPulseWidth == -1.0) { + this.outputPulseWidth = Double.NaN; + } else { + this.outputPulseWidth = outputPulseWidth; + } } /** @@ -645,13 +935,32 @@ public void setAmplifierInUse(final String amplifierInUse) { this.amplifierInUse = amplifierInUse; } + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C2 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + // applicableWavelength, spectralFilter: m --> nm + // darkCount, amplifierBandwidth: Hz --> kHz + final String str = String.format( + "%s %s %.3f %.2f %.1f %.1f %s %.1f %.2f %.1f %.1f %s %.1f %.1f %s", + getConfigurationId(), detectorType, + applicableWavelength * 1e9, quantumEfficiency, + appliedVoltage, darkCount * 1e-3, outputPulseType, + outputPulseWidth, spectralFilter * 1e9, + transmissionOfSpectralFilter, spatialFilter, + externalSignalProcessing, amplifierGain, + amplifierBandwidth * 1e-3, amplifierInUse); + return CRD.handleNaN(str).replace(',', '.'); + } } /** Container for timing system configuration record. */ - public static class TimingSystemConfiguration { - - /** Local timing system configuration ID. */ - private String localTimingId; + public static class TimingSystemConfiguration extends BaseConfiguration { /** Time Source. */ private String timeSource; @@ -668,6 +977,17 @@ public static class TimingSystemConfiguration { /** Epoch delay correction [s]. */ private double epochDelayCorrection; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public TimingSystemConfiguration() { + // nothing to do + } + /** * Get the time source. * @return the time source @@ -681,7 +1001,7 @@ public String getTimeSource() { * @return the local timing system configuration ID */ public String getLocalTimingId() { - return localTimingId; + return getConfigurationId(); } /** @@ -689,7 +1009,7 @@ public String getLocalTimingId() { * @param localTimingId the local timing system configuration ID to set */ public void setLocalTimingId(final String localTimingId) { - this.localTimingId = localTimingId; + setConfigurationId(localTimingId); } /** @@ -764,13 +1084,25 @@ public void setEpochDelayCorrection(final double epochDelayCorrection) { this.epochDelayCorrection = epochDelayCorrection; } + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C3 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + // epochDelayCorrection: s --> us + final String str = String.format("%s %s %s %s %s %.1f", + getConfigurationId(), timeSource, frequencySource, timer, + timerSerialNumber, epochDelayCorrection * 1e6); + return CRD.handleNaN(str).replace(',', '.'); + } } /** Container for transponder configuration record. */ - public static class TransponderConfiguration { - - /** Transponder configuration ID. */ - private String transponderId; + public static class TransponderConfiguration extends BaseConfiguration { /** Estimated Station UTC offset [s]. */ private double stationUTCOffset; @@ -796,12 +1128,23 @@ public static class TransponderConfiguration { /** Spacecraft time simplified flag. */ private boolean isSpacecraftTimeSimplified; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public TransponderConfiguration() { + // nothing to do + } + /** * Get the transponder configuration ID. * @return the transponder configuration ID */ public String getTransponderId() { - return transponderId; + return getConfigurationId(); } /** @@ -809,7 +1152,7 @@ public String getTransponderId() { * @param transponderId the transponder configuration ID to set */ public void setTransponderId(final String transponderId) { - this.transponderId = transponderId; + setConfigurationId(transponderId); } /** @@ -940,13 +1283,34 @@ public void setIsSpacecraftTimeSimplified(final boolean isSpacecraftTimeSimplifi this.isSpacecraftTimeSimplified = isSpacecraftTimeSimplified; } + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C4 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + // stationUTCOffset, transpUTCOffset: s --> ns + final String str = String.format( + "%s %.3f %.2f %.3f %.2f %.12f %d %d %d", + getConfigurationId(), + stationUTCOffset * 1e9, stationOscDrift, + transpUTCOffset * 1e9, transpOscDrift, + transpClkRefTime, + stationClockAndDriftApplied, spacecraftClockAndDriftApplied, + isSpacecraftTimeSimplified ? 1 : 0); + return CRD.handleNaN(str).replace(',', '.'); + } + } /** Container for software configuration record. */ - public static class SoftwareConfiguration { + public static class SoftwareConfiguration extends BaseConfiguration { - /** Software configuration ID. */ - private String softwareId; + /** Pattern of "[\\s+\\[\\]]". */ + private static final Pattern PATTERN_WHITESPACE_OR_SQUAREBRACKET = Pattern.compile("[\\s+\\[\\]]"); /** Tracking software in measurement path. */ private String[] trackingSoftwares; @@ -960,12 +1324,23 @@ public static class SoftwareConfiguration { /** Processing software version(s). */ private String[] processingSoftwareVersions; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public SoftwareConfiguration() { + // nothing to do + } + /** * Get the software configuration ID. * @return the software configuration ID. */ public String getSoftwareId() { - return softwareId; + return getConfigurationId(); } /** @@ -973,7 +1348,7 @@ public String getSoftwareId() { * @param softwareId the software configuration ID */ public void setSoftwareId(final String softwareId) { - this.softwareId = softwareId; + setConfigurationId(softwareId); } /** @@ -1040,13 +1415,34 @@ public void setProcessingSoftwareVersions(final String[] processingSoftwareVersi this.processingSoftwareVersions = processingSoftwareVersions.clone(); } + private static String formatArray(final String[] arr) { + // comma delimited + // "[Monitor, Sattrk]" ==> "Monitor,Sattrk" + // "[conpro, crd_cal, PoissonCRD, gnp]" ==> "conpro,crd_cal,PoissonCRD,gnp" + final String s = Arrays.toString(arr); + return PATTERN_WHITESPACE_OR_SQUAREBRACKET.matcher(s).replaceAll(""); + } + + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C5 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + return String.format("%s %s %s %s %s", getConfigurationId(), + formatArray(trackingSoftwares), + formatArray(trackingSoftwareVersions), + formatArray(processingSoftwares), + formatArray(processingSoftwareVersions)); + } + } /** Container for meteorological configuration record. */ - public static class MeteorologicalConfiguration { - - /** Meteorological configuration ID. */ - private String meteorologicalId; + public static class MeteorologicalConfiguration extends BaseConfiguration { /** Pressure Sensor Manufacturer. */ private String pressSensorManufacturer; @@ -1075,12 +1471,23 @@ public static class MeteorologicalConfiguration { /** Humidity Sensor Serial Number. */ private String humiSensorSerialNumber; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public MeteorologicalConfiguration() { + // nothing to do + } + /** * Get the meteorological configuration ID. * @return the meteorological configuration ID */ public String getMeteorologicalId() { - return meteorologicalId; + return getConfigurationId(); } /** @@ -1088,7 +1495,7 @@ public String getMeteorologicalId() { * @param meteorologicalId the meteorological configuration ID to set */ public void setMeteorologicalId(final String meteorologicalId) { - this.meteorologicalId = meteorologicalId; + setConfigurationId(meteorologicalId); } /** @@ -1235,6 +1642,194 @@ public void setHumiSensorSerialNumber(final String humiSensorSerialNumber) { this.humiSensorSerialNumber = humiSensorSerialNumber; } + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C6 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + return String.format("%s %s %s %s %s %s %s %s %s %s", + getConfigurationId(), pressSensorManufacturer, + pressSensorModel, pressSensorSerialNumber, + tempSensorManufacturer, tempSensorModel, + tempSensorSerialNumber, humiSensorManufacturer, + humiSensorModel, humiSensorSerialNumber); + } } + /** + * Container for calibration target configuration record. + * @since 12.0 + */ + public static class CalibrationTargetConfiguration extends BaseConfiguration { + + /** Target name or ID. */ + private String targetName; + + /** Surveyed target distance. */ + private double surveyedTargetDistance; + + /** Survey error. */ + private double surveyError; + + /** Sum of all constant delays (m, one way). */ + private double sumOfAllConstantDelays; + + /** Pulse Energy [mJ]. */ + private double pulseEnergy; + + /** Processing software name. */ + private String processingSoftwareName; + + /** Processing software version. */ + private String processingSoftwareVersion; + + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public CalibrationTargetConfiguration() { + // nothing to do + } + + /** + * Get the target name or ID. + * @return the target name or ID + */ + public String getTargetName() { + return targetName; + } + + /** + * Set the target name or ID. + * @param targetName target name or ID to set + */ + public void setTargetName(final String targetName) { + this.targetName = targetName; + } + + /** + * Get the surveyed target distance. + * @return the surveyed target distance in meters + */ + public double getSurveyedTargetDistance() { + return surveyedTargetDistance; + } + + /** + * Set the surveyed target distance. + * @param surveyedTargetDistance the surveyed target distance to set, in meters + */ + public void setSurveyedTargetDistance(final double surveyedTargetDistance) { + this.surveyedTargetDistance = surveyedTargetDistance; + } + + /** + * Get the survey error. + * @return the survey error in meters + */ + public double getSurveyError() { + return surveyError; + } + + /** + * Set the survey error. + * @param surveyError the survey error to set, in meters + */ + public void setSurveyError(final double surveyError) { + this.surveyError = surveyError; + } + + /** + * Get the sum of all constant delays (electronic, geometric, optical) that + * are not included in the time of flight measurements or time- variant + * or point angle-variant delays in the “42” record below (m, one way). + * @return the sum of all constant delays + */ + public double getSumOfAllConstantDelays() { + return sumOfAllConstantDelays; + } + + /** + * Set the sum of all constant delays (electronic, geometric, optical) that + * are not included in the time of flight measurements or time- variant + * or point angle-variant delays in the “42” record below (m, one way). + * @param sumOfAllConstantDelays the sum of all constant delays + */ + public void setSumOfAllConstantDelays(final double sumOfAllConstantDelays) { + this.sumOfAllConstantDelays = sumOfAllConstantDelays; + } + + /** + * Get the pulse energy. + * @return the pulse energy in mJ + */ + public double getPulseEnergy() { + return pulseEnergy; + } + + /** + * Set the pulse energy. + * @param pulseEnergy the pulse energy to set, in mJ + */ + public void setPulseEnergy(final double pulseEnergy) { + this.pulseEnergy = pulseEnergy; + } + + /** + * Get the processing software name. + * @return the processing software name + */ + public String getProcessingSoftwareName() { + return processingSoftwareName; + } + + /** + * Set the processing software name. + * @param processingSoftwareName the processing software name to set + */ + public void setProcessingSoftwareName(final String processingSoftwareName) { + this.processingSoftwareName = processingSoftwareName; + } + + /** + * Get the processing software version. + * @return the processing software version + */ + public String getProcessingSoftwareVersion() { + return processingSoftwareVersion; + } + + /** + * Set the processing software version. + * @param processingSoftwareVersion the processing software version to set + */ + public void setProcessingSoftwareVersion(final String processingSoftwareVersion) { + this.processingSoftwareVersion = processingSoftwareVersion; + } + + /** {@inheritDoc} */ + @Override + public String toCrdString() { + return String.format("C7 0 %s", toString()); + } + + @Override + public String toString() { + // CRD suggested format, excluding the record type + // surveyError: m --> mm + final String str = String.format("%s %s %.5f %.2f %.4f %.2f %s %s", + getConfigurationId(), targetName, surveyedTargetDistance, + surveyError * 1e3, sumOfAllConstantDelays, pulseEnergy, + processingSoftwareName, processingSoftwareVersion); + return CRD.handleNaN(str).replace(',', '.'); + } + + } } diff --git a/src/main/java/org/orekit/files/ilrs/CRDHeader.java b/src/main/java/org/orekit/files/ilrs/CRDHeader.java index 2fce6c49f1..e1ad1c65c8 100644 --- a/src/main/java/org/orekit/files/ilrs/CRDHeader.java +++ b/src/main/java/org/orekit/files/ilrs/CRDHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,17 +18,32 @@ import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; +import org.orekit.annotation.DefaultDataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.time.DateComponents; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; /** * Container for Consolidated laser ranging Data Format (CDR) header. * @author Bryan Cazabonne + * @author Rongwang Li * @since 10.3 */ public class CRDHeader extends ILRSHeader { + /** String delimiter regex of datetime. */ + private static final String DATETIME_DELIMITER_REGEX = "[-:T]"; + + /** Space. */ + private static final String SPACE = " "; + + /** Pattern of delimiter of datetime. */ + public static final Pattern PATTERN_DATETIME_DELIMITER_REGEX = Pattern.compile(DATETIME_DELIMITER_REGEX); + /** Station name from official list. */ private String stationName; @@ -93,6 +108,17 @@ public class CRDHeader extends ILRSHeader { /** Prediction provider (CPF provider in H1 record or TLE source). */ private String predictionProvider; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public CRDHeader() { + // nothing to do + } + /** * Get the station name from official list. * @return the station name from official list @@ -425,6 +451,74 @@ public void setPredictionProvider(final String predictionProvider) { this.predictionProvider = predictionProvider; } + /** + * Get a string representation of the H1 in the CRD format. + * @return a string representation of the H1, in the CRD format. + * @since 12.0 + */ + public String getH1CrdString() { + final DateComponents dc = getProductionEpoch(); + return String.format("H1 %3s %2d %04d %02d %02d %02d", getFormat(), + getVersion(), dc.getYear(), dc.getMonth(), dc.getDay(), + getProductionHour()); + } + + /** + * Get a string representation of the H2 in the CRD format. + * @return a string representation of the H2, in the CRD format. + * @since 12.0 + */ + public String getH2CrdString() { + return String.format("H2 %s %4d %02d %02d %2d %s", stationName, + systemIdentifier, systemNumber, systemOccupancy, + epochIdentifier, stationNetword); + } + + /** + * Get a string representation of the H3 in the CRD format. + * @return a string representation of the H3, in the CRD format. + * @since 12.0 + */ + public String getH3CrdString() { + final int targetLocation = getTargetLocation(); + return String.format("H3 %s %7s %4s %5s %1d %1d %2s", getName(), + getIlrsSatelliteId(), getSic(), getNoradId(), + getSpacecraftEpochTimeScale(), getTargetClass(), + CRD.formatIntegerOrNaN(targetLocation, -1)); + } + + /** + * Get a string representation of the H4 in the CRD format. + * @return a string representation of the H4, in the CRD format. + * @since 12.0 + */ + @DefaultDataContext + public String getH4CrdString() { + // "2006-11-13T15:23:52" -- > "2006 11 13 15 23 52" + final TimeScale utc = TimeScalesFactory.getUTC(); + final String startEpoch = getStartEpoch().toStringWithoutUtcOffset(utc, 0); + final String endEpoch = getEndEpoch().toStringWithoutUtcOffset(utc, 0); + return String.format("H4 %2d %s %s %d %d %d %d %d %d %d %d", getDataType(), + PATTERN_DATETIME_DELIMITER_REGEX.matcher(startEpoch).replaceAll(SPACE), + PATTERN_DATETIME_DELIMITER_REGEX.matcher(endEpoch).replaceAll(SPACE), + dataReleaseFlag, isTroposphericRefractionApplied ? 1 : 0, + isCenterOfMassCorrectionApplied ? 1 : 0, + isReceiveAmplitudeCorrectionApplied ? 1 : 0, + isStationSystemDelayApplied ? 1 : 0, + isTransponderDelayApplied ? 1 : 0, rangeType.getIndicator(), + qualityIndicator); + } + + /** + * Get a string representation of the H5 in the CRD format. + * @return a string representation of the H5, in the CRD format. + * @since 12.0 + */ + public String getH5CrdString() { + return String.format("H5 %2d %02d %s %3s %5d", getPredictionType(), getYearOfCentury(), + getDateAndTime(), getPredictionProvider(), getSequenceNumber()); + } + /** Range type for SLR data. */ public enum RangeType { @@ -486,4 +580,61 @@ public static RangeType getRangeType(final int id) { } + /** Data type for CRD data. + * @since 12.0 + */ + public enum DataType { + + /** Full rate. */ + FULL_RATE(0), + + /** Normal point. */ + NORMAL_POINT(1), + + /** Sampled engineering. */ + SAMPLED_ENGIEERING(2); + + /** Codes map. */ + private static final Map CODES_MAP = new HashMap<>(); + static { + for (final DataType type : values()) { + CODES_MAP.put(type.getIndicator(), type); + } + } + + /** data type indicator. */ + private final int indicator; + + /** + * Constructor. + * @param indicator data type indicator + */ + DataType(final int indicator) { + this.indicator = indicator; + } + + /** + * Get the data type indicator. + * @return the data type indicator + */ + public int getIndicator() { + return indicator; + } + + /** + * Get the data type for the given indicator. + * @param id indicator + * @return the data type corresponding to the indicator + */ + public static DataType getDataType(final int id) { + final DataType type = CODES_MAP.get(id); + if (type == null) { + // Invalid value. An exception is thrown + throw new RuntimeException(String.format("Invalid data type indicator {0} in CRD file header", id)); + } + return type; + } + + } + } diff --git a/src/main/java/org/orekit/files/ilrs/CRDParser.java b/src/main/java/org/orekit/files/ilrs/CRDParser.java index cc09d614bb..d360b2f7a4 100644 --- a/src/main/java/org/orekit/files/ilrs/CRDParser.java +++ b/src/main/java/org/orekit/files/ilrs/CRDParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,10 +18,12 @@ import java.io.BufferedReader; import java.io.IOException; -import java.util.Optional; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.regex.Pattern; -import java.util.stream.Stream; +import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; @@ -30,8 +32,15 @@ import org.orekit.errors.OrekitMessages; import org.orekit.files.ilrs.CRD.AnglesMeasurement; import org.orekit.files.ilrs.CRD.CRDDataBlock; +import org.orekit.files.ilrs.CRD.Calibration; +import org.orekit.files.ilrs.CRD.CalibrationDetail; +import org.orekit.files.ilrs.CRD.FrRangeMeasurement; import org.orekit.files.ilrs.CRD.MeteorologicalMeasurement; +import org.orekit.files.ilrs.CRD.NptRangeMeasurement; import org.orekit.files.ilrs.CRD.RangeMeasurement; +import org.orekit.files.ilrs.CRD.RangeSupplement; +import org.orekit.files.ilrs.CRD.SessionStatistics; +import org.orekit.files.ilrs.CRDConfiguration.CalibrationTargetConfiguration; import org.orekit.files.ilrs.CRDConfiguration.DetectorConfiguration; import org.orekit.files.ilrs.CRDConfiguration.LaserConfiguration; import org.orekit.files.ilrs.CRDConfiguration.MeteorologicalConfiguration; @@ -55,8 +64,9 @@ * Note: Not all the records are read by the parser. Only the most significants are parsed. * Contributions are welcome to support more fields in the format. * @see 1.0 file format - * @see 2.0 file format + * @see 2.0 file format * @author Bryan Cazabonne + * @author Rongwang Li * @since 10.3 */ public class CRDParser { @@ -73,6 +83,12 @@ public class CRDParser { /** Microseconds units. */ private static final Unit US = Unit.parse("µs"); + /** Nanoseconds units. */ + private static final Unit NS = Unit.parse("ns"); + + /** Picoseconds units. */ + private static final Unit PS = Unit.parse("ps"); + /** mbar to bar converter. */ private static final UnitsConverter MBAR_TO_BAR = new UnitsConverter(Unit.parse("mbar"), Unit.parse("bar")); @@ -85,6 +101,12 @@ public class CRDParser { /** Pattern for delimiting expressions with comma. */ private static final Pattern COMMA = Pattern.compile(","); + /** Identifier of comment record. */ + private static final String COMMENTS_IDENTIFIER = "00"; + + /** Pattern of " [-]?(na)". */ + private static final Pattern PATTERN_NA = Pattern.compile(" [-]?(na)"); + /** Time scale used to define epochs in CPF file. */ private final TimeScale timeScale; @@ -126,44 +148,64 @@ public CRD parse(final DataSource source) throws IOException { final ParseInfo pi = new ParseInfo(); int lineNumber = 0; - Stream crdParsers = Stream.of(LineParser.H1); + Iterable crdParsers = Collections.singleton(LineParser.H1); try (BufferedReader reader = new BufferedReader(source.getOpener().openReaderOnce())) { - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - ++lineNumber; - final String l = line; - final Optional selected = crdParsers.filter(p -> p.canHandle(l)).findFirst(); - if (selected.isPresent()) { - try { - selected.get().parse(line, pi); - } catch (StringIndexOutOfBoundsException | NumberFormatException e) { - throw new OrekitException(e, - OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, source.getName(), line); + nextLine: + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + ++lineNumber; + + if (line.startsWith(COMMENTS_IDENTIFIER)) { + // Comment is in the beginning of the file. + crdParsers = Arrays.asList(LineParser.COMMENTS); + } + + for (final LineParser candidate : crdParsers) { + if (candidate.canHandle(line)) { + try { + + // Note: since crd v2.01. + // The literal “na” is used instead of “-1” for fields that are not applicable or not avaiable. + // And there may be "-na". + // note: "analog" --> "aNaNlog" + line = PATTERN_NA.matcher(line).replaceAll(" " + CRD.STR_NAN); + + candidate.parse(line, pi); + if (pi.done) { + // Return file + return pi.file; + } + crdParsers = candidate.allowedNext(); + continue nextLine; + } catch (StringIndexOutOfBoundsException | NumberFormatException e) { + throw new OrekitException(e, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, source.getName(), line); + } + } } - crdParsers = selected.get().allowedNext(); - } - if (pi.done) { - // Return file - return pi.file; } - } - } - // We never reached the EOF marker - throw new OrekitException(OrekitMessages.CRD_UNEXPECTED_END_OF_FILE, lineNumber); + // We never reached the EOF marker + throw new OrekitException(OrekitMessages.CRD_UNEXPECTED_END_OF_FILE, lineNumber); + + } catch (IOException ioe) { + throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage()); + } } /** - * Computes if a day shift has happened comparing the current and past epoch, described by seconds in the day. - * This is useful as the data is sorted in the chronological order inside the file. - * - * @param lastSecOfDay - * @param secOfDay - * @return Boolean true if change in day. + * Make sure the epoch is 'right' by doing a day shift if it is required by comparing the current and session start epoch. + * According to the CRD document, the duration of a session must be less than one day. + * @param epoch current epoch + * @param startEpoch start epoch of session + * @return epoch with rollover is handled. */ - private static int checkRollover(final double lastSecOfDay, final double secOfDay) { - return (secOfDay > lastSecOfDay) ? 0 : 1; + private static AbsoluteDate checkRollover(final AbsoluteDate epoch, final AbsoluteDate startEpoch) { + // If the current epoch is before the start epoch of a session, the epoch should be shifted by 1 day. + // For METEO(20) data, the epoch may be a 'little' (10 hours?) before the session start epoch. + // And also for CALIB(40) and CALIB_DETAILS(41) + return epoch.durationFrom(startEpoch) < -36000 ? epoch.shiftedBy(Constants.JULIAN_DAY) : epoch; } /** Transient data used for parsing a CRD file. The data is kept in a @@ -191,15 +233,12 @@ private class ParseInfo { /** Time scale. */ private TimeScale timeScale; - /** Current data block start epoch. */ - private DateComponents startEpoch; + /** Current data block start epoch, DateComponents only. */ + private DateComponents startEpochDateComponents; /** End Of File reached indicator. */ private boolean done; - /** Last parsed range measurement. */ - private RangeMeasurement lastRange; - /** * Constructor. */ @@ -208,8 +247,7 @@ protected ParseInfo() { // Initialise default values this.done = false; this.version = 1; - this.startEpoch = DateComponents.J2000_EPOCH; - this.lastRange = null; + this.startEpochDateComponents = DateComponents.J2000_EPOCH; // Initialise empty object this.file = new CRD(); @@ -263,13 +301,13 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H2, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H2, COMMENTS); } }, - /** Format header. */ + /** Station header. */ H2("H2", "h2") { /** {@inheritDoc} */ @@ -293,19 +331,21 @@ public void parse(final String line, final ParseInfo pi) { // Station network if (pi.version == 2) { pi.header.setStationNetword(values[6]); + } else { + pi.header.setStationNetword(CRD.STR_VALUE_NOT_AVAILABLE); } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H3, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, C0, C1, C2, C3, C4, C5, C6, C7, COMMENTS); } }, - /** Format header. */ + /** Target header. */ H3("H3", "h3") { /** {@inheritDoc} */ @@ -329,20 +369,22 @@ public void parse(final String line, final ParseInfo pi) { // Target class and location (if needed) pi.header.setTargetClass(Integer.parseInt(values[6])); if (pi.version == 2) { - pi.header.setTargetLocation(Integer.parseInt(values[7])); + // na=unknown (for use when tracking a transponder using a Version 1 CPF) + // treated it as -1 + pi.header.setTargetLocation(readIntegerWithNaN(values[7], -1)); } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H4, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H4, C0, C1, C2, C3, C4, C5, C6, C7, COMMENTS); } }, - /** Format header. */ + /** Session (Pass/Pass segment) header. */ H4("H4", "h4") { /** {@inheritDoc} */ @@ -363,23 +405,33 @@ public void parse(final String line, final ParseInfo pi) { final int minuteS = Integer.parseInt(values[6]); final double secondS = Integer.parseInt(values[7]); - pi.startEpoch = new DateComponents(yearS, monthS, dayS); + pi.startEpochDateComponents = new DateComponents(yearS, monthS, dayS); pi.header.setStartEpoch(new AbsoluteDate(yearS, monthS, dayS, hourS, minuteS, secondS, pi.timeScale)); // End epoch - final int yearE = Integer.parseInt(values[8]); - final int monthE = Integer.parseInt(values[9]); - final int dayE = Integer.parseInt(values[10]); - final int hourE = Integer.parseInt(values[11]); - final int minuteE = Integer.parseInt(values[12]); - final double secondE = Integer.parseInt(values[13]); - - pi.header.setEndEpoch(new AbsoluteDate(yearE, monthE, dayE, - hourE, minuteE, secondE, - pi.timeScale)); + // since crd v2.01 + // Set the ending date and time fields to “na” if not available. + if (pi.version == 2 && values[8].equalsIgnoreCase("")) { + pi.header.setEndEpoch(null); + } else { + final int yearE = Integer.parseInt(values[8]); + final int monthE = Integer.parseInt(values[9]); + final int dayE = Integer.parseInt(values[10]); + final int hourE = Integer.parseInt(values[11]); + final int minuteE = Integer.parseInt(values[12]); + final double secondE = Integer.parseInt(values[13]); + + // fixed 2022-12-12 + // if yearE or monthE is -1. + if (monthE == -1) { + pi.header.setEndEpoch(null); + } else { + pi.header.setEndEpoch(new AbsoluteDate(yearE, monthE, dayE, hourE, minuteE, secondE, pi.timeScale)); + } + } // Data release pi.header.setDataReleaseFlag(Integer.parseInt(values[14])); @@ -401,13 +453,14 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H5, C0, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, + CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, - /** Format header. */ + /** Prediction header. */ H5("H5", "h5") { /** {@inheritDoc} */ @@ -428,8 +481,9 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C0, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, + CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -453,15 +507,19 @@ public void parse(final String line, final ParseInfo pi) { // System ID systemRecord.setSystemId(values[3]); - // Set the system configuration record - pi.configurationRecords.setSystemRecord(systemRecord); + // Components, A B C D E F G + systemRecord.setComponents(Arrays.copyOfRange(values, 4, values.length)); + + // Add the system configuration record + pi.configurationRecords.addConfigurationRecord(systemRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -488,17 +546,17 @@ public void parse(final String line, final ParseInfo pi) { laserRecord.setPulseEnergy(Double.parseDouble(values[6])); laserRecord.setPulseWidth(Double.parseDouble(values[7])); laserRecord.setBeamDivergence(Double.parseDouble(values[8])); - laserRecord.setPulseInOutgoingSemiTrain(Integer.parseInt(values[9])); + laserRecord.setPulseInOutgoingSemiTrain(readIntegerWithNaN(values[9], 1)); - // Set the laser configuration record - pi.configurationRecords.setLaserRecord(laserRecord); + // Add the laser configuration record + pi.configurationRecords.addConfigurationRecord(laserRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C2, C3, C4, C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(C2, C3, C4, C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); } }, @@ -535,17 +593,22 @@ public void parse(final String line, final ParseInfo pi) { detectorRecord.setAmplifierGain(Double.parseDouble(values[14])); detectorRecord.setAmplifierBandwidth(KHZ.toSI(Double.parseDouble(values[15]))); detectorRecord.setAmplifierInUse(values[16]); + } else { + detectorRecord.setAmplifierGain(Double.NaN); + detectorRecord.setAmplifierBandwidth(Double.NaN); + detectorRecord.setAmplifierInUse(CRD.STR_VALUE_NOT_AVAILABLE); } - // Set the detector configuration record - pi.configurationRecords.setDetectorRecord(detectorRecord); + // Add the detector configuration record + pi.configurationRecords.addConfigurationRecord(detectorRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C3, C4, C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -568,18 +631,25 @@ public void parse(final String line, final ParseInfo pi) { timingRecord.setTimeSource(values[3]); timingRecord.setFrequencySource(values[4]); timingRecord.setTimer(values[5]); - timingRecord.setTimerSerialNumber(values[6]); + final String timerSerialNumber = values[6]; + if (CRD.STR_NAN.equalsIgnoreCase(timerSerialNumber)) { + // The timer serial number may be "na" + timingRecord.setTimerSerialNumber(CRD.STR_VALUE_NOT_AVAILABLE); + } else { + timingRecord.setTimerSerialNumber(timerSerialNumber); + } timingRecord.setEpochDelayCorrection(US.toSI(Double.parseDouble(values[7]))); - // Set the timing system configuration record - pi.configurationRecords.setTimingRecord(timingRecord); + // Add the timing system configuration record + pi.configurationRecords.addConfigurationRecord(timingRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C4, C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -599,9 +669,9 @@ public void parse(final String line, final ParseInfo pi) { // Estimated offsets and drifts transponderRecord.setTransponderId(values[2]); - transponderRecord.setStationUTCOffset(NM.toSI(Double.parseDouble(values[3]))); + transponderRecord.setStationUTCOffset(NS.toSI(Double.parseDouble(values[3]))); transponderRecord.setStationOscDrift(Double.parseDouble(values[4])); - transponderRecord.setTranspUTCOffset(NM.toSI(Double.parseDouble(values[5]))); + transponderRecord.setTranspUTCOffset(NS.toSI(Double.parseDouble(values[5]))); transponderRecord.setTranspOscDrift(Double.parseDouble(values[6])); // Transponder clock reference time @@ -614,15 +684,16 @@ public void parse(final String line, final ParseInfo pi) { // Spacecraft time simplified transponderRecord.setIsSpacecraftTimeSimplified(readBoolean(values[10])); - // Set the transponder configuration record - pi.configurationRecords.setTransponderRecord(transponderRecord); + // Add the transponder configuration record + pi.configurationRecords.addConfigurationRecord(transponderRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -647,15 +718,16 @@ public void parse(final String line, final ParseInfo pi) { softwareRecord.setProcessingSoftwares(COMMA.split(values[5])); softwareRecord.setProcessingSoftwareVersions(COMMA.split(values[6])); - // Set the software configuration record - pi.configurationRecords.setSoftwareRecord(softwareRecord); + // Add the software configuration record + pi.configurationRecords.addConfigurationRecord(softwareRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -685,15 +757,16 @@ public void parse(final String line, final ParseInfo pi) { meteoRecord.setHumiSensorModel(values[10]); meteoRecord.setHumiSensorSerialNumber(values[11]); - // Set the meteorological configuration record - pi.configurationRecords.setMeteorologicalRecord(meteoRecord); + // Add the meteorological configuration record + pi.configurationRecords.addConfigurationRecord(meteoRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -704,13 +777,32 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // Not implemented yet + + // Initialise an empty calibration target configuration record + final CalibrationTargetConfiguration calibRecord = new CalibrationTargetConfiguration(); + + // Data contained in the line + final String[] values = SEPARATOR.split(line); + + // Fill values + calibRecord.setConfigurationId(values[2]); + calibRecord.setTargetName(values[3]); + calibRecord.setSurveyedTargetDistance(Double.parseDouble(values[4])); + calibRecord.setSurveyError(Double.parseDouble(values[5]) * 1e-3); // mm --> m + calibRecord.setSumOfAllConstantDelays(Double.parseDouble(values[6])); + calibRecord.setPulseEnergy(Double.parseDouble(values[7])); + calibRecord.setProcessingSoftwareName(values[8]); + calibRecord.setProcessingSoftwareVersion(values[9]); + + // Add the calibration target configuration record + pi.configurationRecords.addConfigurationRecord(calibRecord); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, + ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -718,12 +810,6 @@ public Stream allowedNext() { /** Range Record (Full rate, Sampled Engineering/Quicklook). */ TEN("10") { - /** Storage for the last epoch parsed to compare with the current.*/ - private double lastSecOfDay = 0; - - /** Shift in days due to the rollover.*/ - private int dayShift = 0; - /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { @@ -732,32 +818,35 @@ public void parse(final String line, final ParseInfo pi) { final String[] values = SEPARATOR.split(line); // Read data - final double secOfDay = Double.parseDouble(values[1]); - final double timeOfFlight = Double.parseDouble(values[2]); - final int epochEvent = Integer.parseInt(values[4]); + final double secOfDay = Double.parseDouble(values[1]); + final double timeOfFlight = Double.parseDouble(values[2]); + final String systemConfigId = values[3]; + final int epochEvent = Integer.parseInt(values[4]); + final int filterFlag = Integer.parseInt(values[5]); + final int detectorChannel = Integer.parseInt(values[6]); + final int stopNumber = Integer.parseInt(values[7]); + final int receiveAmplitude = readIntegerWithNaN(values[8], -1); + + int transmitAmplitude = -1; + if (pi.version == 2) { + transmitAmplitude = readIntegerWithNaN(values[9], -1); + } - // Check secOfDay for rollover - dayShift = dayShift + checkRollover(lastSecOfDay, secOfDay); // Initialise a new Range measurement - AbsoluteDate epoch = new AbsoluteDate(pi.startEpoch, new TimeComponents(secOfDay), pi.timeScale); - if (pi.lastRange != null) { - final double duration = epoch.durationFrom(pi.lastRange.getDate()); - if (duration < 0) { - epoch = epoch.shiftedBy(Constants.JULIAN_DAY); - } - } - final RangeMeasurement range = new RangeMeasurement(epoch, timeOfFlight, epochEvent); + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); + final RangeMeasurement range = new FrRangeMeasurement(epoch, timeOfFlight, epochEvent, systemConfigId, + filterFlag, detectorChannel, stopNumber, receiveAmplitude, transmitAmplitude); pi.dataBlock.addRangeData(range); - lastSecOfDay = secOfDay; - pi.lastRange = range; - } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, TEN, TWELVE, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, + COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -765,12 +854,6 @@ public Stream allowedNext() { /** Range Record (Normal point). */ ELEVEN("11") { - /** Storage for the last epoch parsed to compare with the current.*/ - private double lastSecOfDay = 0; - - /** Shift in days due to the rollover.*/ - private int dayShift = 0; - /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { @@ -779,33 +862,40 @@ public void parse(final String line, final ParseInfo pi) { final String[] values = SEPARATOR.split(line); // Read data - final double secOfDay = Double.parseDouble(values[1]); - final double timeOfFlight = Double.parseDouble(values[2]); - final int epochEvent = Integer.parseInt(values[4]); - final double snr = (pi.version == 2) ? Double.parseDouble(values[13]) : Double.NaN; + final double secOfDay = Double.parseDouble(values[1]); + final double timeOfFlight = Double.parseDouble(values[2]); + final String systemConfigId = values[3]; + final int epochEvent = Integer.parseInt(values[4]); + final double windowLength = Double.parseDouble(values[5]); + final int numberOfRawRanges = Integer.parseInt(values[6]); + final double binRms = PS.toSI(Double.parseDouble(values[7])); + final double binSkew = Double.parseDouble(values[8]); + final double binKurtosis = Double.parseDouble(values[9]); + final double binPeakMinusMean = PS.toSI(Double.parseDouble(values[10])); + final double returnRate = Double.parseDouble(values[11]); + final int detectorChannel = Integer.parseInt(values[12]); + + double snr = Double.NaN; + if (pi.version == 2) { + snr = Double.parseDouble(values[13]); + } - // Check secOfDay for rollover - dayShift = dayShift + checkRollover(lastSecOfDay, secOfDay); // Initialise a new Range measurement - AbsoluteDate epoch = new AbsoluteDate(pi.startEpoch, new TimeComponents(secOfDay), pi.timeScale); - if (pi.lastRange != null) { - final double duration = epoch.durationFrom(pi.lastRange.getDate()); - if (duration < 0) { - epoch = epoch.shiftedBy(Constants.JULIAN_DAY); - } - } - final RangeMeasurement range = new RangeMeasurement(epoch, timeOfFlight, epochEvent, snr); + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); + final RangeMeasurement range = new NptRangeMeasurement(epoch, timeOfFlight, epochEvent, snr, + systemConfigId, windowLength, numberOfRawRanges, binRms, binSkew, binKurtosis, binPeakMinusMean, + returnRate, detectorChannel); pi.dataBlock.addRangeData(range); - lastSecOfDay = secOfDay; - pi.lastRange = range; - } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, ELEVEN, TWELVE, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, + COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -816,13 +906,37 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // Not implemented yet + + // Data contained in the line + final String[] values = SEPARATOR.split(line); + + // Read data + final double secOfDay = Double.parseDouble(values[1]); + final String systemConfigId = values[2]; + final double troposphericRefractionCorr = PS.toSI(Double.parseDouble(values[3])); + final double centerOfMassCorr = Double.parseDouble(values[4]); + final double ndFilterValue = Double.parseDouble(values[5]); + final double timeBiasApplied = Double.parseDouble(values[6]); + + double rangeRate = Double.NaN; + if (pi.version == 2) { + rangeRate = Double.parseDouble(values[7]); + } + + // Initialise a new Range measurement + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); + final RangeSupplement rangeSup = new RangeSupplement(epoch, systemConfigId, troposphericRefractionCorr, + centerOfMassCorr, ndFilterValue, timeBiasApplied, rangeRate); + pi.dataBlock.addRangeSupplementData(rangeSup); + } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, TEN, ELEVEN, TWELVE, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); } }, @@ -830,12 +944,6 @@ public Stream allowedNext() { /** Meteorological record. */ METEO("20") { - /** Storage for the last epoch parsed to compare with the current.*/ - private double lastSecOfDay = 0; - - /** Shift in days due to the rollover.*/ - private int dayShift = 0; - /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { @@ -844,25 +952,27 @@ public void parse(final String line, final ParseInfo pi) { final String[] values = SEPARATOR.split(line); // Read data - final double secOfDay = Double.parseDouble(values[1]); - final double pressure = MBAR_TO_BAR.convert(Double.parseDouble(values[2])); - final double temperature = Double.parseDouble(values[3]); - final double humidity = Double.parseDouble(values[4]); + final double secOfDay = Double.parseDouble(values[1]); + final double pressure = MBAR_TO_BAR.convert(Double.parseDouble(values[2])); + final double temperature = Double.parseDouble(values[3]); + final double humidity = Double.parseDouble(values[4]); + final int originOfValues = Integer.parseInt(values[5]); - // Check secOfDay for rollover - dayShift = dayShift + checkRollover(lastSecOfDay, secOfDay); // Initialise a new Range measurement - final AbsoluteDate epoch = new AbsoluteDate(pi.startEpoch, new TimeComponents(secOfDay), pi.timeScale).shiftedBy(dayShift * 86400); - final MeteorologicalMeasurement meteo = new MeteorologicalMeasurement(epoch, pressure, - temperature, humidity); + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); + final MeteorologicalMeasurement meteo = new MeteorologicalMeasurement(epoch, pressure, temperature, + humidity, originOfValues); pi.dataBlock.addMeteoData(meteo); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, METEO_SUPP, TEN, ELEVEN, TWELVE, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -878,8 +988,9 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, METEO_SUPP, TEN, ELEVEN, TWELVE, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -887,12 +998,6 @@ public Stream allowedNext() { /** Pointing Angle Record. */ ANGLES("30") { - /** Storage for the last epoch parsed to compare with the current.*/ - private double lastSecOfDay = 0; - - /** Shift in days due to the rollover.*/ - private int dayShift = 0; - /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { @@ -913,14 +1018,15 @@ public void parse(final String line, final ParseInfo pi) { double azimuthRate = Double.NaN; double elevationRate = Double.NaN; if (pi.version == 2) { - azimuthRate = readDoubleWithNaN(values[7]); - elevationRate = readDoubleWithNaN(values[8]); + // degrees/second ==> rad/s + azimuthRate = FastMath.toRadians(Double.parseDouble(values[7])); + elevationRate = FastMath.toRadians(Double.parseDouble(values[8])); } - // Check secOfDay for rollover - dayShift = dayShift + checkRollover(lastSecOfDay, secOfDay); // Initialise a new angles measurement - final AbsoluteDate epoch = new AbsoluteDate(pi.startEpoch, new TimeComponents(secOfDay), pi.timeScale); + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); final AnglesMeasurement angles = new AnglesMeasurement(epoch, azmiuth, elevation, directionFlag, orginFlag, isRefractionCorrected, @@ -931,8 +1037,9 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, TEN, ELEVEN, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -943,13 +1050,53 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // Not implemented yet + + // Data contained in the line + final String[] values = SEPARATOR.split(line); + + // Read data + final double secOfDay = Double.parseDouble(values[1]); + final int typeOfData = Integer.parseInt(values[2]); + final String systemConfigId = values[3]; + final int numberOfPointsRecorded = readIntegerWithNaN(values[4], -1); + final int numberOfPointsUsed = readIntegerWithNaN(values[5], -1); + final double oneWayDistance = Double.parseDouble(values[6]); + final double systemDelay = PS.toSI(Double.parseDouble(values[7])); + final double delayShift = PS.toSI(Double.parseDouble(values[8])); + final double rms = PS.toSI(Double.parseDouble(values[9])); + final double skew = Double.parseDouble(values[10]); + final double kurtosis = Double.parseDouble(values[11]); + final double peakMinusMean = PS.toSI(Double.parseDouble(values[12])); + final int typeIndicator = Integer.parseInt(values[13]); + final int shiftTypeIndicator = Integer.parseInt(values[14]); + final int detectorChannel = Integer.parseInt(values[15]); + + // Check file version for additional data + int span = 0; + double returnRate = Double.NaN; + if (pi.version == 2) { + // fixed 20230321 + // the span may be "na" + span = readIntegerWithNaN(values[16], -1); + returnRate = Double.parseDouble(values[17]); + } + + // Initialise a new angles measurement + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); + final Calibration cal = new Calibration(epoch, typeOfData, systemConfigId, numberOfPointsRecorded, + numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew, kurtosis, peakMinusMean, + typeIndicator, shiftTypeIndicator, detectorChannel, span, returnRate); + pi.dataBlock.addCalibrationData(cal); + } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, CALIB, CALIB_DETAILS, CALIB_SHOT, TEN, ELEVEN, TWELVE, ANGLES, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -960,13 +1107,51 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // Not implemented yet + + // Data contained in the line + final String[] values = SEPARATOR.split(line); + + // Read data + final double secOfDay = Double.parseDouble(values[1]); + final int typeOfData = Integer.parseInt(values[2]); + final String systemConfigId = values[3]; + final int numberOfPointsRecorded = readIntegerWithNaN(values[4], -1); + final int numberOfPointsUsed = readIntegerWithNaN(values[5], -1); + final double oneWayDistance = Double.parseDouble(values[6]); + final double systemDelay = PS.toSI(Double.parseDouble(values[7])); + final double delayShift = PS.toSI(Double.parseDouble(values[8])); + final double rms = PS.toSI(Double.parseDouble(values[9])); + final double skew = Double.parseDouble(values[10]); + final double kurtosis = Double.parseDouble(values[11]); + final double peakMinusMean = PS.toSI(Double.parseDouble(values[12])); + final int typeIndicator = Integer.parseInt(values[13]); + final int shiftTypeIndicator = Integer.parseInt(values[14]); + final int detectorChannel = Integer.parseInt(values[15]); + + // Check file version for additional data + int span = 0; + double returnRate = Double.NaN; + if (pi.version == 2) { + span = Integer.parseInt(values[16]); + returnRate = Double.parseDouble(values[17]); + } + + // Initialise a new angles measurement + AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale); + // Check rollover + epoch = checkRollover(epoch, pi.header.getStartEpoch()); + final CalibrationDetail cal = new CalibrationDetail(epoch, typeOfData, systemConfigId, + numberOfPointsRecorded, numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew, + kurtosis, peakMinusMean, typeIndicator, shiftTypeIndicator, detectorChannel, span, returnRate); + pi.dataBlock.addCalibrationDetailData(cal); + } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, CALIB, CALIB_DETAILS, CALIB_SHOT, TEN, ELEVEN, TWELVE, ANGLES, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -982,8 +1167,9 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, CALIB, CALIB_DETAILS, CALIB_SHOT, TEN, ELEVEN, TWELVE, ANGLES, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -994,13 +1180,33 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // Not implemented yet + + // Data contained in the line + final String[] values = SEPARATOR.split(line); + + // Read data + final String systemConfigId = values[1]; + final double rms = PS.toSI(Double.parseDouble(values[2])); + final double skewness = Double.parseDouble(values[3]); + final double kurtosis = Double.parseDouble(values[4]); + // + // The peak minus mean may be "*" + // 50 shao 35.0 -0.509 2.221 ****** 0 + final double peakMinusMean = values[5].contains("*") ? Double.NaN : PS.toSI(Double.parseDouble(values[5])); + + final int dataQualityIndicator = Integer.parseInt(values[6]); + + final SessionStatistics stat = new SessionStatistics(systemConfigId, rms, skewness, kurtosis, peakMinusMean, + dataQualityIndicator); + pi.dataBlock.addSessionStatisticsData(stat); + } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, CALIB, CALIB_DETAILS, CALIB_SHOT, TEN, ELEVEN, TWELVE, ANGLES, STAT, COMPATIBILITY, H8, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, @@ -1016,30 +1222,49 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H8, METEO, CALIB, CALIB_DETAILS, CALIB_SHOT, TEN, ELEVEN, TWELVE, ANGLES, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } }, /** Comments. */ - COMMENTS("00") { + COMMENTS(COMMENTS_IDENTIFIER) { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { // Comment - final String comment = line.split(getFirstIdentifier())[1].trim(); + final String comment = line.substring(2).trim(); pi.file.getComments().add(comment); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H1, H2, H3, H4, H5, H8, H9, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, - METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H1, H2, H3, H4, H5, H8, H9, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, + METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM); + + } + + }, + + /** Custom. */ + CUSTOM("9\\d") { + + /** {@inheritDoc} */ + @Override + public void parse(final String line, final ParseInfo pi) { + // Not implemented yet + } + + /** {@inheritDoc} */ + public Iterable allowedNext() { + return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, + STAT, COMPATIBILITY, COMMENTS, CUSTOM); } @@ -1052,6 +1277,14 @@ public Stream allowedNext() { @Override public void parse(final String line, final ParseInfo pi) { + // fixed 2022-12-12 + // For the case of monthE is -1. + // Use the date of the last range data as the end epoch. + if (pi.header.getEndEpoch() == null) { + final List rangeData = pi.dataBlock.getRangeData(); + pi.header.setEndEpoch(rangeData.get(rangeData.size() - 1).getDate()); + } + // Fill data block pi.dataBlock.setHeader(pi.header); pi.dataBlock.setConfigurationRecords(pi.configurationRecords); @@ -1060,18 +1293,40 @@ public void parse(final String line, final ParseInfo pi) { pi.file.addDataBlock(pi.dataBlock); // Initialize a new empty containers - pi.startEpoch = DateComponents.J2000_EPOCH; + pi.startEpochDateComponents = DateComponents.J2000_EPOCH; + final CRDHeader lastHeader = pi.header; pi.header = new CRDHeader(); pi.configurationRecords = new CRDConfiguration(); pi.dataBlock = new CRDDataBlock(); - pi.lastRange = null; + + // fill header with H1 H2 H3 if the file is for many targets, single system + // configuration (see P31 in crd201) + pi.header.setFormat(lastHeader.getFormat()); + pi.header.setVersion(lastHeader.getVersion()); + pi.header.setProductionEpoch(lastHeader.getProductionEpoch()); + pi.header.setProductionHour(lastHeader.getProductionHour()); + + pi.header.setStationName(lastHeader.getStationName()); + pi.header.setSystemIdentifier(lastHeader.getSystemIdentifier()); + pi.header.setSystemNumber(lastHeader.getSystemNumber()); + pi.header.setSystemOccupancy(lastHeader.getSystemOccupancy()); + pi.header.setEpochIdentifier(lastHeader.getEpochIdentifier()); + pi.header.setStationNetword(lastHeader.getStationNetword()); + + pi.header.setName(lastHeader.getName()); + pi.header.setIlrsSatelliteId(lastHeader.getIlrsSatelliteId()); + pi.header.setSic(lastHeader.getSic()); + pi.header.setNoradId(lastHeader.getNoradId()); + pi.header.setSpacecraftEpochTimeScale(lastHeader.getSpacecraftEpochTimeScale()); + pi.header.setTargetClass(lastHeader.getTargetClass()); + pi.header.setTargetLocation(lastHeader.getTargetLocation()); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H1, H9, COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(H1, H4, H9, COMMENTS); } }, @@ -1087,8 +1342,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(H9); + public Iterable allowedNext() { + return Collections.singleton(H9); } }; @@ -1111,14 +1366,6 @@ public Stream allowedNext() { } } - /** - * Get the regular expression for identifying line. - * @return the regular expression for identifying line - */ - public String getFirstIdentifier() { - return identifiers[0]; - } - /** Parse a line. * @param line line to parse * @param pi holder for transient data @@ -1128,7 +1375,7 @@ public String getFirstIdentifier() { /** Get the allowed parsers for next line. * @return allowed parsers for next line */ - public abstract Stream allowedNext(); + public abstract Iterable allowedNext(); /** Check if parser can handle line. * @param line line to parse @@ -1157,14 +1404,15 @@ private static boolean readBoolean(final String value) { } /** - * Read a double value taking into consideration a possible "na". + * Read an integer value taking into consideration a possible "NaN". + * If the value is "NaN", the defaultValue is returned. * @param value input string - * @return the corresponding double value + * @param defaultValue the default value + * @return the corresponding integer value */ - private static double readDoubleWithNaN(final String value) { - return "na".equals(value) ? Double.NaN : Double.parseDouble(value); + private static int readIntegerWithNaN(final String value, final int defaultValue) { + return CRD.STR_NAN.equalsIgnoreCase(value) ? defaultValue : Integer.parseInt(value); } - } } diff --git a/src/main/java/org/orekit/files/ilrs/ILRSHeader.java b/src/main/java/org/orekit/files/ilrs/ILRSHeader.java index 6fb6333a04..a5a8286677 100644 --- a/src/main/java/org/orekit/files/ilrs/ILRSHeader.java +++ b/src/main/java/org/orekit/files/ilrs/ILRSHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -67,6 +67,17 @@ public abstract class ILRSHeader { /** Sequence number. */ private int sequenceNumber; + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public ILRSHeader() { + // nothing to do + } + /** * Get the file format. * @return the file format diff --git a/src/main/java/org/orekit/files/ilrs/StreamingCpfWriter.java b/src/main/java/org/orekit/files/ilrs/StreamingCpfWriter.java index bf14ada58f..acc67cb268 100644 --- a/src/main/java/org/orekit/files/ilrs/StreamingCpfWriter.java +++ b/src/main/java/org/orekit/files/ilrs/StreamingCpfWriter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/ilrs/package-info.java b/src/main/java/org/orekit/files/ilrs/package-info.java index 1ec9eef147..1c4cac1e60 100644 --- a/src/main/java/org/orekit/files/ilrs/package-info.java +++ b/src/main/java/org/orekit/files/ilrs/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/package-info.java b/src/main/java/org/orekit/files/package-info.java index 18f5d96c43..268670f2aa 100644 --- a/src/main/java/org/orekit/files/package-info.java +++ b/src/main/java/org/orekit/files/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/AppliedDCBS.java b/src/main/java/org/orekit/files/rinex/AppliedDCBS.java similarity index 92% rename from src/main/java/org/orekit/gnss/AppliedDCBS.java rename to src/main/java/org/orekit/files/rinex/AppliedDCBS.java index 3ad2dac84f..6b0763cab3 100644 --- a/src/main/java/org/orekit/gnss/AppliedDCBS.java +++ b/src/main/java/org/orekit/files/rinex/AppliedDCBS.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.files.rinex; + +import org.orekit.gnss.SatelliteSystem; /** Corrections of Differential Code Biases (DCBs) applied. * Contains information on the programs used to correct the observations @@ -37,7 +39,7 @@ public class AppliedDCBS { * @param sourceDCBS Source of corrections (URL) */ public AppliedDCBS(final SatelliteSystem satelliteSystem, - final String progDCBS, final String sourceDCBS) { + final String progDCBS, final String sourceDCBS) { this.satelliteSystem = satelliteSystem; this.progDCBS = progDCBS; this.sourceDCBS = sourceDCBS; diff --git a/src/main/java/org/orekit/gnss/AppliedPCVS.java b/src/main/java/org/orekit/files/rinex/AppliedPCVS.java similarity index 95% rename from src/main/java/org/orekit/gnss/AppliedPCVS.java rename to src/main/java/org/orekit/files/rinex/AppliedPCVS.java index 9c1f6eeb7c..9835bd8e16 100644 --- a/src/main/java/org/orekit/gnss/AppliedPCVS.java +++ b/src/main/java/org/orekit/files/rinex/AppliedPCVS.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.files.rinex; + +import org.orekit.gnss.SatelliteSystem; /** Corrections of antenna phase center variations (PCVs) applied. * Contains information on the programs used to correct the observations diff --git a/src/main/java/org/orekit/gnss/HatanakaCompressFilter.java b/src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java similarity index 99% rename from src/main/java/org/orekit/gnss/HatanakaCompressFilter.java rename to src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java index 511f97c21a..f7d337a936 100644 --- a/src/main/java/org/orekit/gnss/HatanakaCompressFilter.java +++ b/src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.files.rinex; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; @@ -31,6 +31,7 @@ import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.gnss.SatelliteSystem; /** Decompression filter for Hatanaka compressed RINEX files. * @see A @@ -45,6 +46,17 @@ public class HatanakaCompressFilter implements DataFilter { /** Pattern for rinex 3 observation files. */ private static final Pattern RINEX_3_PATTERN = Pattern.compile("^(\\w{9}_\\w{1}_\\d{11}_\\d{2}\\w_\\d{2}\\w{1}_\\w{2})\\.crx$"); + /** Empty constructor. + *

          + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

          + * @since 12.0 + */ + public HatanakaCompressFilter() { + // nothing to do + } + /** {@inheritDoc} */ @Override public DataSource filter(final DataSource original) { diff --git a/src/main/java/org/orekit/files/rinex/RinexFile.java b/src/main/java/org/orekit/files/rinex/RinexFile.java new file mode 100644 index 0000000000..453caa3d52 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/RinexFile.java @@ -0,0 +1,68 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.orekit.files.rinex.section.RinexBaseHeader; +import org.orekit.files.rinex.section.RinexComment; + +/** Container for Rinex file. + * @param Type of the header + * @author Luc Maisonobe + * @since 12.0 + */ +public class RinexFile { + + /** Header. */ + private final T header; + + /** Comments. */ + private final List comments; + + /** Simple constructor. + * @param header header + */ + protected RinexFile(final T header) { + this.header = header; + this.comments = new ArrayList<>(); + } + + /** Get the header. + * @return header + */ + public T getHeader() { + return header; + } + + /** Get an unmodifiable view of the comments. + * @return unmodifiable view of the comments + */ + public List getComments() { + return Collections.unmodifiableList(comments); + } + + /** Add a comment. + * @param comment comment to add + */ + public void addComment(final RinexComment comment) { + comments.add(comment); + } + +} diff --git a/src/main/java/org/orekit/gnss/clock/RinexClock.java b/src/main/java/org/orekit/files/rinex/clock/RinexClock.java similarity index 99% rename from src/main/java/org/orekit/gnss/clock/RinexClock.java rename to src/main/java/org/orekit/files/rinex/clock/RinexClock.java index a39c023c20..30e9eb6bcf 100644 --- a/src/main/java/org/orekit/gnss/clock/RinexClock.java +++ b/src/main/java/org/orekit/files/rinex/clock/RinexClock.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.clock; +package org.orekit.files.rinex.clock; import java.util.ArrayList; import java.util.Collections; @@ -25,9 +25,9 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.AppliedDCBS; +import org.orekit.files.rinex.AppliedPCVS; import org.orekit.frames.Frame; -import org.orekit.gnss.AppliedDCBS; -import org.orekit.gnss.AppliedPCVS; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.TimeSystem; diff --git a/src/main/java/org/orekit/gnss/clock/RinexClockParser.java b/src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java similarity index 94% rename from src/main/java/org/orekit/gnss/clock/RinexClockParser.java rename to src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java index abf07d570b..1b481f1d36 100644 --- a/src/main/java/org/orekit/gnss/clock/RinexClockParser.java +++ b/src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.clock; +package org.orekit.files.rinex.clock; import java.io.BufferedReader; import java.io.IOException; @@ -25,28 +25,27 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.InputMismatchException; import java.util.List; import java.util.Locale; -import java.util.Optional; import java.util.Scanner; import java.util.function.Function; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.AppliedDCBS; +import org.orekit.files.rinex.AppliedPCVS; +import org.orekit.files.rinex.clock.RinexClock.ClockDataType; +import org.orekit.files.rinex.clock.RinexClock.Receiver; +import org.orekit.files.rinex.clock.RinexClock.ReferenceClock; import org.orekit.frames.Frame; -import org.orekit.gnss.AppliedDCBS; -import org.orekit.gnss.AppliedPCVS; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.TimeSystem; -import org.orekit.gnss.clock.RinexClock.ClockDataType; -import org.orekit.gnss.clock.RinexClock.Receiver; -import org.orekit.gnss.clock.RinexClock.ReferenceClock; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.TimeComponents; @@ -217,24 +216,28 @@ public RinexClock parse(final BufferedReader reader, final ParseInfo pi = new ParseInfo(); int lineNumber = 0; - Stream candidateParsers = Stream.of(LineParser.HEADER_VERSION); + Iterable candidateParsers = Collections.singleton(LineParser.HEADER_VERSION); + nextLine: for (String line = reader.readLine(); line != null; line = reader.readLine()) { ++lineNumber; - final String l = line; - final Optional selected = candidateParsers.filter(p -> p.canHandle(l)).findFirst(); - if (selected.isPresent()) { - try { - selected.get().parse(line, pi); - } catch (StringIndexOutOfBoundsException | NumberFormatException | InputMismatchException e) { - throw new OrekitException(e, - OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, fileName, line); + for (final LineParser candidate : candidateParsers) { + if (candidate.canHandle(line)) { + try { + candidate.parse(line, pi); + candidateParsers = candidate.allowedNext(); + continue nextLine; + } catch (StringIndexOutOfBoundsException | NumberFormatException | InputMismatchException e) { + throw new OrekitException(e, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, fileName, line); + } } - candidateParsers = selected.get().allowedNext(); - } else { - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, fileName, line); } + + // no parsers found for this line + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, fileName, line); + } return pi.file; @@ -321,7 +324,7 @@ public void parse(final String line, final ParseInfo pi) { // Record satellite system and default time system in clock file object final SatelliteSystem satelliteSystem = SatelliteSystem.parseSatelliteSystem(satelliteSystemString); pi.file.setSatelliteSystem(satelliteSystem); - pi.file.setTimeScale(satelliteSystem.getDefaultTimeSystem(pi.timeScales)); + pi.file.setTimeScale(satelliteSystem.getObservationTimeScale().getTimeScale(pi.timeScales)); } // Set time scale to UTC by default if (pi.file.getTimeScale() == null) { @@ -837,8 +840,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(CLOCK_DATA); + public Iterable allowedNext() { + return Collections.singleton(CLOCK_DATA); } }, @@ -899,8 +902,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(CLOCK_DATA, CLOCK_DATA_CONTINUATION); + public Iterable allowedNext() { + return Arrays.asList(CLOCK_DATA, CLOCK_DATA_CONTINUATION); } }, @@ -940,8 +943,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(CLOCK_DATA); + public Iterable allowedNext() { + return Collections.singleton(CLOCK_DATA); } }; @@ -969,11 +972,11 @@ public Stream allowedNext() { *

          * @return allowed parsers for next line */ - public Stream allowedNext() { - return Stream.of(HEADER_PROGRAM, HEADER_COMMENT, HEADER_SYSTEM_OBS, HEADER_SYSTEM_OBS_CONTINUATION, HEADER_TIME_SYSTEM, HEADER_LEAP_SECONDS, - HEADER_LEAP_SECONDS_GNSS, HEADER_DCBS, HEADER_PCVS, HEADER_TYPES_OF_DATA, HEADER_STATIONS_NAME, HEADER_STATION_CLOCK_REF, - HEADER_ANALYSIS_CENTER, HEADER_NUMBER_OF_CLOCK_REF, HEADER_ANALYSIS_CLOCK_REF, HEADER_NUMBER_OF_SOLN_STATIONS, - HEADER_SOLN_STATIONS, HEADER_NUMBER_OF_SOLN_SATS, HEADER_PRN_LIST, HEADER_END); + public Iterable allowedNext() { + return Arrays.asList(HEADER_PROGRAM, HEADER_COMMENT, HEADER_SYSTEM_OBS, HEADER_SYSTEM_OBS_CONTINUATION, HEADER_TIME_SYSTEM, HEADER_LEAP_SECONDS, + HEADER_LEAP_SECONDS_GNSS, HEADER_DCBS, HEADER_PCVS, HEADER_TYPES_OF_DATA, HEADER_STATIONS_NAME, HEADER_STATION_CLOCK_REF, + HEADER_ANALYSIS_CENTER, HEADER_NUMBER_OF_CLOCK_REF, HEADER_ANALYSIS_CLOCK_REF, HEADER_NUMBER_OF_SOLN_STATIONS, + HEADER_SOLN_STATIONS, HEADER_NUMBER_OF_SOLN_SATS, HEADER_PRN_LIST, HEADER_END); } /** Check if parser can handle line. diff --git a/src/main/java/org/orekit/gnss/clock/package-info.java b/src/main/java/org/orekit/files/rinex/clock/package-info.java similarity index 92% rename from src/main/java/org/orekit/gnss/clock/package-info.java rename to src/main/java/org/orekit/files/rinex/clock/package-info.java index ba1456b949..89bcb35191 100644 --- a/src/main/java/org/orekit/gnss/clock/package-info.java +++ b/src/main/java/org/orekit/files/rinex/clock/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,4 +22,4 @@ * @since 11.0 * */ -package org.orekit.gnss.clock; +package org.orekit.files.rinex.clock; diff --git a/src/main/java/org/orekit/files/rinex/navigation/EarthOrientationParameterMessage.java b/src/main/java/org/orekit/files/rinex/navigation/EarthOrientationParameterMessage.java new file mode 100644 index 0000000000..1e75fe8f72 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/EarthOrientationParameterMessage.java @@ -0,0 +1,227 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; +import org.orekit.time.AbsoluteDate; + +/** Container for data contained in a Earth Orientation Parameter navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class EarthOrientationParameterMessage extends TypeSvMessage { + + /** Reference epoch. */ + private AbsoluteDate referenceEpoch; + + /** X component of the pole (rad). */ + private double xp; + + /** X component of the pole first derivative (rad/s). */ + private double xpDot; + + /** X component of the pole second derivative (rad/s²). */ + private double xpDotDot; + + /** Y component of the pole (rad). */ + private double yp; + + /** Y component of the pole first derivative (rad/s). */ + private double ypDot; + + /** Y component of the pole second derivative (rad/s²). */ + private double ypDotDot; + + /** ΔUT₁ (s). + * According to Rinex 4.00 table A31, this may be either UT₁-UTC or UT₁-GPST + * depending on constellation and applicable Interface Control Document). + */ + private double dUt1; + + /** ΔUT₁ first derivative (s/s). */ + private double dUt1Dot; + + /** ΔUT₁ second derivative (s/s²). */ + private double dUt1DotDot; + + /** Transmission time. */ + private double transmissionTime; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + public EarthOrientationParameterMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + super(system, prn, navigationMessageType); + } + + /** Get the reference epoch. + * @return the reference epoch + */ + public AbsoluteDate getReferenceEpoch() { + return referenceEpoch; + } + + /** Set the reference epoch. + * @param referenceEpoch the reference epoch to set + */ + public void setReferenceEpoch(final AbsoluteDate referenceEpoch) { + this.referenceEpoch = referenceEpoch; + } + + /** Get the X component of the pole. + * @return the X component of the pole (rad) + */ + public double getXp() { + return xp; + } + + /** Set the X component of the pole. + * @param xp X component of the pole (rad) + */ + public void setXp(final double xp) { + this.xp = xp; + } + + /** Get the X component of the pole first derivative. + * @return the X component of the pole first derivative (rad/s) + */ + public double getXpDot() { + return xpDot; + } + + /** Set the X component of the pole first derivative. + * @param xpDot X component of the pole first derivative (rad/s) + */ + public void setXpDot(final double xpDot) { + this.xpDot = xpDot; + } + + /** Get the X component of the pole second derivative. + * @return the X component of the pole second derivative (rad/s²) + */ + public double getXpDotDot() { + return xpDotDot; + } + + /** Set the X component of the pole second derivative. + * @param xpDotDot X component of the pole second derivative (rad/s²) + */ + public void setXpDotDot(final double xpDotDot) { + this.xpDotDot = xpDotDot; + } + + /** Get the Y component of the pole. + * @return the Y component of the pole (rad) + */ + public double getYp() { + return yp; + } + + /** Set the Y component of the pole. + * @param yp Y component of the pole (rad) + */ + public void setYp(final double yp) { + this.yp = yp; + } + + /** Get the Y component of the pole first derivative. + * @return the Y component of the pole first derivative (rad/s) + */ + public double getYpDot() { + return ypDot; + } + + /** Set the Y component of the pole first derivative. + * @param ypDot Y component of the pole first derivative (rad/s) + */ + public void setYpDot(final double ypDot) { + this.ypDot = ypDot; + } + + /** Get the Y component of the pole second derivative. + * @return the Y component of the pole second derivative (rad/s²) + */ + public double getYpDotDot() { + return ypDotDot; + } + + /** Set the Y component of the pole second derivative. + * @param ypDotDot Y component of the pole second derivative (rad/s²) + */ + public void setYpDotDot(final double ypDotDot) { + this.ypDotDot = ypDotDot; + } + + /** Get the ΔUT₁. + * @return the ΔUT₁ (s) + */ + public double getDut1() { + return dUt1; + } + + /** Set the ΔUT₁. + * @param dUT1 ΔUT₁ (s) + */ + public void setDut1(final double dUT1) { + this.dUt1 = dUT1; + } + + /** Get the ΔUT₁ first derivative. + * @return the ΔUT₁ first derivative (s/s) + */ + public double getDut1Dot() { + return dUt1Dot; + } + + /** Set the ΔUT₁ first derivative. + * @param dUT1Dot ΔUT₁ first derivative (s/s) + */ + public void setDut1Dot(final double dUT1Dot) { + this.dUt1Dot = dUT1Dot; + } + + /** Get the ΔUT₁ second derivative. + * @return the ΔUT₁ second derivative (s/s²) + */ + public double getDut1DotDot() { + return dUt1DotDot; + } + + /** Set the ΔUT₁ second derivative. + * @param dUT1DotDot ΔUT₁ second derivative (s/s²) + */ + public void setDut1DotDot(final double dUT1DotDot) { + this.dUt1DotDot = dUT1DotDot; + } + + /** Get the message transmission time. + * @return message transmission time + */ + public double getTransmissionTime() { + return transmissionTime; + } + + /** Set the message transmission time. + * @param transmissionTime the message transmission time + */ + public void setTransmissionTime(final double transmissionTime) { + this.transmissionTime = transmissionTime; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/IonosphereBDGIMMessage.java b/src/main/java/org/orekit/files/rinex/navigation/IonosphereBDGIMMessage.java new file mode 100644 index 0000000000..ccaca2f8c7 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/IonosphereBDGIMMessage.java @@ -0,0 +1,67 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; + +/** Container for data contained in a ionosphere BDGIM message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class IonosphereBDGIMMessage extends IonosphereBaseMessage { + + /** α (TECu). */ + private final double[] alpha; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + public IonosphereBDGIMMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + super(system, prn, navigationMessageType); + alpha = new double[9]; + } + + /** Get the α coefficients. + *

          + * Beware Orekit uses SI units here. + * In order to retrieve the more traditional TECu, use + * {@code Unit.TOTAL_ELECTRON_CONTENT_UNIT.fromSI(msg.getAlpha()[i])} + *

          + * @return α coefficients (m⁻²) + * @see org.orekit.utils.units.Unit#TOTAL_ELECTRON_CONTENT_UNIT + */ + public double[] getAlpha() { + return alpha.clone(); + } + + /** Set one α coefficient. + *

          + * Beware Orekit uses SI units here. + * In order to use the more traditional TECu, use + * {@code msg.setAlpha(i, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(ai))} + *

          + * @param i index of the coefficient + * @param alphaI α coefficient to set (m⁻²) + * @see org.orekit.utils.units.Unit#TOTAL_ELECTRON_CONTENT_UNIT + */ + public void setAlphaI(final int i, final double alphaI) { + alpha[i] = alphaI; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/IonosphereBaseMessage.java b/src/main/java/org/orekit/files/rinex/navigation/IonosphereBaseMessage.java new file mode 100644 index 0000000000..46b39fcc59 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/IonosphereBaseMessage.java @@ -0,0 +1,54 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; +import org.orekit.time.AbsoluteDate; + +/** Base container for data contained in a ionosphere message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class IonosphereBaseMessage extends TypeSvMessage { + + /** Transmit time. */ + private AbsoluteDate transmitTime; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + public IonosphereBaseMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + super(system, prn, navigationMessageType); + } + + /** Get the transmit time. + * @return the transmit time + */ + public AbsoluteDate getTransmitTime() { + return transmitTime; + } + + /** Set the transmit time. + * @param transmitTime the transmit time to set + */ + public void setTransmitTime(final AbsoluteDate transmitTime) { + this.transmitTime = transmitTime; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/IonosphereKlobucharMessage.java b/src/main/java/org/orekit/files/rinex/navigation/IonosphereKlobucharMessage.java new file mode 100644 index 0000000000..c0e6d97ede --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/IonosphereKlobucharMessage.java @@ -0,0 +1,128 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; +import org.orekit.propagation.analytical.gnss.data.GNSSConstants; +import org.orekit.utils.units.Unit; + +/** Container for data contained in a ionosphere Klobuchar message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class IonosphereKlobucharMessage extends IonosphereBaseMessage { + + /** Converters for Klobuchar parameters. */ + public static final Unit[] S_PER_SC_N; + static { + final Unit sc = Unit.RADIAN.scale("sc", GNSSConstants.GNSS_PI); + S_PER_SC_N = new Unit[4]; + S_PER_SC_N[0] = Unit.SECOND; + S_PER_SC_N[1] = S_PER_SC_N[0].divide("s/sc", sc); + S_PER_SC_N[2] = S_PER_SC_N[1].divide("s/sc²", sc); + S_PER_SC_N[3] = S_PER_SC_N[2].divide("s/sc³", sc); + } + + /** α (s/radⁿ). */ + private final double[] alpha; + + /** β (s/radⁿ). */ + private final double[] beta; + + /** Region code. */ + private RegionCode regionCode; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + public IonosphereKlobucharMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + super(system, prn, navigationMessageType); + alpha = new double[4]; + beta = new double[4]; + } + + /** Get the α coefficients. + *

          + * Beware Orekit uses SI units here. + * In order to retrieve the more traditional s/semi-circleⁿ, use + * {@code IonosphereKlobucharMessage.S_PER_SC_N[i].fromSI(alpha[i])} + *

          + * @return α coefficients (s/radⁿ) + * @see #S_PER_SC_N + */ + public double[] getAlpha() { + return alpha.clone(); + } + + /** Set one α coefficient. + *

          + * Beware Orekit uses SI units here. + * In order to use the more traditional s/semi-circleⁿ, use + * {@code setAlphaI(i, IonosphereKlobucharMessage.S_PER_SC_N[i].toSi(alpha[i]))} + *

          + * @param i index of the coefficient + * @param alphaI α coefficient to set (s/radⁿ) + * @see #S_PER_SC_N + */ + public void setAlphaI(final int i, final double alphaI) { + alpha[i] = alphaI; + } + + /** Get the β coefficients. + *

          + * Beware Orekit uses SI units here. + * In order to retrieve the more traditional s/semi-circleⁿ, use + * {@code IonosphereKlobucharMessage.S_PER_SC_N[i].fromSI(beta[i])} + *

          + * @return β coefficients (s/radⁿ) + * @see #S_PER_SC_N + */ + public double[] getBeta() { + return beta.clone(); + } + + /** Set one β coefficient. + *

          + * Beware Orekit uses SI units here. + * In order to use the more traditional s/semi-circleⁿ, use + * {@code setBetaI(i, IonosphereKlobucharMessage.S_PER_SC_N[i].toSi(beta[i]))} + *

          + * @param i index of the coefficient + * @param betaI β coefficient to set (s/radⁿ) + * @see #S_PER_SC_N + */ + public void setBetaI(final int i, final double betaI) { + beta[i] = betaI; + } + + /** Get the region code. + * @return region code + */ + public RegionCode getRegionCode() { + return regionCode; + } + + /** Set the region code. + * @param regionCode region code + */ + public void setRegionCode(final RegionCode regionCode) { + this.regionCode = regionCode; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/IonosphereNequickGMessage.java b/src/main/java/org/orekit/files/rinex/navigation/IonosphereNequickGMessage.java new file mode 100644 index 0000000000..ffe26b7eb8 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/IonosphereNequickGMessage.java @@ -0,0 +1,150 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; +import org.orekit.utils.units.Unit; + +/** Container for data contained in a ionosphere Nequick G message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class IonosphereNequickGMessage extends IonosphereBaseMessage { + + /** Converter for Nequick-G aᵢ₀ parameter. */ + public static final Unit SFU = Unit.SOLAR_FLUX_UNIT; + + /** Converter for Nequick-G aᵢ₁ parameter. */ + public static final Unit SFU_PER_DEG = SFU.divide("sfu/deg", Unit.DEGREE); + + /** Converter for Nequick-G aᵢ₂ parameter. */ + public static final Unit SFU_PER_DEG2 = SFU_PER_DEG.divide("sfu/deg²", Unit.DEGREE); + + /** aᵢ₀ (sfu). */ + private double ai0; + + /** aᵢ₁ (sfu/rad). */ + private double ai1; + + /** aᵢ₂ (sfu/rad²). */ + private double ai2; + + /** Disturbance flags. */ + private int flags; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + public IonosphereNequickGMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + super(system, prn, navigationMessageType); + } + + /** Get aᵢ₀. + *

          + * Beware Orekit uses SI units here. + * In order to retrieve the more traditional SFU, use + * {@code IonosphereNequickGMessage.SFU.fromSI(msg.getAi0())} + *

          + * @return aᵢ₀ (W/m²/Hz) + * @see #SFU + */ + public double getAi0() { + return ai0; + } + + /** Set aᵢ₀. + *

          + * Beware Orekit uses SI units here. + * In order to use the more traditional SFU, use + * {@code msg.setAi0(IonosphereNequickGMessage.SFU.toSI(ai0))} + *

          + * @param ai0 aᵢ₀ (W/m²/Hz) + * @see #SFU + */ + public void setAi0(final double ai0) { + this.ai0 = ai0; + } + + /** Get aᵢ₁. + *

          + * Beware Orekit uses SI units here. + * In order to retrieve the more traditional SFU/deg, use + * {@code IonosphereNequickGMessage.SFU_PAR_DEG.fromSI(msg.getAi1())} + *

          + * @return aᵢ₁ (W/m²/Hz/rad) + * @see #SFU_PER_DEG + */ + public double getAi1() { + return ai1; + } + + /** Set aᵢ₁. + *

          + * Beware Orekit uses SI units here. + * In order to use the more traditional SFU/deg, use + * {@code msg.setAi1(IonosphereNequickGMessage.SFU_PER_DEG.toSI(ai1))} + *

          + * @param ai1 aᵢ₁ (W/m²/Hz/rad) + * @see #SFU_PER_DEG + */ + public void setAi1(final double ai1) { + this.ai1 = ai1; + } + + /** Get aᵢ₂. + *

          + * Beware Orekit uses SI units here. + * In order to retrieve the more traditional SFU/deg², use + * {@code IonosphereNequickGMessage.SFU_PER_DEG_2.fromSI(msg.getAi2())} + *

          + * @return aᵢ₂ (W/m²/Hz/rad²) + * @see #SFU_PER_DEG2 + */ + public double getAi2() { + return ai2; + } + + /** Set aᵢ₂. + *

          + * Beware Orekit uses SI units here. + * In order to use the more traditional SFU/deg², use + * {@code msg.setAi2(IonosphereNequickGMessage.SFU_PER_DEG2.toSI(ai2))} + *

          + * @param ai2 aᵢ₂ (W/m²/Hz/rad²) + * @see #SFU_PER_DEG2 + */ + public void setAi2(final double ai2) { + this.ai2 = ai2; + } + + /** Get the disturbance flags. + * @return disturbance flags + */ + public int getFlags() { + return flags; + } + + /** Set the disturbance flags. + * @param flags disturbance flags + */ + public void setFlags(final int flags) { + this.flags = flags; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/IonosphericCorrectionType.java b/src/main/java/org/orekit/files/rinex/navigation/IonosphericCorrectionType.java new file mode 100644 index 0000000000..d7048b4c03 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/IonosphericCorrectionType.java @@ -0,0 +1,41 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +/** + * Ionospheric correction type. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum IonosphericCorrectionType { + + /** Galileo. */ + GAL, + + /** GPS α/β. */ + GPS, + + /** QZSSS α/β. */ + QZS, + + /** Beidou α/β. */ + BDS, + + /** IRNSS α/β. */ + IRN; + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/RegionCode.java b/src/main/java/org/orekit/files/rinex/navigation/RegionCode.java new file mode 100644 index 0000000000..c9e2979b6e --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/RegionCode.java @@ -0,0 +1,32 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +/** Enumerate for region code. + * @see IonosphereKlobucharMessage + * @author Luc Maisonobe + * @since 12.0 + */ +public enum RegionCode { + + /** Wide Area. */ + WIDE_AREA, + + /** Japan area (for QZSS only). */ + JAPAN; + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/RinexNavigation.java b/src/main/java/org/orekit/files/rinex/navigation/RinexNavigation.java new file mode 100644 index 0000000000..4ca372cbb6 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/RinexNavigation.java @@ -0,0 +1,587 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.orekit.files.rinex.RinexFile; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.models.earth.ionosphere.KlobucharIonoModel; +import org.orekit.models.earth.ionosphere.NeQuickModel; +import org.orekit.propagation.analytical.gnss.data.BeidouCivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.BeidouLegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GPSCivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GPSLegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.IRNSSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.QZSSCivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.QZSSLegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.SBASNavigationMessage; + +/** + * Represents a parsed RINEX navigation messages files. + * @author Bryan Cazabonne + * @author Luc Maisonobe + * @since 11.0 + */ +public class RinexNavigation extends RinexFile { + + /** The 4 Klobuchar coefficients of a cubic equation representing the amplitude of the vertical delay. */ + private double[] klobucharAlpha; + + /** The 4 coefficients of a cubic equation representing the period of the model. */ + private double[] klobucharBeta; + + /** The three ionospheric coefficients broadcast in the Galileo navigation message. */ + private double[] neQuickAlpha; + + /** A map containing the GPS navigation messages. */ + private final Map> gpsLegacyData; + + /** A map containing the GPS navigation messages. */ + private final Map> gpsCivilianData; + + /** A map containing the Galileo navigation messages. */ + private final Map> galileoData; + + /** A map containing the Beidou navigation messages. */ + private final Map> beidouLegacyData; + + /** A map containing the Beidou navigation messages. */ + private final Map> beidouCivilianData; + + /** A map containing the QZSS navigation messages. */ + private final Map> qzssLegacyData; + + /** A map containing the QZSS navigation messages. */ + private final Map> qzssCivilianData; + + /** A map containing the IRNSS navigation messages. */ + private final Map> irnssData; + + /** A map containing the GLONASS navigation messages. */ + private final Map> glonassData; + + /** A map containing the SBAS navigation messages. */ + private final Map> sbasData; + + /** System time offsets. + * @since 12.0 + */ + private final List systemTimeOffsets; + + /** Earth orientation parameters. + * @since 12.0 + */ + private final List eops; + + /** Ionosphere Klobuchar messages. + * @since 12.0 + */ + private final List klobucharMessages; + + /** Ionosphere Nequick G messages. + * @since 12.0 + */ + private final List nequickGMessages; + + /** Ionosphere BDGIM messages. + * @since 12.0 + */ + private final List bdgimMessages; + + /** Constructor. */ + public RinexNavigation() { + super(new RinexNavigationHeader()); + this.gpsLegacyData = new HashMap<>(); + this.gpsCivilianData = new HashMap<>(); + this.galileoData = new HashMap<>(); + this.beidouLegacyData = new HashMap<>(); + this.beidouCivilianData = new HashMap<>(); + this.qzssLegacyData = new HashMap<>(); + this.qzssCivilianData = new HashMap<>(); + this.irnssData = new HashMap<>(); + this.glonassData = new HashMap<>(); + this.sbasData = new HashMap<>(); + this.systemTimeOffsets = new ArrayList<>(); + this.eops = new ArrayList<>(); + this.klobucharMessages = new ArrayList<>(); + this.nequickGMessages = new ArrayList<>(); + this.bdgimMessages = new ArrayList<>(); + } + + /** + * Get the "alpha" ionospheric parameters. + *

          + * They are used to initialize the {@link KlobucharIonoModel}. + *

          + * @return the "alpha" ionospheric parameters + */ + public double[] getKlobucharAlpha() { + return klobucharAlpha.clone(); + } + + /** + * Set the "alpha" ionspheric parameters. + * @param klobucharAlpha the "alpha" ionspheric parameters to set + */ + public void setKlobucharAlpha(final double[] klobucharAlpha) { + this.klobucharAlpha = klobucharAlpha.clone(); + } + + /** + * Get the "beta" ionospheric parameters. + *

          + * They are used to initialize the {@link KlobucharIonoModel}. + *

          + * @return the "beta" ionospheric parameters + */ + public double[] getKlobucharBeta() { + return klobucharBeta.clone(); + } + + /** + * Set the "beta" ionospheric parameters. + * @param klobucharBeta the "beta" ionospheric parameters to set + */ + public void setKlobucharBeta(final double[] klobucharBeta) { + this.klobucharBeta = klobucharBeta.clone(); + } + + /** + * Get the "alpha" ionospheric parameters. + *

          + * They are used to initialize the {@link NeQuickModel}. + *

          + * @return the "alpha" ionospheric parameters + */ + public double[] getNeQuickAlpha() { + return neQuickAlpha.clone(); + } + + /** + * Set the "alpha" ionospheric parameters. + * @param neQuickAlpha the "alpha" ionospheric parameters to set + */ + public void setNeQuickAlpha(final double[] neQuickAlpha) { + this.neQuickAlpha = neQuickAlpha.clone(); + } + + /** + * Get all the GPS legacy navigation messages contained in the file. + * @return an unmodifiable list of GPS legacy navigation messages + * @since 12.0 + */ + public Map> getGPSLegacyNavigationMessages() { + return Collections.unmodifiableMap(gpsLegacyData); + } + + /** + * Get the GPS legacy navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. G) + satellite number) + * @return an unmodifiable list of GPS legacy navigation messages + * @since 12.0 + */ + public List getGPSLegacyNavigationMessages(final String satId) { + return Collections.unmodifiableList(gpsLegacyData.get(satId)); + } + + /** + * Add a GPS legacy navigation message to the list. + * @param message message to add + * @since 12.0 + */ + public void addGPSLegacyNavigationMessage(final GPSLegacyNavigationMessage message) { + final int gpsPRN = message.getPRN(); + final String prnString = gpsPRN < 10 ? "0" + String.valueOf(gpsPRN) : String.valueOf(gpsPRN); + final String satId = SatelliteSystem.GPS.getKey() + prnString; + gpsLegacyData.putIfAbsent(satId, new ArrayList<>()); + gpsLegacyData.get(satId).add(message); + } + + /** + * Get all the GPS civilian navigation messages contained in the file. + * @return an unmodifiable list of GPS civilian navigation messages + * @since 12.0 + */ + public Map> getGPSCivilianNavigationMessages() { + return Collections.unmodifiableMap(gpsCivilianData); + } + + /** + * Get the GPS civilian navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. G) + satellite number) + * @return an unmodifiable list of GPS civilian navigation messages + * @since 12.0 + */ + public List getGPSCivilianNavigationMessages(final String satId) { + return Collections.unmodifiableList(gpsCivilianData.get(satId)); + } + + /** + * Add a GPS civilian navigation message to the list. + * @param message message to add + * @since 12.0 + */ + public void addGPSLegacyNavigationMessage(final GPSCivilianNavigationMessage message) { + final int gpsPRN = message.getPRN(); + final String prnString = gpsPRN < 10 ? "0" + String.valueOf(gpsPRN) : String.valueOf(gpsPRN); + final String satId = SatelliteSystem.GPS.getKey() + prnString; + gpsCivilianData.putIfAbsent(satId, new ArrayList<>()); + gpsCivilianData.get(satId).add(message); + } + + /** + * Get all the Galileo navigation messages contained in the file. + * @return an unmodifiable list of Galileo navigation messages + */ + public Map> getGalileoNavigationMessages() { + return Collections.unmodifiableMap(galileoData); + } + + /** + * Get the Galileo navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. E) + satellite number) + * @return an unmodifiable list of Galileo navigation messages + */ + public List getGalileoNavigationMessages(final String satId) { + return Collections.unmodifiableList(galileoData.get(satId)); + } + + /** + * Add a Galileo navigation message to the list. + * @param message message to add + */ + public void addGalileoNavigationMessage(final GalileoNavigationMessage message) { + final int galPRN = message.getPRN(); + final String prnString = galPRN < 10 ? "0" + String.valueOf(galPRN) : String.valueOf(galPRN); + final String satId = SatelliteSystem.GALILEO.getKey() + prnString; + galileoData.putIfAbsent(satId, new ArrayList<>()); + galileoData.get(satId).add(message); + } + + /** + * Get all the Beidou navigation messages contained in the file. + * @return an unmodifiable list of Beidou navigation messages + * @since 12.0 + */ + public Map> getBeidouLegacyNavigationMessages() { + return Collections.unmodifiableMap(beidouLegacyData); + } + + /** + * Get the Beidou navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. C) + satellite number) + * @return an unmodifiable list of Beidou navigation messages + * @since 12.0 + */ + public List getBeidouLegacyNavigationMessages(final String satId) { + return Collections.unmodifiableList(beidouLegacyData.get(satId)); + } + + /** + * Add a Beidou navigation message to the list. + * @param message message to add + * @since 12.0 + */ + public void addBeidouLegacyNavigationMessage(final BeidouLegacyNavigationMessage message) { + final int bdtPRN = message.getPRN(); + final String prnString = bdtPRN < 10 ? "0" + String.valueOf(bdtPRN) : String.valueOf(bdtPRN); + final String satId = SatelliteSystem.BEIDOU.getKey() + prnString; + beidouLegacyData.putIfAbsent(satId, new ArrayList<>()); + beidouLegacyData.get(satId).add(message); + } + + /** + * Get all the Beidou navigation messages contained in the file. + * @return an unmodifiable list of Beidou navigation messages + * @since 12.0 + */ + public Map> getBeidouCivilianNavigationMessages() { + return Collections.unmodifiableMap(beidouCivilianData); + } + + /** + * Get the Beidou navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. C) + satellite number) + * @return an unmodifiable list of Beidou navigation messages + * @since 12.0 + */ + public List getBeidouCivilianNavigationMessages(final String satId) { + return Collections.unmodifiableList(beidouCivilianData.get(satId)); + } + + /** + * Add a Beidou navigation message to the list. + * @param message message to add + * @since 12.0 + */ + public void addBeidouCivilianNavigationMessage(final BeidouCivilianNavigationMessage message) { + final int bdtPRN = message.getPRN(); + final String prnString = bdtPRN < 10 ? "0" + String.valueOf(bdtPRN) : String.valueOf(bdtPRN); + final String satId = SatelliteSystem.BEIDOU.getKey() + prnString; + beidouCivilianData.putIfAbsent(satId, new ArrayList<>()); + beidouCivilianData.get(satId).add(message); + } + + /** + * Get all the QZSS navigation messages contained in the file. + * @return an unmodifiable list of QZSS navigation messages + * @since 12.0 + */ + public Map> getQZSSLegacyNavigationMessages() { + return Collections.unmodifiableMap(qzssLegacyData); + } + + /** + * Get the QZSS navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. J) + satellite number) + * @return an unmodifiable list of QZSS navigation messages + * @since 12.0 + */ + public List getQZSSLegacyNavigationMessages(final String satId) { + return Collections.unmodifiableList(qzssLegacyData.get(satId)); + } + + /** + * Add a QZSS navigation message to the list. + * @param message message to add + * @since 12.0 + */ + public void addQZSSLegacyNavigationMessage(final QZSSLegacyNavigationMessage message) { + final int qzsPRN = message.getPRN(); + final String prnString = qzsPRN < 10 ? "0" + String.valueOf(qzsPRN) : String.valueOf(qzsPRN); + final String satId = SatelliteSystem.QZSS.getKey() + prnString; + qzssLegacyData.putIfAbsent(satId, new ArrayList<>()); + qzssLegacyData.get(satId).add(message); + } + + /** + * Get all the QZSS navigation messages contained in the file. + * @return an unmodifiable list of QZSS navigation messages + * @since 12.0 + */ + public Map> getQZSSCivilianNavigationMessages() { + return Collections.unmodifiableMap(qzssCivilianData); + } + + /** + * Get the QZSS navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. J) + satellite number) + * @return an unmodifiable list of QZSS navigation messages + * @since 12.0 + */ + public List getQZSSCivilianNavigationMessages(final String satId) { + return Collections.unmodifiableList(qzssCivilianData.get(satId)); + } + + /** + * Add a QZSS navigation message to the list. + * @param message message to add + * @since 12.0 + */ + public void addQZSSCivilianNavigationMessage(final QZSSCivilianNavigationMessage message) { + final int qzsPRN = message.getPRN(); + final String prnString = qzsPRN < 10 ? "0" + String.valueOf(qzsPRN) : String.valueOf(qzsPRN); + final String satId = SatelliteSystem.QZSS.getKey() + prnString; + qzssCivilianData.putIfAbsent(satId, new ArrayList<>()); + qzssCivilianData.get(satId).add(message); + } + + /** + * Get all the IRNSS navigation messages contained in the file. + * @return an unmodifiable list of IRNSS navigation messages + */ + public Map> getIRNSSNavigationMessages() { + return Collections.unmodifiableMap(irnssData); + } + + /** + * Get the IRNSS navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. I) + satellite number) + * @return an unmodifiable list of IRNSS navigation messages + */ + public List getIRNSSNavigationMessages(final String satId) { + return Collections.unmodifiableList(irnssData.get(satId)); + } + + /** + * Add a IRNSS navigation message to the list. + * @param message message to add + */ + public void addIRNSSNavigationMessage(final IRNSSNavigationMessage message) { + final int irsPRN = message.getPRN(); + final String prnString = irsPRN < 10 ? "0" + String.valueOf(irsPRN) : String.valueOf(irsPRN); + final String satId = SatelliteSystem.IRNSS.getKey() + prnString; + irnssData.putIfAbsent(satId, new ArrayList<>()); + irnssData.get(satId).add(message); + } + + /** + * Get all the Glonass navigation messages contained in the file. + * @return an unmodifiable list of Glonass navigation messages + */ + public Map> getGlonassNavigationMessages() { + return Collections.unmodifiableMap(glonassData); + } + + /** + * Get the Glonass navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. R) + satellite number) + * @return an unmodifiable list of Glonass navigation messages + */ + public List getGlonassNavigationMessages(final String satId) { + return Collections.unmodifiableList(glonassData.get(satId)); + } + + /** + * Add a Glonass navigation message to the list. + * @param message message to add + */ + public void addGlonassNavigationMessage(final GLONASSNavigationMessage message) { + final int gloPRN = message.getPRN(); + final String prnString = gloPRN < 10 ? "0" + String.valueOf(gloPRN) : String.valueOf(gloPRN); + final String satId = SatelliteSystem.GLONASS.getKey() + prnString; + glonassData.putIfAbsent(satId, new ArrayList<>()); + glonassData.get(satId).add(message); + } + + /** + * Get all the SBAS navigation messages contained in the file. + * @return an unmodifiable list of SBAS navigation messages + */ + public Map> getSBASNavigationMessages() { + return Collections.unmodifiableMap(sbasData); + } + + /** + * Get the SBAS navigation messages for the given satellite Id. + * @param satId satellite Id (i.e. Satellite System (e.g. S) + satellite number) + * @return an unmodifiable list of SBAS navigation messages + */ + public List getSBASNavigationMessages(final String satId) { + return Collections.unmodifiableList(sbasData.get(satId)); + } + + /** + * Add a SBAS navigation message to the list. + * @param message message to add + */ + public void addSBASNavigationMessage(final SBASNavigationMessage message) { + final int sbsPRN = message.getPRN(); + final String prnString = sbsPRN < 10 ? "0" + String.valueOf(sbsPRN) : String.valueOf(sbsPRN); + final String satId = SatelliteSystem.SBAS.getKey() + prnString; + sbasData.putIfAbsent(satId, new ArrayList<>()); + sbasData.get(satId).add(message); + } + + /** + * Get the system time offsets. + * @return an unmodifiable list of system time offsets + * @since 12.0 + */ + public List getSystemTimeOffsets() { + return Collections.unmodifiableList(systemTimeOffsets); + } + + /** + * Add a system time offset. + * @param systemTimeOffset system time offset message + * @since 12.0 + */ + public void addSystemTimeOffset(final SystemTimeOffsetMessage systemTimeOffset) { + systemTimeOffsets.add(systemTimeOffset); + } + + /** + * Get the Earth orientation parameters. + * @return an unmodifiable list of Earth orientation parameters + * @since 12.0 + */ + public List getEarthOrientationParameters() { + return Collections.unmodifiableList(eops); + } + + /** + * Add an Earth orientation parameter. + * @param eop Earth orientation oarameter message + * @since 12.0 + */ + public void addEarthOrientationParameter(final EarthOrientationParameterMessage eop) { + eops.add(eop); + } + + /** + * Get the ionosphere Klobuchar messages. + * @return an unmodifiable list of ionosphere Klobuchar messages + * @since 12.0 + */ + public List getKlobucharMessages() { + return Collections.unmodifiableList(klobucharMessages); + } + + /** + * Add an ionosphere Klobuchar message. + * @param klobuchar ionosphere Klobuchar message + * @since 12.0 + */ + public void addKlobucharMessage(final IonosphereKlobucharMessage klobuchar) { + klobucharMessages.add(klobuchar); + } + + /** + * Get the ionosphere Nequick-G messages. + * @return an unmodifiable list of ionosphere Nequick-G messages + * @since 12.0 + */ + public List getNequickGMessages() { + return Collections.unmodifiableList(nequickGMessages); + } + + /** + * Add an ionosphere Nequick-G message. + * @param nequickG ionosphere Nequick-G message + * @since 12.0 + */ + public void addNequickGMessage(final IonosphereNequickGMessage nequickG) { + nequickGMessages.add(nequickG); + } + + /** + * Get the ionosphere BDGIM messages. + * @return an unmodifiable list of ionosphere BDGIM messages + * @since 12.0 + */ + public List getBDGIMMessages() { + return Collections.unmodifiableList(bdgimMessages); + } + + /** + * Add an ionosphere BDGIM message. + * @param bdgim ionosphere BDGIM message + * @since 12.0 + */ + public void addBDGIMMessage(final IonosphereBDGIMMessage bdgim) { + bdgimMessages.add(bdgim); + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/RinexNavigationHeader.java b/src/main/java/org/orekit/files/rinex/navigation/RinexNavigationHeader.java new file mode 100644 index 0000000000..fca2fcacb9 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/RinexNavigationHeader.java @@ -0,0 +1,119 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import java.util.ArrayList; +import java.util.List; + +import org.orekit.files.rinex.section.RinexBaseHeader; +import org.orekit.files.rinex.utils.RinexFileType; + +/** Header for Rinex Navigation. + * @author Luc Maisonobe + * @since 12.0 + */ +public class RinexNavigationHeader extends RinexBaseHeader { + + /** Ionospheric correction type. */ + private IonosphericCorrectionType ionosphericCorrectionType; + + /** List of time system corrections. */ + private List timeSystemCorrections; + + /** Number of merged files. */ + private int mergedFiles; + + /** Current number of leap seconds. */ + private int numberOfLeapSeconds; + + /** Simple constructor. + */ + public RinexNavigationHeader() { + super(RinexFileType.NAVIGATION); + this.timeSystemCorrections = new ArrayList<>(); + this.mergedFiles = -1; + this.numberOfLeapSeconds = -1; + } + + /** + * Getter for the ionospheric correction type. + * @return the ionospheric correction type + */ + public IonosphericCorrectionType getIonosphericCorrectionType() { + return ionosphericCorrectionType; + } + + /** + * Setter for the ionospheric correction type. + * @param ionosphericCorrectionType the ionospheric correction type to set + */ + public void setIonosphericCorrectionType(final IonosphericCorrectionType ionosphericCorrectionType) { + this.ionosphericCorrectionType = ionosphericCorrectionType; + } + + /** + * Getter for the time system corrections contained in the file header. + *

          + * Corrections to transform the system time to UTC or oter time system. + *

          + * @return the list of time system corrections + */ + public List getTimeSystemCorrections() { + return timeSystemCorrections; + } + + /** + * Add a time system correction to the list. + * @param timeSystemCorrection the element to add + */ + public void addTimeSystemCorrections(final TimeSystemCorrection timeSystemCorrection) { + this.timeSystemCorrections.add(timeSystemCorrection); + } + + /** + * Getter for the number of merged files. + * @return the number of merged files + */ + public int getMergedFiles() { + return mergedFiles; + } + + /** + * Setter for the number of merged files. + * @param mergedFiles the number of merged files + */ + public void setMergedFiles(final int mergedFiles) { + this.mergedFiles = mergedFiles; + } + + /** + * Getter for the current number of leap seconds. + * @return the current number of leap seconds + */ + public int getNumberOfLeapSeconds() { + return numberOfLeapSeconds; + } + + /** + * Setter for the current number of leap seconds. + * @param numberOfLeapSeconds the number of leap seconds to set + */ + public void setNumberOfLeapSeconds(final int numberOfLeapSeconds) { + this.numberOfLeapSeconds = numberOfLeapSeconds; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/RinexNavigationParser.java b/src/main/java/org/orekit/files/rinex/navigation/RinexNavigationParser.java new file mode 100644 index 0000000000..e59ad328f3 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/RinexNavigationParser.java @@ -0,0 +1,2177 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Arrays; +import java.util.Collections; +import java.util.InputMismatchException; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.utils.parsing.RinexUtils; +import org.orekit.gnss.Frequency; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.gnss.TimeSystem; +import org.orekit.propagation.analytical.gnss.data.AbstractNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.BeidouCivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.BeidouLegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.BeidouSatelliteType; +import org.orekit.propagation.analytical.gnss.data.CivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GPSCivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GPSLegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.IRNSSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.LegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.QZSSCivilianNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.QZSSLegacyNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.SBASNavigationMessage; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.GNSSDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScales; +import org.orekit.utils.Constants; +import org.orekit.utils.units.Unit; + +/** + * Parser for RINEX navigation messages files. + *

          + * This parser handles RINEX version from 2 to 4.00. + *

          + * @see
          rinex 2.0 + * @see rinex 2.10 + * @see rinex 2.11 + * @see 3.01 navigation messages file format + * @see 3.02 navigation messages file format + * @see 3.03 navigation messages file format + * @see 3.04 navigation messages file format + * @see 3.05 navigation messages file format + * @see 4.00 navigation messages file format + * + * @author Bryan Cazabonne + * @since 11.0 + * + */ +public class RinexNavigationParser { + + /** Converter for positions. */ + private static final Unit KM = Unit.KILOMETRE; + + /** Converter for velocities. */ + private static final Unit KM_PER_S = Unit.parse("km/s"); + + /** Converter for accelerations. */ + private static final Unit KM_PER_S2 = Unit.parse("km/s²");; + + /** Converter for velocities. */ + private static final Unit M_PER_S = Unit.parse("m/s"); + + /** Converter for clock drift. */ + private static final Unit S_PER_S = Unit.parse("s/s"); + + /** Converter for clock drift rate. */ + private static final Unit S_PER_S2 = Unit.parse("s/s²"); + + /** Converter for ΔUT₁ first derivative. */ + private static final Unit S_PER_DAY = Unit.parse("s/d"); + + /** Converter for ΔUT₁ second derivative. */ + private static final Unit S_PER_DAY2 = Unit.parse("s/d²"); + + /** Converter for square root of semi-major axis. */ + private static final Unit SQRT_M = Unit.parse("√m"); + + /** Converter for angular rates. */ + private static final Unit RAD_PER_S = Unit.parse("rad/s");; + + /** Converter for angular accelerations. */ + private static final Unit RAD_PER_S2 = Unit.parse("rad/s²");; + + /** Converter for rates of small angle. */ + private static final Unit AS_PER_DAY = Unit.parse("as/d");; + + /** Converter for accelerations of small angles. */ + private static final Unit AS_PER_DAY2 = Unit.parse("as/d²");; + + /** System initials. */ + private static final String INITIALS = "GRECIJS"; + + /** Set of time scales. */ + private final TimeScales timeScales; + + /** + * Constructor. + *

          This constructor uses the {@link DataContext#getDefault() default data context}.

          + * @see #RinexNavigationParser(TimeScales) + * + */ + @DefaultDataContext + public RinexNavigationParser() { + this(DataContext.getDefault().getTimeScales()); + } + + /** + * Constructor. + * @param timeScales the set of time scales used for parsing dates. + */ + public RinexNavigationParser(final TimeScales timeScales) { + this.timeScales = timeScales; + } + + /** + * Parse RINEX navigation messages. + * @param source source providing the data to parse + * @return a parsed RINEX navigation messages file + * @throws IOException if {@code reader} throws one + */ + public RinexNavigation parse(final DataSource source) throws IOException { + + // initialize internal data structures + final ParseInfo pi = new ParseInfo(source.getName()); + + Iterable candidateParsers = Collections.singleton(LineParser.HEADER_VERSION); + try (Reader reader = source.getOpener().openReaderOnce(); + BufferedReader br = new BufferedReader(reader)) { + nextLine: + for (String line = br.readLine(); line != null; line = br.readLine()) { + ++pi.lineNumber; + for (final LineParser candidate : candidateParsers) { + if (candidate.canHandle.test(line)) { + try { + candidate.parsingMethod.parse(line, pi); + candidateParsers = candidate.allowedNextProvider.apply(pi); + continue nextLine; + } catch (StringIndexOutOfBoundsException | NumberFormatException | InputMismatchException e) { + throw new OrekitException(e, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + pi.lineNumber, source.getName(), line); + } + } + } + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + pi.lineNumber, source.getName(), line); + } + } + + if (!pi.headerParsed) { + throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE, source.getName()); + } + + pi.closePendingMessage(); + + return pi.file; + + } + + /** Transient data used for parsing a RINEX navigation messages file. */ + private class ParseInfo { + + /** Name of the data source. */ + private final String name; + + /** Set of time scales for parsing dates. */ + private final TimeScales timeScales; + + /** The corresponding navigation messages file object. */ + private RinexNavigation file; + + /** Number of initial spaces in broadcase orbits lines. */ + private int initialSpaces; + + /** Flag indicating header has been completely parsed. */ + private boolean headerParsed; + + /** Flag indicating the distinction between "alpha" and "beta" ionospheric coefficients. */ + private boolean isIonosphereAlphaInitialized; + + /** Satellite system line parser. */ + private SatelliteSystemLineParser systemLineParser; + + /** Current global line number. */ + private int lineNumber; + + /** Current line number within the navigation message. */ + private int messageLineNumber; + + /** Container for GPS navigation message. */ + private GPSLegacyNavigationMessage gpsLNav; + + /** Container for GPS navigation message. */ + private GPSCivilianNavigationMessage gpsCNav; + + /** Container for Galileo navigation message. */ + private GalileoNavigationMessage galileoNav; + + /** Container for Beidou navigation message. */ + private BeidouLegacyNavigationMessage beidouLNav; + + /** Container for Beidou navigation message. */ + private BeidouCivilianNavigationMessage beidouCNav; + + /** Container for QZSS navigation message. */ + private QZSSLegacyNavigationMessage qzssLNav; + + /** Container for QZSS navigation message. */ + private QZSSCivilianNavigationMessage qzssCNav; + + /** Container for IRNSS navigation message. */ + private IRNSSNavigationMessage irnssNav; + + /** Container for GLONASS navigation message. */ + private GLONASSNavigationMessage glonassNav; + + /** Container for SBAS navigation message. */ + private SBASNavigationMessage sbasNav; + + /** Container for System Time Offset message. */ + private SystemTimeOffsetMessage sto; + + /** Container for Earth Orientation Parameter message. */ + private EarthOrientationParameterMessage eop; + + /** Container for ionosphere Klobuchar message. */ + private IonosphereKlobucharMessage klobuchar; + + /** Container for ionosphere Nequick-G message. */ + private IonosphereNequickGMessage nequickG; + + /** Container for ionosphere BDGIM message. */ + private IonosphereBDGIMMessage bdgim; + + /** Constructor, build the ParseInfo object. + * @param name name of the data source + */ + ParseInfo(final String name) { + // Initialize default values for fields + this.name = name; + this.timeScales = RinexNavigationParser.this.timeScales; + this.isIonosphereAlphaInitialized = false; + this.file = new RinexNavigation(); + + } + + /** Ensure navigation message has been closed. + */ + void closePendingMessage() { + if (systemLineParser != null) { + systemLineParser.closeMessage(this); + systemLineParser = null; + } + + } + + } + + /** Parsers for specific lines. */ + private enum LineParser { + + /** Parser for version, file type and satellite system. */ + HEADER_VERSION(line -> RinexUtils.matchesLabel(line, "RINEX VERSION / TYPE"), + (line, pi) -> { + RinexUtils.parseVersionFileTypeSatelliteSystem(line, pi.name, pi.file.getHeader(), + 2.0, 2.01, 2.10, 2.11, + 3.01, 3.02, 3.03, 3.04, 3.05, + 4.00); + pi.initialSpaces = pi.file.getHeader().getFormatVersion() < 3.0 ? 3 : 4; + }, + LineParser::headerNext), + + /** Parser for generating program and emitting agency. */ + HEADER_PROGRAM(line -> RinexUtils.matchesLabel(line, "PGM / RUN BY / DATE"), + (line, pi) -> RinexUtils.parseProgramRunByDate(line, pi.lineNumber, pi.name, pi.timeScales, pi.file.getHeader()), + LineParser::headerNext), + + /** Parser for comments. */ + HEADER_COMMENT(line -> RinexUtils.matchesLabel(line, "COMMENT"), + (line, pi) -> RinexUtils.parseComment(pi.lineNumber, line, pi.file), + LineParser::headerNext), + + /** Parser for ionospheric correction parameters. */ + HEADER_ION_ALPHA(line -> RinexUtils.matchesLabel(line, "ION ALPHA"), + (line, pi) -> { + + pi.file.getHeader().setIonosphericCorrectionType(IonosphericCorrectionType.GPS); + + // Read coefficients + final double[] parameters = new double[4]; + parameters[0] = RinexUtils.parseDouble(line, 2, 12); + parameters[1] = RinexUtils.parseDouble(line, 14, 12); + parameters[2] = RinexUtils.parseDouble(line, 26, 12); + parameters[3] = RinexUtils.parseDouble(line, 38, 12); + pi.file.setKlobucharAlpha(parameters); + pi.isIonosphereAlphaInitialized = true; + + }, + LineParser::headerNext), + + /** Parser for ionospheric correction parameters. */ + HEADER_ION_BETA(line -> RinexUtils.matchesLabel(line, "ION BETA"), + (line, pi) -> { + + pi.file.getHeader().setIonosphericCorrectionType(IonosphericCorrectionType.GPS); + + // Read coefficients + final double[] parameters = new double[4]; + parameters[0] = RinexUtils.parseDouble(line, 2, 12); + parameters[1] = RinexUtils.parseDouble(line, 14, 12); + parameters[2] = RinexUtils.parseDouble(line, 26, 12); + parameters[3] = RinexUtils.parseDouble(line, 38, 12); + pi.file.setKlobucharBeta(parameters); + + }, + LineParser::headerNext), + + /** Parser for ionospheric correction parameters. */ + HEADER_IONOSPHERIC(line -> RinexUtils.matchesLabel(line, "IONOSPHERIC CORR"), + (line, pi) -> { + + // ionospheric correction type + final IonosphericCorrectionType ionoType = + IonosphericCorrectionType.valueOf(RinexUtils.parseString(line, 0, 3)); + pi.file.getHeader().setIonosphericCorrectionType(ionoType); + + // Read coefficients + final double[] parameters = new double[4]; + parameters[0] = RinexUtils.parseDouble(line, 5, 12); + parameters[1] = RinexUtils.parseDouble(line, 17, 12); + parameters[2] = RinexUtils.parseDouble(line, 29, 12); + parameters[3] = RinexUtils.parseDouble(line, 41, 12); + + // Verify if we are parsing Galileo ionospheric parameters + if (ionoType == IonosphericCorrectionType.GAL) { + + // We are parsing Galileo ionospheric parameters + pi.file.setNeQuickAlpha(parameters); + + } else { + // We are parsing Klobuchar ionospheric parameters + + // Verify if we are parsing "alpha" or "beta" ionospheric parameters + if (pi.isIonosphereAlphaInitialized) { + + // Ionospheric "beta" parameters + pi.file.setKlobucharBeta(parameters); + + } else { + + // Ionospheric "alpha" parameters + pi.file.setKlobucharAlpha(parameters); + + // Set the flag to true + pi.isIonosphereAlphaInitialized = true; + + } + + } + + }, + LineParser::headerNext), + + /** Parser for corrections to transform the system time to UTC or to other time systems. */ + HEADER_DELTA_UTC(line -> RinexUtils.matchesLabel(line, "DELTA-UTC: A0,A1,T,W"), + (line, pi) -> { + // Read fields + final double a0 = RinexUtils.parseDouble(line, 3, 19); + final double a1 = RinexUtils.parseDouble(line, 22, 19); + final int refTime = RinexUtils.parseInt(line, 41, 9); + final int refWeek = RinexUtils.parseInt(line, 50, 9); + + // convert date + final SatelliteSystem satSystem = pi.file.getHeader().getSatelliteSystem(); + final AbsoluteDate date = new GNSSDate(refWeek, refTime, satSystem, pi.timeScales).getDate(); + + // Add to the list + final TimeSystemCorrection tsc = new TimeSystemCorrection("GPUT", date, a0, a1); + pi.file.getHeader().addTimeSystemCorrections(tsc); + }, + LineParser::headerNext), + + /** Parser for corrections to transform the GLONASS system time to UTC or to other time systems. */ + HEADER_CORR_SYSTEM_TIME(line -> RinexUtils.matchesLabel(line, "CORR TO SYSTEM TIME"), + (line, pi) -> { + // Read fields + final int year = RinexUtils.parseInt(line, 0, 6); + final int month = RinexUtils.parseInt(line, 6, 6); + final int day = RinexUtils.parseInt(line, 12, 6); + final double minusTau = RinexUtils.parseDouble(line, 21, 19); + + // convert date + final SatelliteSystem satSystem = pi.file.getHeader().getSatelliteSystem(); + final TimeScale timeScale = satSystem.getObservationTimeScale().getTimeScale(pi.timeScales); + final AbsoluteDate date = new AbsoluteDate(year, month, day, timeScale); + + // Add to the list + final TimeSystemCorrection tsc = new TimeSystemCorrection("GLUT", date, minusTau, 0.0); + pi.file.getHeader().addTimeSystemCorrections(tsc); + + }, + LineParser::headerNext), + + /** Parser for corrections to transform the system time to UTC or to other time systems. */ + HEADER_TIME(line -> RinexUtils.matchesLabel(line, "TIME SYSTEM CORR"), + (line, pi) -> { + + // Read fields + final String type = RinexUtils.parseString(line, 0, 4); + final double a0 = RinexUtils.parseDouble(line, 5, 17); + final double a1 = RinexUtils.parseDouble(line, 22, 16); + final int refTime = RinexUtils.parseInt(line, 38, 7); + final int refWeek = RinexUtils.parseInt(line, 46, 5); + + // convert date + final SatelliteSystem satSystem = pi.file.getHeader().getSatelliteSystem(); + final AbsoluteDate date; + if (satSystem == SatelliteSystem.GLONASS) { + date = null; + } else if (satSystem == SatelliteSystem.BEIDOU) { + date = new GNSSDate(refWeek, refTime, satSystem, pi.timeScales).getDate(); + } else { + // all other systems are converted to GPS week in Rinex files! + date = new GNSSDate(refWeek, refTime, SatelliteSystem.GPS, pi.timeScales).getDate(); + } + + // Add to the list + final TimeSystemCorrection tsc = new TimeSystemCorrection(type, date, a0, a1); + pi.file.getHeader().addTimeSystemCorrections(tsc); + + }, + LineParser::headerNext), + + /** Parser for leap seconds. */ + HEADER_LEAP_SECONDS(line -> RinexUtils.matchesLabel(line, "LEAP SECONDS"), + (line, pi) -> pi.file.getHeader().setNumberOfLeapSeconds(RinexUtils.parseInt(line, 0, 6)), + LineParser::headerNext), + + /** Parser for DOI. + * @since 12.0 + */ + HEADER_DOI(line -> RinexUtils.matchesLabel(line, "DOI"), + (line, pi) -> pi.file.getHeader().setDoi(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for license. + * @since 12.0 + */ + HEADER_LICENSE(line -> RinexUtils.matchesLabel(line, "LICENSE OF USE"), + (line, pi) -> pi.file.getHeader().setLicense(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for stationInformation. + * @since 12.0 + */ + HEADER_STATION_INFORMATION(line -> RinexUtils.matchesLabel(line, "STATION INFORMATION"), + (line, pi) -> pi.file.getHeader().setStationInformation(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for merged files. + * @since 12.0 + */ + HEADER_MERGED_FILE(line -> RinexUtils.matchesLabel(line, "MERGED FILE"), + (line, pi) -> pi.file.getHeader().setMergedFiles(RinexUtils.parseInt(line, 0, 9)), + LineParser::headerNext), + + /** Parser for the end of header. */ + HEADER_END(line -> RinexUtils.matchesLabel(line, "END OF HEADER"), + (line, pi) -> { + // get rinex format version + final RinexNavigationHeader header = pi.file.getHeader(); + final double version = header.getFormatVersion(); + + // check mandatory header fields + if (header.getRunByName() == null || + version >= 4 && header.getNumberOfLeapSeconds() < 0) { + throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, pi.name); + } + + pi.headerParsed = true; + + }, + LineParser::navigationNext), + + /** Parser for navigation message space vehicle epoch and clock. */ + NAVIGATION_SV_EPOCH_CLOCK_RINEX_2(line -> true, + (line, pi) -> { + + // Set the line number to 0 + pi.messageLineNumber = 0; + + // Initialize parser + pi.closePendingMessage(); + pi.systemLineParser = SatelliteSystemLineParser.getParser(pi.file.getHeader().getSatelliteSystem(), + null, pi, line); + + pi.systemLineParser.parseSvEpochSvClockLine(line, pi); + + }, + LineParser::navigationNext), + + /** Parser for navigation message space vehicle epoch and clock. */ + NAVIGATION_SV_EPOCH_CLOCK(line -> INITIALS.indexOf(line.charAt(0)) >= 0, + (line, pi) -> { + + // Set the line number to 0 + pi.messageLineNumber = 0; + + if (pi.file.getHeader().getFormatVersion() < 4) { + // Current satellite system + final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)); + + // Initialize parser + pi.closePendingMessage(); + pi.systemLineParser = SatelliteSystemLineParser.getParser(system, null, pi, line); + } + + // Read first line + pi.systemLineParser.parseSvEpochSvClockLine(line, pi); + + }, + LineParser::navigationNext), + + /** Parser for navigation message type. */ + EPH_TYPE(line -> line.startsWith("> EPH"), + (line, pi) -> { + final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 6, 1)); + final String type = RinexUtils.parseString(line, 10, 4); + pi.closePendingMessage(); + pi.systemLineParser = SatelliteSystemLineParser.getParser(system, type, pi, line); + }, + pi -> Collections.singleton(NAVIGATION_SV_EPOCH_CLOCK)), + + /** Parser for broadcast orbit. */ + BROADCAST_ORBIT(line -> line.startsWith(" "), + (line, pi) -> { + switch (++pi.messageLineNumber) { + case 1: pi.systemLineParser.parseFirstBroadcastOrbit(line, pi); + break; + case 2: pi.systemLineParser.parseSecondBroadcastOrbit(line, pi); + break; + case 3: pi.systemLineParser.parseThirdBroadcastOrbit(line, pi); + break; + case 4: pi.systemLineParser.parseFourthBroadcastOrbit(line, pi); + break; + case 5: pi.systemLineParser.parseFifthBroadcastOrbit(line, pi); + break; + case 6: pi.systemLineParser.parseSixthBroadcastOrbit(line, pi); + break; + case 7: pi.systemLineParser.parseSeventhBroadcastOrbit(line, pi); + break; + case 8: pi.systemLineParser.parseEighthBroadcastOrbit(line, pi); + break; + case 9: pi.systemLineParser.parseNinthBroadcastOrbit(line, pi); + break; + default: + // this should never happen + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + pi.lineNumber, pi.name, line); + } + + }, + LineParser::navigationNext), + + /** Parser for system time offset message model. */ + STO_LINE_1(line -> true, + (line, pi) -> { + pi.sto.setTransmissionTime(Unit.SECOND.toSI(RinexUtils.parseDouble(line, 4, 19))); + pi.sto.setA0(Unit.SECOND.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.sto.setA1(S_PER_S.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.sto.setA2(S_PER_S2.toSI(RinexUtils.parseDouble(line, 61, 19))); + pi.file.addSystemTimeOffset(pi.sto); + pi.sto = null; + }, + LineParser::navigationNext), + + /** Parser for system time offset message space vehicle epoch and clock. */ + STO_SV_EPOCH_CLOCK(line -> true, + (line, pi) -> { + + pi.sto.setDefinedTimeSystem(TimeSystem.parseTwoLettersCode(RinexUtils.parseString(line, 24, 2))); + pi.sto.setReferenceTimeSystem(TimeSystem.parseTwoLettersCode(RinexUtils.parseString(line, 26, 2))); + final String sbas = RinexUtils.parseString(line, 43, 18); + pi.sto.setSbasId(sbas.length() > 0 ? SbasId.valueOf(sbas) : null); + final String utc = RinexUtils.parseString(line, 62, 18); + pi.sto.setUtcId(utc.length() > 0 ? UtcId.parseUtcId(utc) : null); + + // TODO is the reference date relative to one or the other time scale? + final int year = RinexUtils.parseInt(line, 4, 4); + final int month = RinexUtils.parseInt(line, 9, 2); + final int day = RinexUtils.parseInt(line, 12, 2); + final int hours = RinexUtils.parseInt(line, 15, 2); + final int min = RinexUtils.parseInt(line, 18, 2); + final int sec = RinexUtils.parseInt(line, 21, 2); + pi.sto.setReferenceEpoch(new AbsoluteDate(year, month, day, hours, min, sec, + pi.sto.getDefinedTimeSystem().getTimeScale(pi.timeScales))); + + }, + pi -> Collections.singleton(STO_LINE_1)), + + /** Parser for system time offset message type. */ + STO_TYPE(line -> line.startsWith("> STO"), + (line, pi) -> { + pi.closePendingMessage(); + pi.sto = new SystemTimeOffsetMessage(SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 6, 1)), + RinexUtils.parseInt(line, 7, 2), + RinexUtils.parseString(line, 10, 4)); + }, + pi -> Collections.singleton(STO_SV_EPOCH_CLOCK)), + + /** Parser for Earth orientation parameter message model. */ + EOP_LINE_2(line -> true, + (line, pi) -> { + pi.eop.setTransmissionTime(Unit.SECOND.toSI(RinexUtils.parseDouble(line, 4, 19))); + pi.eop.setDut1(Unit.SECOND.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.eop.setDut1Dot(S_PER_DAY.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.eop.setDut1DotDot(S_PER_DAY2.toSI(RinexUtils.parseDouble(line, 61, 19))); + pi.file.addEarthOrientationParameter(pi.eop); + pi.eop = null; + }, + LineParser::navigationNext), + + /** Parser for Earth orientation parameter message model. */ + EOP_LINE_1(line -> true, + (line, pi) -> { + pi.eop.setYp(Unit.ARC_SECOND.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.eop.setYpDot(AS_PER_DAY.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.eop.setYpDotDot(AS_PER_DAY2.toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(EOP_LINE_2)), + + /** Parser for Earth orientation parameter message space vehicle epoch and clock. */ + EOP_SV_EPOCH_CLOCK(line -> true, + (line, pi) -> { + final int year = RinexUtils.parseInt(line, 4, 4); + final int month = RinexUtils.parseInt(line, 9, 2); + final int day = RinexUtils.parseInt(line, 12, 2); + final int hours = RinexUtils.parseInt(line, 15, 2); + final int min = RinexUtils.parseInt(line, 18, 2); + final int sec = RinexUtils.parseInt(line, 21, 2); + pi.eop.setReferenceEpoch(new AbsoluteDate(year, month, day, hours, min, sec, + pi.eop.getSystem().getObservationTimeScale().getTimeScale(pi.timeScales))); + pi.eop.setXp(Unit.ARC_SECOND.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.eop.setXpDot(AS_PER_DAY.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.eop.setXpDotDot(AS_PER_DAY2.toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(EOP_LINE_1)), + + /** Parser for Earth orientation parameter message type. */ + EOP_TYPE(line -> line.startsWith("> EOP"), + (line, pi) -> { + pi.closePendingMessage(); + pi.eop = new EarthOrientationParameterMessage(SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 6, 1)), + RinexUtils.parseInt(line, 7, 2), + RinexUtils.parseString(line, 10, 4)); + }, + pi -> Collections.singleton(EOP_SV_EPOCH_CLOCK)), + + /** Parser for ionosphere Klobuchar message model. */ + KLOBUCHAR_LINE_2(line -> true, + (line, pi) -> { + pi.klobuchar.setBetaI(3, IonosphereKlobucharMessage.S_PER_SC_N[3].toSI(RinexUtils.parseDouble(line, 4, 19))); + pi.klobuchar.setRegionCode(RinexUtils.parseDouble(line, 23, 19) < 0.5 ? + RegionCode.WIDE_AREA : RegionCode.JAPAN); + pi.file.addKlobucharMessage(pi.klobuchar); + pi.klobuchar = null; + }, + LineParser::navigationNext), + + /** Parser for ionosphere Klobuchar message model. */ + KLOBUCHAR_LINE_1(line -> true, + (line, pi) -> { + pi.klobuchar.setAlphaI(3, IonosphereKlobucharMessage.S_PER_SC_N[3].toSI(RinexUtils.parseDouble(line, 4, 19))); + pi.klobuchar.setBetaI(0, IonosphereKlobucharMessage.S_PER_SC_N[0].toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.klobuchar.setBetaI(1, IonosphereKlobucharMessage.S_PER_SC_N[1].toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.klobuchar.setBetaI(2, IonosphereKlobucharMessage.S_PER_SC_N[2].toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(KLOBUCHAR_LINE_2)), + + /** Parser for ionosphere Klobuchar message model. */ + KLOBUCHAR_LINE_0(line -> true, + (line, pi) -> { + final int year = RinexUtils.parseInt(line, 4, 4); + final int month = RinexUtils.parseInt(line, 9, 2); + final int day = RinexUtils.parseInt(line, 12, 2); + final int hours = RinexUtils.parseInt(line, 15, 2); + final int min = RinexUtils.parseInt(line, 18, 2); + final int sec = RinexUtils.parseInt(line, 21, 2); + pi.klobuchar.setTransmitTime(new AbsoluteDate(year, month, day, hours, min, sec, + pi.klobuchar.getSystem().getObservationTimeScale().getTimeScale(pi.timeScales))); + pi.klobuchar.setAlphaI(0, IonosphereKlobucharMessage.S_PER_SC_N[0].toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.klobuchar.setAlphaI(1, IonosphereKlobucharMessage.S_PER_SC_N[1].toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.klobuchar.setAlphaI(2, IonosphereKlobucharMessage.S_PER_SC_N[2].toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(KLOBUCHAR_LINE_1)), + + /** Parser for ionosphere Nequick-G message model. */ + NEQUICK_LINE_1(line -> true, + (line, pi) -> { + pi.nequickG.setFlags((int) FastMath.rint(RinexUtils.parseDouble(line, 4, 19))); + pi.file.addNequickGMessage(pi.nequickG); + pi.nequickG = null; + }, + LineParser::navigationNext), + + /** Parser for ionosphere Nequick-G message model. */ + NEQUICK_LINE_0(line -> true, + (line, pi) -> { + final int year = RinexUtils.parseInt(line, 4, 4); + final int month = RinexUtils.parseInt(line, 9, 2); + final int day = RinexUtils.parseInt(line, 12, 2); + final int hours = RinexUtils.parseInt(line, 15, 2); + final int min = RinexUtils.parseInt(line, 18, 2); + final int sec = RinexUtils.parseInt(line, 21, 2); + pi.nequickG.setTransmitTime(new AbsoluteDate(year, month, day, hours, min, sec, + pi.nequickG.getSystem().getObservationTimeScale().getTimeScale(pi.timeScales))); + pi.nequickG.setAi0(IonosphereNequickGMessage.SFU.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.nequickG.setAi1(IonosphereNequickGMessage.SFU_PER_DEG.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.nequickG.setAi2(IonosphereNequickGMessage.SFU_PER_DEG2.toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(NEQUICK_LINE_1)), + + /** Parser for ionosphere BDGIM message model. */ + BDGIM_LINE_2(line -> true, + (line, pi) -> { + pi.bdgim.setAlphaI(7, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 4, 19))); + pi.bdgim.setAlphaI(8, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.file.addBDGIMMessage(pi.bdgim); + pi.bdgim = null; + }, + LineParser::navigationNext), + + /** Parser for ionosphere BDGIM message model. */ + BDGIM_LINE_1(line -> true, + (line, pi) -> { + pi.bdgim.setAlphaI(3, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 4, 19))); + pi.bdgim.setAlphaI(4, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.bdgim.setAlphaI(5, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.bdgim.setAlphaI(6, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(BDGIM_LINE_2)), + + /** Parser for ionosphere BDGIM message model. */ + BDGIM_LINE_0(line -> true, + (line, pi) -> { + final int year = RinexUtils.parseInt(line, 4, 4); + final int month = RinexUtils.parseInt(line, 9, 2); + final int day = RinexUtils.parseInt(line, 12, 2); + final int hours = RinexUtils.parseInt(line, 15, 2); + final int min = RinexUtils.parseInt(line, 18, 2); + final int sec = RinexUtils.parseInt(line, 21, 2); + pi.bdgim.setTransmitTime(new AbsoluteDate(year, month, day, hours, min, sec, + pi.bdgim.getSystem().getObservationTimeScale().getTimeScale(pi.timeScales))); + pi.bdgim.setAlphaI(0, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 23, 19))); + pi.bdgim.setAlphaI(1, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 42, 19))); + pi.bdgim.setAlphaI(2, Unit.TOTAL_ELECTRON_CONTENT_UNIT.toSI(RinexUtils.parseDouble(line, 61, 19))); + }, + pi -> Collections.singleton(BDGIM_LINE_1)), + + /** Parser for ionosphere message type. */ + IONO_TYPE(line -> line.startsWith("> ION"), + (line, pi) -> { + pi.closePendingMessage(); + final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 6, 1)); + final int prn = RinexUtils.parseInt(line, 7, 2); + final String type = RinexUtils.parseString(line, 10, 4); + if (system == SatelliteSystem.GALILEO) { + pi.nequickG = new IonosphereNequickGMessage(system, prn, type); + } else { + // in Rinex 4.00, tables A32 and A34 are ambiguous as both seem to apply + // to Beidou CNVX messages, we consider BDGIM is the proper model in this case + if (system == SatelliteSystem.BEIDOU && "CNVX".equals(type)) { + pi.bdgim = new IonosphereBDGIMMessage(system, prn, type); + } else { + pi.klobuchar = new IonosphereKlobucharMessage(system, prn, type); + } + } + }, + pi -> Collections.singleton(pi.nequickG != null ? NEQUICK_LINE_0 : (pi.bdgim != null ? BDGIM_LINE_0 : KLOBUCHAR_LINE_0))); + + /** Predicate for identifying lines that can be parsed. */ + private final Predicate canHandle; + + /** Parsing method. */ + private final ParsingMethod parsingMethod; + + /** Provider for next line parsers. */ + private final Function> allowedNextProvider; + + /** Simple constructor. + * @param canHandle predicate for identifying lines that can be parsed + * @param parsingMethod parsing method + * @param allowedNextProvider supplier for allowed parsers for next line + */ + LineParser(final Predicate canHandle, final ParsingMethod parsingMethod, + final Function> allowedNextProvider) { + this.canHandle = canHandle; + this.parsingMethod = parsingMethod; + this.allowedNextProvider = allowedNextProvider; + } + + /** Get the allowed parsers for next lines while parsing Rinex header. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable headerNext(final ParseInfo parseInfo) { + if (parseInfo.file.getHeader().getFormatVersion() < 3) { + // Rinex 2.x header entries + return Arrays.asList(HEADER_COMMENT, HEADER_PROGRAM, + HEADER_ION_ALPHA, HEADER_ION_BETA, + HEADER_DELTA_UTC, HEADER_CORR_SYSTEM_TIME, + HEADER_LEAP_SECONDS, HEADER_END); + } else if (parseInfo.file.getHeader().getFormatVersion() < 4) { + // Rinex 3.x header entries + return Arrays.asList(HEADER_COMMENT, HEADER_PROGRAM, + HEADER_IONOSPHERIC, HEADER_TIME, + HEADER_LEAP_SECONDS, HEADER_END); + } else { + // Rinex 4.x header entries + return Arrays.asList(HEADER_COMMENT, HEADER_PROGRAM, + HEADER_DOI, HEADER_LICENSE, HEADER_STATION_INFORMATION, HEADER_MERGED_FILE, + HEADER_LEAP_SECONDS, HEADER_END); + } + } + + /** Get the allowed parsers for next lines while parsing navigation date. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable navigationNext(final ParseInfo parseInfo) { + if (parseInfo.gpsLNav != null || parseInfo.gpsCNav != null || parseInfo.galileoNav != null || + parseInfo.beidouLNav != null || parseInfo.beidouCNav != null || parseInfo.qzssLNav != null || + parseInfo.qzssCNav != null || parseInfo.irnssNav != null || parseInfo.sbasNav != null) { + return Collections.singleton(BROADCAST_ORBIT); + } else if (parseInfo.glonassNav != null) { + if (parseInfo.messageLineNumber < 3) { + return Collections.singleton(BROADCAST_ORBIT); + } else { + // workaround for some invalid files that should nevertheless be parsed + // we have encountered in the wild merged files that claimed to be in 3.05 version + // and hence needed at least 4 broadcast GLONASS orbit lines (the fourth line was + // introduced in 3.05), but in fact only had 3 broadcast lines. We think they were + // merged from files in 3.04 or earlier format. In order to parse these files, + // we accept after the third line either another broadcast orbit line or a new message + if (parseInfo.file.getHeader().getFormatVersion() < 4) { + return Arrays.asList(BROADCAST_ORBIT, NAVIGATION_SV_EPOCH_CLOCK); + } else { + return Arrays.asList(BROADCAST_ORBIT, EPH_TYPE, STO_TYPE, EOP_TYPE, IONO_TYPE); + } + } + } else if (parseInfo.file.getHeader().getFormatVersion() < 3) { + return Collections.singleton(NAVIGATION_SV_EPOCH_CLOCK_RINEX_2); + } else if (parseInfo.file.getHeader().getFormatVersion() < 4) { + return Collections.singleton(NAVIGATION_SV_EPOCH_CLOCK); + } else { + return Arrays.asList(EPH_TYPE, STO_TYPE, EOP_TYPE, IONO_TYPE); + } + } + + } + + /** Parsers for satellite system specific lines. */ + private enum SatelliteSystemLineParser { + + /** GPS legacy. */ + GPS_LNAV() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + if (pi.file.getHeader().getFormatVersion() < 3.0) { + parseSvEpochSvClockLineRinex2(line, pi.timeScales.getGPS(), pi.gpsLNav); + } else { + parseSvEpochSvClockLine(line, pi.timeScales.getGPS(), pi.gpsLNav); + } + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsLNav.setIODE(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsLNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.gpsLNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.gpsLNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsLNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsLNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.gpsLNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsLNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsLNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsLNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsLNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsLNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsLNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsLNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.gpsLNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsLNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + // iDot + pi.gpsLNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + // Codes on L2 channel (ignored) + // RinexUtils.parseDouble(line, 23, 19) + // GPS week (to go with Toe) + pi.gpsLNav.setWeek((int) RinexUtils.parseDouble(line, 42, 19)); + pi.gpsLNav.setDate(new GNSSDate(pi.gpsLNav.getWeek(), + pi.gpsLNav.getTime(), + SatelliteSystem.GPS, + pi.timeScales).getDate()); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsLNav.setSvAccuracy(parseBroadcastDouble1(line, pi.initialSpaces, Unit.METRE)); + pi.gpsLNav.setSvHealth(parseBroadcastInt2(line, pi.initialSpaces)); + pi.gpsLNav.setTGD(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsLNav.setIODC(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsLNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsLNav.setFitInterval(parseBroadcastInt2(line, pi.initialSpaces)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addGPSLegacyNavigationMessage(pi.gpsLNav); + pi.gpsLNav = null; + } + + }, + + /** GPS civilian. + * @since 12.0 + */ + GPS_CNAV() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getGPS(), pi.gpsCNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setADot(parseBroadcastDouble1(line, pi.initialSpaces, M_PER_S)); + pi.gpsCNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.gpsCNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.gpsCNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsCNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.gpsCNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsCNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsCNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsCNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsCNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsCNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.gpsCNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.gpsCNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + pi.gpsCNav.setDeltaN0Dot(parseBroadcastDouble2(line, pi.initialSpaces, RAD_PER_S2)); + pi.gpsCNav.setUraiNed0(parseBroadcastInt3(line, pi.initialSpaces)); + pi.gpsCNav.setUraiNed1(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setUraiEd(parseBroadcastInt1(line, pi.initialSpaces)); + pi.gpsCNav.setSvHealth(parseBroadcastInt2(line, pi.initialSpaces)); + pi.gpsCNav.setTGD(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsCNav.setUraiNed2(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.gpsCNav.setIscL1CA(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsCNav.setIscL2C(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsCNav.setIscL5I5(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsCNav.setIscL5Q5(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } + + /** {@inheritDoc} */ + @Override + public void parseEighthBroadcastOrbit(final String line, final ParseInfo pi) { + if (pi.gpsCNav.isCnv2()) { + // in CNAV2 messages, there is an additional line for L1 CD and L1 CP inter signal delay + pi.gpsCNav.setIscL1CD(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.gpsCNav.setIscL1CP(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + } else { + parseTransmissionTimeLine(line, pi); + } + } + + /** {@inheritDoc} */ + @Override + public void parseNinthBroadcastOrbit(final String line, final ParseInfo pi) { + parseTransmissionTimeLine(line, pi); + } + + /** Parse transmission time line. + * @param line line to parse + * @param pi holder for transient data + */ + private void parseTransmissionTimeLine(final String line, final ParseInfo pi) { + pi.gpsCNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addGPSLegacyNavigationMessage(pi.gpsCNav); + pi.gpsCNav = null; + } + + }, + + /** Galileo. */ + GALILEO() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getGPS(), pi.galileoNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.galileoNav.setIODNav(parseBroadcastInt1(line, pi.initialSpaces)); + pi.galileoNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.galileoNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.galileoNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.galileoNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.galileoNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.galileoNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.galileoNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.galileoNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.galileoNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.galileoNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.galileoNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.galileoNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.galileoNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.galileoNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.galileoNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + // iDot + pi.galileoNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + pi.galileoNav.setDataSource(parseBroadcastInt2(line, pi.initialSpaces)); + // GAL week (to go with Toe) + pi.galileoNav.setWeek(parseBroadcastInt3(line, pi.initialSpaces)); + pi.galileoNav.setDate(new GNSSDate(pi.galileoNav.getWeek(), + pi.galileoNav.getTime(), + SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week! + pi.timeScales).getDate()); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.galileoNav.setSisa(parseBroadcastDouble1(line, pi.initialSpaces, Unit.METRE)); + pi.galileoNav.setSvHealth(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.galileoNav.setBGDE1E5a(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.galileoNav.setBGDE5bE1(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.galileoNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addGalileoNavigationMessage(pi.galileoNav); + pi.galileoNav = null; + } + + }, + + /** Glonass. */ + GLONASS() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + + if (pi.file.getHeader().getFormatVersion() < 3.0) { + + pi.glonassNav.setPRN(RinexUtils.parseInt(line, 0, 2)); + + // Toc + final int year = RinexUtils.convert2DigitsYear(RinexUtils.parseInt(line, 3, 2)); + final int month = RinexUtils.parseInt(line, 6, 2); + final int day = RinexUtils.parseInt(line, 9, 2); + final int hours = RinexUtils.parseInt(line, 12, 2); + final int min = RinexUtils.parseInt(line, 15, 2); + final double sec = RinexUtils.parseDouble(line, 17, 5); + pi.glonassNav.setEpochToc(new AbsoluteDate(year, month, day, hours, min, sec, + pi.timeScales.getUTC())); + + // clock + pi.glonassNav.setTauN(-RinexUtils.parseDouble(line, 22, 19)); + pi.glonassNav.setGammaN(RinexUtils.parseDouble(line, 41, 19)); + pi.glonassNav.setTime(fmod(RinexUtils.parseDouble(line, 60, 19), Constants.JULIAN_DAY)); + + // Set the ephemeris epoch (same as time of clock epoch) + pi.glonassNav.setDate(pi.glonassNav.getEpochToc()); + + } else { + pi.glonassNav.setPRN(RinexUtils.parseInt(line, 1, 2)); + + // Toc + pi.glonassNav.setEpochToc(parsePrnSvEpochClock(line, pi.timeScales.getUTC())); + + // clock + pi.glonassNav.setTauN(-RinexUtils.parseDouble(line, 23, 19)); + pi.glonassNav.setGammaN(RinexUtils.parseDouble(line, 42, 19)); + pi.glonassNav.setTime(fmod(RinexUtils.parseDouble(line, 61, 19), Constants.JULIAN_DAY)); + + // Set the ephemeris epoch (same as time of clock epoch) + pi.glonassNav.setDate(pi.glonassNav.getEpochToc()); + } + + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.glonassNav.setX(parseBroadcastDouble1(line, pi.initialSpaces, KM)); + pi.glonassNav.setXDot(parseBroadcastDouble2(line, pi.initialSpaces, KM_PER_S)); + pi.glonassNav.setXDotDot(parseBroadcastDouble3(line, pi.initialSpaces, KM_PER_S2)); + pi.glonassNav.setHealth(parseBroadcastDouble4(line, pi.initialSpaces, Unit.NONE)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.glonassNav.setY(parseBroadcastDouble1(line, pi.initialSpaces, KM)); + pi.glonassNav.setYDot(parseBroadcastDouble2(line, pi.initialSpaces, KM_PER_S)); + pi.glonassNav.setYDotDot(parseBroadcastDouble3(line, pi.initialSpaces, KM_PER_S2)); + pi.glonassNav.setFrequencyNumber(parseBroadcastDouble4(line, pi.initialSpaces, Unit.NONE)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.glonassNav.setZ(parseBroadcastDouble1(line, pi.initialSpaces, KM)); + pi.glonassNav.setZDot(parseBroadcastDouble2(line, pi.initialSpaces, KM_PER_S)); + pi.glonassNav.setZDotDot(parseBroadcastDouble3(line, pi.initialSpaces, KM_PER_S2)); + if (pi.file.getHeader().getFormatVersion() < 3.045) { + pi.closePendingMessage(); + } + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.glonassNav.setStatusFlags(parseBroadcastDouble1(line, pi.initialSpaces, Unit.NONE)); + pi.glonassNav.setGroupDelayDifference(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.glonassNav.setURA(parseBroadcastDouble3(line, pi.initialSpaces, Unit.NONE)); + pi.glonassNav.setHealthFlags(parseBroadcastDouble4(line, pi.initialSpaces, Unit.NONE)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addGlonassNavigationMessage(pi.glonassNav); + pi.glonassNav = null; + } + + }, + + /** QZSS legacy. */ + QZSS_LNAV() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getGPS(), pi.qzssLNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssLNav.setIODE(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssLNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.qzssLNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.qzssLNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssLNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssLNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.qzssLNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssLNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssLNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssLNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssLNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssLNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssLNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssLNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.qzssLNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssLNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + // iDot + pi.qzssLNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + // Codes on L2 channel (ignored) + // RinexUtils.parseDouble(line, 23, 19) + // GPS week (to go with Toe) + pi.qzssLNav.setWeek(parseBroadcastInt3(line, pi.initialSpaces)); + pi.qzssLNav.setDate(new GNSSDate(pi.qzssLNav.getWeek(), + pi.qzssLNav.getTime(), + SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week! + pi.timeScales).getDate()); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssLNav.setSvAccuracy(parseBroadcastDouble1(line, pi.initialSpaces, Unit.METRE)); + pi.qzssLNav.setSvHealth(parseBroadcastInt2(line, pi.initialSpaces)); + pi.qzssLNav.setTGD(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssLNav.setIODC(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssLNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssLNav.setFitInterval(parseBroadcastInt2(line, pi.initialSpaces)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addQZSSLegacyNavigationMessage(pi.qzssLNav); + pi.qzssLNav = null; + } + + }, + + /** QZSS civilian. + * @since 12.0 + */ + QZSS_CNAV() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getGPS(), pi.qzssCNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setADot(parseBroadcastDouble1(line, pi.initialSpaces, M_PER_S)); + pi.qzssCNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.qzssCNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.qzssCNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssCNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.qzssCNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssCNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssCNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssCNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssCNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssCNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.qzssCNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.qzssCNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + pi.qzssCNav.setDeltaN0Dot(parseBroadcastDouble2(line, pi.initialSpaces, RAD_PER_S2)); + pi.qzssCNav.setUraiNed0(parseBroadcastInt3(line, pi.initialSpaces)); + pi.qzssCNav.setUraiNed1(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setUraiEd(parseBroadcastInt1(line, pi.initialSpaces)); + pi.qzssCNav.setSvHealth(parseBroadcastInt2(line, pi.initialSpaces)); + pi.qzssCNav.setTGD(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssCNav.setUraiNed2(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.qzssCNav.setIscL1CA(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssCNav.setIscL2C(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssCNav.setIscL5I5(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssCNav.setIscL5Q5(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } + + /** {@inheritDoc} */ + @Override + public void parseEighthBroadcastOrbit(final String line, final ParseInfo pi) { + if (pi.qzssCNav.isCnv2()) { + // in CNAV2 messages, there is an additional line for L1 CD and L1 CP inter signal delay + pi.qzssCNav.setIscL1CD(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.qzssCNav.setIscL1CP(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + } else { + parseTransmissionTimeLine(line, pi); + } + } + + /** {@inheritDoc} */ + @Override + public void parseNinthBroadcastOrbit(final String line, final ParseInfo pi) { + parseTransmissionTimeLine(line, pi); + } + + /** Parse transmission time line. + * @param line line to parse + * @param pi holder for transient data + */ + private void parseTransmissionTimeLine(final String line, final ParseInfo pi) { + pi.qzssCNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addQZSSCivilianNavigationMessage(pi.qzssCNav); + pi.qzssCNav = null; + } + + }, + + /** Beidou legacy. */ + BEIDOU_D1_D2() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getBDT(), pi.beidouLNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouLNav.setAODE(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouLNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.beidouLNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.beidouLNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouLNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouLNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.beidouLNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouLNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouLNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouLNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouLNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouLNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouLNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouLNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.beidouLNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouLNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + // iDot + pi.beidouLNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + // BDT week (to go with Toe) + pi.beidouLNav.setWeek(parseBroadcastInt3(line, pi.initialSpaces)); + pi.beidouLNav.setDate(new GNSSDate(pi.beidouLNav.getWeek(), + pi.beidouLNav.getTime(), + SatelliteSystem.BEIDOU, + pi.timeScales).getDate()); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouLNav.setSvAccuracy(parseBroadcastDouble1(line, pi.initialSpaces, Unit.METRE)); + // TODO SatH1 + pi.beidouLNav.setTGD1(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouLNav.setTGD2(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouLNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouLNav.setAODC(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addBeidouLegacyNavigationMessage(pi.beidouLNav); + pi.beidouLNav = null; + } + + }, + + /** Beidou-3 CNAV. */ + BEIDOU_CNV_123() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getBDT(), pi.beidouCNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setADot(parseBroadcastDouble1(line, pi.initialSpaces, M_PER_S)); + pi.beidouCNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.beidouCNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.beidouCNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouCNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.beidouCNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouCNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouCNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouCNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouCNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouCNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.beidouCNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.beidouCNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + pi.beidouCNav.setDeltaN0Dot(parseBroadcastDouble2(line, pi.initialSpaces, RAD_PER_S2)); + switch (parseBroadcastInt3(line, pi.initialSpaces)) { + case 0 : + pi.beidouCNav.setSatelliteType(BeidouSatelliteType.RESERVED); + break; + case 1 : + pi.beidouCNav.setSatelliteType(BeidouSatelliteType.GEO); + break; + case 2 : + pi.beidouCNav.setSatelliteType(BeidouSatelliteType.IGSO); + break; + case 3 : + pi.beidouCNav.setSatelliteType(BeidouSatelliteType.MEO); + break; + default: + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + pi.lineNumber, pi.name, line); + } + pi.beidouCNav.setTime(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setSisaiOe(parseBroadcastInt1(line, pi.initialSpaces)); + pi.beidouCNav.setSisaiOcb(parseBroadcastInt2(line, pi.initialSpaces)); + pi.beidouCNav.setSisaiOc1(parseBroadcastInt3(line, pi.initialSpaces)); + pi.beidouCNav.setSisaiOc2(parseBroadcastInt4(line, pi.initialSpaces)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + if (pi.beidouCNav.getSignal() == Frequency.B1C) { + pi.beidouCNav.setIscB1CD(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + // field 2 is spare + pi.beidouCNav.setTgdB1Cp(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouCNav.setTgdB2ap(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } else if (pi.beidouCNav.getSignal() == Frequency.B2A) { + // field 1 is spare + pi.beidouCNav.setIscB2AD(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouCNav.setTgdB1Cp(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + pi.beidouCNav.setTgdB2ap(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + } else { + parseSismaiHealthIntegrity(line, pi); + } + } + + /** {@inheritDoc} */ + @Override + public void parseEighthBroadcastOrbit(final String line, final ParseInfo pi) { + if (pi.beidouCNav.getSignal() == Frequency.B2B) { + pi.beidouCNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.closePendingMessage(); + } else { + parseSismaiHealthIntegrity(line, pi); + } + } + + /** {@inheritDoc} */ + @Override + public void parseNinthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.beidouCNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + // field 2 is spare + // field 3 is spare + pi.beidouCNav.setIODE(parseBroadcastInt4(line, pi.initialSpaces)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addBeidouCivilianNavigationMessage(pi.beidouCNav); + pi.beidouCNav = null; + } + + /** + * Parse the SISMAI/Health/integrity line. + * @param line line to read + * @param pi holder for transient data + */ + private void parseSismaiHealthIntegrity(final String line, final ParseInfo pi) { + pi.beidouCNav.setSismai(parseBroadcastInt1(line, pi.initialSpaces)); + pi.beidouCNav.setHealth(parseBroadcastInt2(line, pi.initialSpaces)); + pi.beidouCNav.setIntegrityFlags(parseBroadcastInt3(line, pi.initialSpaces)); + pi.beidouCNav.setIODC(parseBroadcastInt4(line, pi.initialSpaces)); + } + + }, + + /** SBAS. */ + SBAS() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + + // parse PRN + pi.sbasNav.setPRN(RinexUtils.parseInt(line, 1, 2)); + + // Time scale (UTC for Rinex 3.01 and GPS for other RINEX versions) + final int version100 = (int) FastMath.rint(pi.file.getHeader().getFormatVersion() * 100); + final TimeScale timeScale = (version100 == 301) ? pi.timeScales.getUTC() : pi.timeScales.getGPS(); + + pi.sbasNav.setEpochToc(parsePrnSvEpochClock(line, timeScale)); + pi.sbasNav.setAGf0(parseBroadcastDouble2(line, pi.initialSpaces, Unit.SECOND)); + pi.sbasNav.setAGf1(parseBroadcastDouble3(line, pi.initialSpaces, S_PER_S)); + pi.sbasNav.setTime(parseBroadcastDouble4(line, pi.initialSpaces, Unit.SECOND)); + + // Set the ephemeris epoch (same as time of clock epoch) + pi.sbasNav.setDate(pi.sbasNav.getEpochToc()); + + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.sbasNav.setX(parseBroadcastDouble1(line, pi.initialSpaces, KM)); + pi.sbasNav.setXDot(parseBroadcastDouble2(line, pi.initialSpaces, KM_PER_S)); + pi.sbasNav.setXDotDot(parseBroadcastDouble3(line, pi.initialSpaces, KM_PER_S2)); + pi.sbasNav.setHealth(parseBroadcastDouble4(line, pi.initialSpaces, Unit.NONE)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.sbasNav.setY(parseBroadcastDouble1(line, pi.initialSpaces, KM)); + pi.sbasNav.setYDot(parseBroadcastDouble2(line, pi.initialSpaces, KM_PER_S)); + pi.sbasNav.setYDotDot(parseBroadcastDouble3(line, pi.initialSpaces, KM_PER_S2)); + pi.sbasNav.setURA(parseBroadcastDouble4(line, pi.initialSpaces, Unit.NONE)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.sbasNav.setZ(parseBroadcastDouble1(line, pi.initialSpaces, KM)); + pi.sbasNav.setZDot(parseBroadcastDouble2(line, pi.initialSpaces, KM_PER_S)); + pi.sbasNav.setZDotDot(parseBroadcastDouble3(line, pi.initialSpaces, KM_PER_S2)); + pi.sbasNav.setIODN(parseBroadcastDouble4(line, pi.initialSpaces, Unit.NONE)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addSBASNavigationMessage(pi.sbasNav); + pi.sbasNav = null; + } + + }, + + /** IRNSS. */ + IRNSS() { + + /** {@inheritDoc} */ + @Override + public void parseSvEpochSvClockLine(final String line, final ParseInfo pi) { + parseSvEpochSvClockLine(line, pi.timeScales.getIRNSS(), pi.irnssNav); + } + + /** {@inheritDoc} */ + @Override + public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { + pi.irnssNav.setIODEC(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.irnssNav.setCrs(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.irnssNav.setDeltaN(parseBroadcastDouble3(line, pi.initialSpaces, RAD_PER_S)); + pi.irnssNav.setM0(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { + pi.irnssNav.setCuc(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.irnssNav.setE(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.irnssNav.setCus(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.irnssNav.setSqrtA(parseBroadcastDouble4(line, pi.initialSpaces, SQRT_M)); + } + + /** {@inheritDoc} */ + @Override + public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { + pi.irnssNav.setTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.irnssNav.setCic(parseBroadcastDouble2(line, pi.initialSpaces, Unit.RADIAN)); + pi.irnssNav.setOmega0(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.irnssNav.setCis(parseBroadcastDouble4(line, pi.initialSpaces, Unit.RADIAN)); + } + + /** {@inheritDoc} */ + @Override + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.irnssNav.setI0(parseBroadcastDouble1(line, pi.initialSpaces, Unit.RADIAN)); + pi.irnssNav.setCrc(parseBroadcastDouble2(line, pi.initialSpaces, Unit.METRE)); + pi.irnssNav.setPa(parseBroadcastDouble3(line, pi.initialSpaces, Unit.RADIAN)); + pi.irnssNav.setOmegaDot(parseBroadcastDouble4(line, pi.initialSpaces, RAD_PER_S)); + } + + /** {@inheritDoc} */ + @Override + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + // iDot + pi.irnssNav.setIDot(parseBroadcastDouble1(line, pi.initialSpaces, RAD_PER_S)); + // IRNSS week (to go with Toe) + pi.irnssNav.setWeek(parseBroadcastInt3(line, pi.initialSpaces)); + pi.irnssNav.setDate(new GNSSDate(pi.irnssNav.getWeek(), + pi.irnssNav.getTime(), + SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week! + pi.timeScales).getDate()); + } + + /** {@inheritDoc} */ + @Override + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + pi.irnssNav.setURA(parseBroadcastDouble1(line, pi.initialSpaces, Unit.METRE)); + pi.irnssNav.setSvHealth(parseBroadcastDouble2(line, pi.initialSpaces, Unit.NONE)); + pi.irnssNav.setTGD(parseBroadcastDouble3(line, pi.initialSpaces, Unit.SECOND)); + } + + /** {@inheritDoc} */ + @Override + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + pi.irnssNav.setTransmissionTime(parseBroadcastDouble1(line, pi.initialSpaces, Unit.SECOND)); + pi.closePendingMessage(); + } + + /** {@inheritDoc} */ + @Override + public void closeMessage(final ParseInfo pi) { + pi.file.addIRNSSNavigationMessage(pi.irnssNav); + pi.irnssNav = null; + } + + }; + + /** Get the parse for navigation message. + * @param system satellite system + * @param type message type (null for Rinex 3.x) + * @param parseInfo container for transient data + * @param line line being parsed + * @return the satellite system line parser + */ + private static SatelliteSystemLineParser getParser(final SatelliteSystem system, final String type, + final ParseInfo parseInfo, final String line) { + switch (system) { + case GPS : + if (type == null || type.equals(LegacyNavigationMessage.LNAV)) { + parseInfo.gpsLNav = new GPSLegacyNavigationMessage(); + return GPS_LNAV; + } else if (type.equals(CivilianNavigationMessage.CNAV)) { + parseInfo.gpsCNav = new GPSCivilianNavigationMessage(false); + return GPS_CNAV; + } else if (type.equals(CivilianNavigationMessage.CNV2)) { + parseInfo.gpsCNav = new GPSCivilianNavigationMessage(true); + return GPS_CNAV; + } + break; + case GALILEO : + if (type == null || type.equals("INAV") || type.equals("FNAV")) { + parseInfo.galileoNav = new GalileoNavigationMessage(); + return GALILEO; + } + break; + case GLONASS : + if (type == null || type.equals("FDMA")) { + parseInfo.glonassNav = new GLONASSNavigationMessage(); + return GLONASS; + } + break; + case QZSS : + if (type == null || type.equals(LegacyNavigationMessage.LNAV)) { + parseInfo.qzssLNav = new QZSSLegacyNavigationMessage(); + return QZSS_LNAV; + } else if (type.equals(CivilianNavigationMessage.CNAV)) { + parseInfo.qzssCNav = new QZSSCivilianNavigationMessage(false); + return QZSS_CNAV; + } else if (type.equals(CivilianNavigationMessage.CNV2)) { + parseInfo.qzssCNav = new QZSSCivilianNavigationMessage(true); + return QZSS_CNAV; + } + break; + case BEIDOU : + if (type == null || + type.equals(BeidouLegacyNavigationMessage.D1) || + type.equals(BeidouLegacyNavigationMessage.D2)) { + parseInfo.beidouLNav = new BeidouLegacyNavigationMessage(); + return BEIDOU_D1_D2; + } else if (type.equals(BeidouCivilianNavigationMessage.CNV1)) { + parseInfo.beidouCNav = new BeidouCivilianNavigationMessage(Frequency.B1C); + return BEIDOU_CNV_123; + } else if (type.equals(BeidouCivilianNavigationMessage.CNV2)) { + parseInfo.beidouCNav = new BeidouCivilianNavigationMessage(Frequency.B2A); + return BEIDOU_CNV_123; + } else if (type.equals(BeidouCivilianNavigationMessage.CNV3)) { + parseInfo.beidouCNav = new BeidouCivilianNavigationMessage(Frequency.B2B); + return BEIDOU_CNV_123; + } + break; + case IRNSS : + if (type == null || type.equals("LNAV")) { + parseInfo.irnssNav = new IRNSSNavigationMessage(); + return IRNSS; + } + break; + case SBAS : + if (type == null || type.equals("SBAS")) { + parseInfo.sbasNav = new SBASNavigationMessage(); + return SBAS; + } + break; + default: + // do nothing, handle error after the switch + } + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, parseInfo.name, line); + } + + /** + * Parse the SV/Epoch/Sv clock of the navigation message. + * @param line line to read + * @param timeScale time scale to use + * @param message navigation message + */ + protected void parseSvEpochSvClockLineRinex2(final String line, final TimeScale timeScale, + final AbstractNavigationMessage message) { + // PRN + message.setPRN(RinexUtils.parseInt(line, 0, 2)); + + // Toc + final int year = RinexUtils.convert2DigitsYear(RinexUtils.parseInt(line, 2, 3)); + final int month = RinexUtils.parseInt(line, 5, 3); + final int day = RinexUtils.parseInt(line, 8, 3); + final int hours = RinexUtils.parseInt(line, 11, 3); + final int min = RinexUtils.parseInt(line, 14, 3); + final double sec = RinexUtils.parseDouble(line, 17, 5); + message.setEpochToc(new AbsoluteDate( year, month, day, hours, min, sec, timeScale)); + + // clock + message.setAf0(RinexUtils.parseDouble(line, 22, 19)); + message.setAf1(RinexUtils.parseDouble(line, 41, 19)); + message.setAf2(RinexUtils.parseDouble(line, 60, 19)); + + } + + /** + * Parse the SV/Epoch/Sv clock of the navigation message. + * @param line line to read + * @param timeScale time scale to use + * @param message navigation message + */ + protected void parseSvEpochSvClockLine(final String line, final TimeScale timeScale, + final AbstractNavigationMessage message) { + // PRN + message.setPRN(RinexUtils.parseInt(line, 1, 2)); + + // Toc + message.setEpochToc(parsePrnSvEpochClock(line, timeScale)); + + // clock + message.setAf0(RinexUtils.parseDouble(line, 23, 19)); + message.setAf1(RinexUtils.parseDouble(line, 42, 19)); + message.setAf2(RinexUtils.parseDouble(line, 61, 19)); + + } + + /** Parse epoch field of a Sv/epoch/clock line. + * @param line line to parse + * @param timeScale time scale to use + * @return parsed field + */ + protected AbsoluteDate parsePrnSvEpochClock(final String line, final TimeScale timeScale) { + final int year = RinexUtils.parseInt(line, 4, 4); + final int month = RinexUtils.parseInt(line, 9, 2); + final int day = RinexUtils.parseInt(line, 12, 2); + final int hours = RinexUtils.parseInt(line, 15, 2); + final int min = RinexUtils.parseInt(line, 18, 2); + final int sec = RinexUtils.parseInt(line, 21, 2); + return new AbsoluteDate(year, month, day, hours, min, sec, timeScale); + } + + /** Parse double field 1 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @param unit unit to used for parsing the field + * @return parsed field + */ + protected double parseBroadcastDouble1(final String line, final int initialSpaces, final Unit unit) { + return unit.toSI(RinexUtils.parseDouble(line, initialSpaces, 19)); + } + + /** Parse integer field 1 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @return parsed field + */ + protected int parseBroadcastInt1(final String line, final int initialSpaces) { + return (int) FastMath.rint(RinexUtils.parseDouble(line, initialSpaces, 19)); + } + + /** Parse double field 2 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @param unit unit to used for parsing the field + * @return parsed field + */ + protected double parseBroadcastDouble2(final String line, final int initialSpaces, final Unit unit) { + return unit.toSI(RinexUtils.parseDouble(line, initialSpaces + 19, 19)); + } + + /** Parse integer field 2 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @return parsed field + */ + protected int parseBroadcastInt2(final String line, final int initialSpaces) { + return (int) FastMath.rint(RinexUtils.parseDouble(line, initialSpaces + 19, 19)); + } + + /** Parse double field 3 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @param unit unit to used for parsing the field + * @return parsed field + */ + protected double parseBroadcastDouble3(final String line, final int initialSpaces, final Unit unit) { + return unit.toSI(RinexUtils.parseDouble(line, initialSpaces + 38, 19)); + } + + /** Parse integer field 3 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @return parsed field + */ + protected int parseBroadcastInt3(final String line, final int initialSpaces) { + return (int) FastMath.rint(RinexUtils.parseDouble(line, initialSpaces + 38, 19)); + } + + /** Parse double field 4 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @param unit unit to used for parsing the field + * @return parsed field + */ + protected double parseBroadcastDouble4(final String line, final int initialSpaces, final Unit unit) { + return unit.toSI(RinexUtils.parseDouble(line, initialSpaces + 57, 19)); + } + + /** Parse integer field 4 of a broadcast orbit line. + * @param line line to parse + * @param initialSpaces number of initial spaces in the line + * @return parsed field + */ + protected int parseBroadcastInt4(final String line, final int initialSpaces) { + return (int) FastMath.rint(RinexUtils.parseDouble(line, initialSpaces + 57, 19)); + } + + /** + * Parse the SV/Epoch/Sv clock of the navigation message. + * @param line line to read + * @param pi holder for transient data + */ + public abstract void parseSvEpochSvClockLine(String line, ParseInfo pi); + + /** + * Parse the "BROADCASTORBIT - 1" line. + * @param line line to read + * @param pi holder for transient data + */ + public abstract void parseFirstBroadcastOrbit(String line, ParseInfo pi); + + /** + * Parse the "BROADCASTORBIT - 2" line. + * @param line line to read + * @param pi holder for transient data + */ + public abstract void parseSecondBroadcastOrbit(String line, ParseInfo pi); + + /** + * Parse the "BROADCASTORBIT - 3" line. + * @param line line to read + * @param pi holder for transient data + */ + public abstract void parseThirdBroadcastOrbit(String line, ParseInfo pi); + + /** + * Parse the "BROADCASTORBIT - 4" line. + * @param line line to read + * @param pi holder for transient data + */ + public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { + // this should never be called (except by some tests that use reflection) + throw new OrekitInternalError(null); + } + + /** + * Parse the "BROADCASTORBIT - 5" line. + * @param line line to read + * @param pi holder for transient data + */ + public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { + // this should never be called (except by some tests that use reflection) + throw new OrekitInternalError(null); + } + + /** + * Parse the "BROADCASTORBIT - 6" line. + * @param line line to read + * @param pi holder for transient data + */ + public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { + // this should never be called (except by some tests that use reflection) + throw new OrekitInternalError(null); + } + + /** + * Parse the "BROADCASTORBIT - 7" line. + * @param line line to read + * @param pi holder for transient data + */ + public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { + // this should never be called (except by some tests that use reflection) + throw new OrekitInternalError(null); + } + + /** + * Parse the "BROADCASTORBIT - 8" line. + * @param line line to read + * @param pi holder for transient data + */ + public void parseEighthBroadcastOrbit(final String line, final ParseInfo pi) { + // this should never be called (except by some tests that use reflection) + throw new OrekitInternalError(null); + } + + /** + * Parse the "BROADCASTORBIT - 9" line. + * @param line line to read + * @param pi holder for transient data + */ + public void parseNinthBroadcastOrbit(final String line, final ParseInfo pi) { + // this should never be called (except by some tests that use reflection) + throw new OrekitInternalError(null); + } + + /** + * Close a message as last line was parsed. + * @param pi holder for transient data + */ + public abstract void closeMessage(ParseInfo pi); + + /** + * Calculates the floating-point remainder of a / b. + *

          + * fmod = a - x * b + * where x = (int) a / b + *

          + * @param a numerator + * @param b denominator + * @return the floating-point remainder of a / b + */ + private static double fmod(final double a, final double b) { + final double x = (int) (a / b); + return a - x * b; + } + + } + + /** Parsing method. */ + @FunctionalInterface + private interface ParsingMethod { + /** Parse a line. + * @param line line to parse + * @param parseInfo holder for transient data + */ + void parse(String line, ParseInfo parseInfo); + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/SbasId.java b/src/main/java/org/orekit/files/rinex/navigation/SbasId.java new file mode 100644 index 0000000000..4e8c248fe6 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/SbasId.java @@ -0,0 +1,56 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +/** Enumerate for the SBAS ids. + * + * @author Luc Maisonobe + * @since 12.0 + */ +public enum SbasId { + + /** Wide Area Augmentation System. */ + WAAS, + + /** European Geostationary Navigation Overlay Service. */ + EGNOS, + + /** Multi-functional Satellite Augmentation System. */ + MSAS, + + /** GPS Aided Geo Augmented Navigation. */ + GAGAN, + + /** System for Differential Corrections and Monitoring. */ + SDCM, + + /** BeiDou Satellite-based Augmentation System. */ + BDSBAS, + + /** Soluciόn de Aumentaciόn para Caribe, Centro y Sudamérica. */ + SACCSA, + + /** Korea Augmentation Satellite System. */ + KASS, + + /** African Satellite-Based Augmentation System (ASECNA). */ + A_SBAS, + + /** Southern Positioning Augmentation System. */ + SPAN; + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/SystemTimeOffsetMessage.java b/src/main/java/org/orekit/files/rinex/navigation/SystemTimeOffsetMessage.java new file mode 100644 index 0000000000..0b59255c7d --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/SystemTimeOffsetMessage.java @@ -0,0 +1,192 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; +import org.orekit.gnss.TimeSystem; +import org.orekit.time.AbsoluteDate; + +/** + * Container for data contained in a System Time Offset navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class SystemTimeOffsetMessage extends TypeSvMessage { + + /** Reference epoch. */ + private AbsoluteDate referenceEpoch; + + /** Time system defined by this message. */ + private TimeSystem definedTimeSystem; + + /** Time system used as a reference to define a time system. */ + private TimeSystem referenceTimeSystem; + + /** SBAS ID. */ + private SbasId sbasId; + + /** UTC ID. */ + private UtcId utcId; + + /** Constant term of the offset. */ + private double a0; + + /** Linear term of the offset. */ + private double a1; + + /** Quadratic term of the offset. */ + private double a2; + + /** Transmission time. */ + private double transmissionTime; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + public SystemTimeOffsetMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + super(system, prn, navigationMessageType); + } + + /** Get the reference epoch. + * @return the reference epoch + */ + public AbsoluteDate getReferenceEpoch() { + return referenceEpoch; + } + + /** Set the reference epoch. + * @param referenceEpoch the reference epoch to set + */ + public void setReferenceEpoch(final AbsoluteDate referenceEpoch) { + this.referenceEpoch = referenceEpoch; + } + + /** Get the time system defined by this message. + * @return the time system defined by this message + */ + public TimeSystem getDefinedTimeSystem() { + return definedTimeSystem; + } + + /** Set the time system defined by this message. + * @param definedTimeSystem the time system defined by this message + */ + public void setDefinedTimeSystem(final TimeSystem definedTimeSystem) { + this.definedTimeSystem = definedTimeSystem; + } + + /** Get the time system used as a reference to define a time system. + * @return the time system used as a reference to define a time system + */ + public TimeSystem getReferenceTimeSystem() { + return referenceTimeSystem; + } + + /** Set the time system used as a reference to define a time system. + * @param referenceTimeSystem the time system used as a reference to define a time system + */ + public void setReferenceTimeSystem(final TimeSystem referenceTimeSystem) { + this.referenceTimeSystem = referenceTimeSystem; + } + + /** Get the SBAS Id. + * @return the SBAS Id + */ + public SbasId getSbasId() { + return sbasId; + } + + /** Set the SBAS Id. + * @param sbasId the SBAS Id to set + */ + public void setSbasId(final SbasId sbasId) { + this.sbasId = sbasId; + } + + /** Get the UTC Id. + * @return the URTC Id + */ + public UtcId getUtcId() { + return utcId; + } + + /** Set the UTC Id. + * @param utcId the URC Id to set + */ + public void setUtcId(final UtcId utcId) { + this.utcId = utcId; + } + + /** Get the constant term of the offset. + * @return the constant term of the offset + */ + public double getA0() { + return a0; + } + + /** Set the constant term of the offset. + * @param a0 constant term of the offset + */ + public void setA0(final double a0) { + this.a0 = a0; + } + + /** Get the linear term of the offset. + * @return the linear term of the offset + */ + public double getA1() { + return a1; + } + + /** set the linear term of the offset. + * @param a1 the linear term of the offset + */ + public void setA1(final double a1) { + this.a1 = a1; + } + + /** Get the quadratic term of the offset. + * @return the quadratic term of the offset + */ + public double getA2() { + return a2; + } + + /** Set the quadratic term of the offset. + * @param a2 quadratic term of the offset + */ + public void setA2(final double a2) { + this.a2 = a2; + } + + /** Get the message transmission time. + * @return message transmission time + */ + public double getTransmissionTime() { + return transmissionTime; + } + + /** Set the message transmission time. + * @param transmissionTime the message transmission time + */ + public void setTransmissionTime(final double transmissionTime) { + this.transmissionTime = transmissionTime; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/TimeSystemCorrection.java b/src/main/java/org/orekit/files/rinex/navigation/TimeSystemCorrection.java new file mode 100644 index 0000000000..298009999a --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/TimeSystemCorrection.java @@ -0,0 +1,97 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.time.AbsoluteDate; + +/** Container for time system corrections. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class TimeSystemCorrection { + + /** Time system correction type. */ + private String timeSystemCorrectionType; + + /** A0 coefficient of linear polynomial for time system correction. */ + private double timeSystemCorrectionA0; + + /** A1 coefficient of linear polynomial for time system correction. */ + private double timeSystemCorrectionA1; + + /** Reference date for time system correction. */ + private AbsoluteDate referenceDate; + + /** + * Constructor. + * @param timeSystemCorrectionType time system correction type + * @param referenceDate reference date for time system correction + * @param timeSystemCorrectionA0 A0 coefficient of linear polynomial for time system correction + * @param timeSystemCorrectionA1 A1 coefficient of linear polynomial for time system correction + */ + public TimeSystemCorrection(final String timeSystemCorrectionType, + final AbsoluteDate referenceDate, + final double timeSystemCorrectionA0, + final double timeSystemCorrectionA1) { + this.timeSystemCorrectionType = timeSystemCorrectionType; + this.referenceDate = referenceDate; + this.timeSystemCorrectionA0 = timeSystemCorrectionA0; + this.timeSystemCorrectionA1 = timeSystemCorrectionA1; + } + + /** + * Getter for the time system correction type. + * @return the time system correction type + */ + public String getTimeSystemCorrectionType() { + return timeSystemCorrectionType; + } + + /** + * Getter for the A0 coefficient of the time system correction. + *

          + * deltaT = {@link #getTimeSystemCorrectionA0() A0} + + * {@link #getTimeSystemCorrectionA1() A1} * (t - tref) + *

          + * @return the A0 coefficient of the time system correction + */ + public double getTimeSystemCorrectionA0() { + return timeSystemCorrectionA0; + } + + /** + * Getter for the A1 coefficient of the time system correction. + *

          + * deltaT = {@link #getTimeSystemCorrectionA0() A0} + + * {@link #getTimeSystemCorrectionA1() A1} * (t - tref) + *

          + * @return the A1 coefficient of the time system correction + */ + public double getTimeSystemCorrectionA1() { + return timeSystemCorrectionA1; + } + + /** + * Getter for the reference date of the time system correction polynomial. + * @return the reference date of the time system correction polynomial, + * or null for GLONASS correction, which is constant + */ + public AbsoluteDate getReferenceDate() { + return referenceDate; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/TypeSvMessage.java b/src/main/java/org/orekit/files/rinex/navigation/TypeSvMessage.java new file mode 100644 index 0000000000..9d39db42e6 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/TypeSvMessage.java @@ -0,0 +1,68 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import org.orekit.gnss.SatelliteSystem; + +/** Container for data shared by several navigation messages. + * @author Luc Maisonobe + * @since 12.0 + */ +public class TypeSvMessage { + + /** Satellite system. */ + private final SatelliteSystem system; + + /** Satellite number. */ + private final int prn; + + /** Navigation message type. */ + private final String navigationMessageType; + + /** Simple constructor. + * @param system satellite system + * @param prn satellite number + * @param navigationMessageType navigation message type + */ + protected TypeSvMessage(final SatelliteSystem system, final int prn, final String navigationMessageType) { + this.system = system; + this.prn = prn; + this.navigationMessageType = navigationMessageType; + } + + /** Get satellite system. + * @return the system + */ + public SatelliteSystem getSystem() { + return system; + } + + /** Get satellite number. + * @return the prn + */ + public int getPrn() { + return prn; + } + + /** Get navigation message type. + * @return the navigation message type + */ + public String getNavigationMessageType() { + return navigationMessageType; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/navigation/UtcId.java b/src/main/java/org/orekit/files/rinex/navigation/UtcId.java new file mode 100644 index 0000000000..6e2a8028f5 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/navigation/UtcId.java @@ -0,0 +1,95 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.navigation; + +import java.util.HashMap; +import java.util.Map; + +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; + +/** Enumerate for the UTC ids. + * + * @author Luc Maisonobe + * @since 12.0 + */ +public enum UtcId { + + /** UTC(USNO). */ + USNO("UTC(USNO)"), + + /** UTC(SU). */ + SU("UTC(SU)"), + + /** UTCGAL. */ + GAL("UTCGAL"), + + /** UTC(NTSC). */ + NTSC("UTC(NTSC)"), + + /** UTC(NICT). */ + NICT("UTC(NICT)"), + + /** UTCIRN / UTC(NPLI). */ + IRN("UTCIRN", "UTC(NPLI)"), + + /** UTC(OP). */ + OP("UTC(OP)"), + + /** UTC(NIST). + *

          + * In Rinex 4.00, this entry is not present in table 23, but appears in table A30. + *

          + */ + NIST("UTC(NIST)"); + + /** Parsing map. */ + private static final Map MAP = new HashMap<>(); + + static { + for (final UtcId utc : values()) { + for (final String id : utc.ids) { + MAP.put(id, utc); + } + } + } + + /** Valid ids. */ + private final String[] ids; + + /** Simple constructor. + * @param ids valid ids + */ + UtcId(final String... ids) { + this.ids = ids.clone(); + } + + /** Parse a string to get the UTC id. + * @param id string to parse + * @return the UTC id + * @exception OrekitIllegalArgumentException if the string does not correspond to a UTC id + */ + public static UtcId parseUtcId(final String id) + throws OrekitIllegalArgumentException { + final UtcId utcId = MAP.get(id); + if (utcId == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.UNKNOWN_UTC_ID, id); + } + return utcId; + } + +} diff --git a/src/main/java/org/orekit/gnss/navigation/package-info.java b/src/main/java/org/orekit/files/rinex/navigation/package-info.java similarity index 92% rename from src/main/java/org/orekit/gnss/navigation/package-info.java rename to src/main/java/org/orekit/files/rinex/navigation/package-info.java index 37db61a23a..c0eb97ab1d 100644 --- a/src/main/java/org/orekit/gnss/navigation/package-info.java +++ b/src/main/java/org/orekit/files/rinex/navigation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,4 +22,4 @@ * @since 11.0 * */ -package org.orekit.gnss.navigation; +package org.orekit.files.rinex.navigation; diff --git a/src/main/java/org/orekit/files/rinex/observation/GlonassSatelliteChannel.java b/src/main/java/org/orekit/files/rinex/observation/GlonassSatelliteChannel.java new file mode 100644 index 0000000000..5da197bab0 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/GlonassSatelliteChannel.java @@ -0,0 +1,56 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; + +import org.orekit.gnss.SatInSystem; + +/** Container for association between GLONASS satellites and frequency channels (f = f₀ + k Δf with k ranging-7 to +6). + * @author Luc Maisonobe + * @since 12.0 + */ +public class GlonassSatelliteChannel { + + /** Satellite. */ + private final SatInSystem satellite; + + /** Channel frequency multiplier. */ + private final int k; + + /** Simple constructor. + * @param satellite satellite identifier + * @param k channel frequency multiplier (should be between -7 and +6) + */ + public GlonassSatelliteChannel(final SatInSystem satellite, final int k) { + this.satellite = satellite; + this.k = k; + } + + /** Get the satellite identifier. + * @return satellite identifier + */ + public SatInSystem getSatellite() { + return satellite; + } + + /** Get the channel frequency multiplier. + * @return channel frequency multiplier + */ + public int getK() { + return k; + } + +} diff --git a/src/main/java/org/orekit/gnss/ObservationData.java b/src/main/java/org/orekit/files/rinex/observation/ObservationData.java similarity index 85% rename from src/main/java/org/orekit/gnss/ObservationData.java rename to src/main/java/org/orekit/files/rinex/observation/ObservationData.java index 8caa3964bb..66d1efa7c9 100644 --- a/src/main/java/org/orekit/gnss/ObservationData.java +++ b/src/main/java/org/orekit/files/rinex/observation/ObservationData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.files.rinex.observation; + +import org.orekit.gnss.ObservationType; /** Observation Data. * @since 9.2 @@ -22,16 +24,16 @@ public class ObservationData { /** Observed RINEX frequency. */ - private ObservationType observationType; + private final ObservationType observationType; /** Observed value. */ - private double value; + private final double value; /** Loss of Lock Indicator (LLI). */ - private int lli; + private final int lli; /** Signal strength. */ - private int signalStrength; + private final int signalStrength; /** Simple constructor. * @param observationType observation type @@ -39,7 +41,8 @@ public class ObservationData { * @param lli Loss of Lock Indicator * @param signalStrength signal strength */ - public ObservationData(final ObservationType observationType, final double value, final int lli, final int signalStrength) { + public ObservationData(final ObservationType observationType, + final double value, final int lli, final int signalStrength) { this.observationType = observationType; this.value = value; this.lli = lli; diff --git a/src/main/java/org/orekit/gnss/ObservationDataSet.java b/src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java similarity index 63% rename from src/main/java/org/orekit/gnss/ObservationDataSet.java rename to src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java index 7f27a4209c..547442b033 100644 --- a/src/main/java/org/orekit/gnss/ObservationDataSet.java +++ b/src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,11 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss; +package org.orekit.files.rinex.observation; import java.util.Collections; import java.util.List; +import org.orekit.gnss.SatInSystem; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; @@ -28,18 +29,17 @@ */ public class ObservationDataSet implements TimeStamped { - /** Rinex header associated with this data set. */ - private final RinexObservationHeader header; - - /** Satellite System. */ - private final SatelliteSystem satelliteSystem; - - /** PRN Number of the satellite observed. */ - private final int prnNumber; + /** Observed satellite. */ + private final SatInSystem satellite; /** Date of the observation. */ private final AbsoluteDate tObs; + /** Event flag. + * @since 12.0 + */ + private final int eventFlag; + /** List of Observation data. */ private final List observationData; @@ -48,44 +48,28 @@ public class ObservationDataSet implements TimeStamped { /** * Simple constructor. - * @param header Rinex header associated with this data set - * @param satelliteSystem Satellite system - * @param prnNumber PRN number + * @param satellite observed satellite * @param tObs Observation date + * @param eventFlag event flag * @param rcvrClkOffset Receiver clock offset (optional, 0 by default) * @param observationData List of observation data + * @since 12.0 */ - public ObservationDataSet(final RinexObservationHeader header, final SatelliteSystem satelliteSystem, - final int prnNumber, final AbsoluteDate tObs, + public ObservationDataSet(final SatInSystem satellite, final AbsoluteDate tObs, final int eventFlag, final double rcvrClkOffset, final List observationData) { - this.header = header; - this.satelliteSystem = satelliteSystem; - this.prnNumber = prnNumber; + this.satellite = satellite; this.tObs = tObs; + this.eventFlag = eventFlag; this.observationData = observationData; this.rcvrClkOffset = rcvrClkOffset; } - /** Get the Rinex header associated with this data set. - * @return Rinex header associated with this data set - * @since 9.3 - */ - public RinexObservationHeader getHeader() { - return header; - } - - /** Get Satellite System. - * @return satellite system of observed satellite + /** Get observed satellite. + * @return observed satellite + * @since 12.0 */ - public SatelliteSystem getSatelliteSystem() { - return satelliteSystem; - } - - /** Get PRN number. - * @return PRN number of the observed satellite - */ - public int getPrnNumber() { - return prnNumber; + public SatInSystem getSatellite() { + return satellite; } /** {@inheritDoc} */ @@ -94,6 +78,14 @@ public AbsoluteDate getDate() { return tObs; } + /** Get the event flag. + * @return event flag + * @since 12.0 + */ + public int getEventFlag() { + return eventFlag; + } + /** Get list of observation data. * @return unmodifiable view of of observation data for the observed satellite */ diff --git a/src/main/java/org/orekit/files/rinex/observation/PhaseShiftCorrection.java b/src/main/java/org/orekit/files/rinex/observation/PhaseShiftCorrection.java new file mode 100644 index 0000000000..09bbcd0f20 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/PhaseShiftCorrection.java @@ -0,0 +1,93 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; + +import java.util.List; + +import org.orekit.gnss.ObservationType; +import org.orekit.gnss.SatInSystem; +import org.orekit.gnss.SatelliteSystem; + +/** Phase Shift corrections. + * Contains the phase shift corrections used to + * generate phases consistent with respect to cycle shifts. + * @since 12.0 + */ +public class PhaseShiftCorrection { + + /** Satellite System. */ + private final SatelliteSystem satSystemPhaseShift; + + /** Carrier Phase Observation Code (may be null). */ + private final ObservationType typeObsPhaseShift; + + /** Phase Shift Corrections (cycles). */ + private final double phaseShiftCorrection; + + /** List of satellites involved. */ + private final List satsPhaseShift; + + /** Simple constructor. + * @param satSystemPhaseShift Satellite System + * @param typeObsPhaseShift Carrier Phase Observation Code (may be null) + * @param phaseShiftCorrection Phase Shift Corrections (cycles) + * @param satsPhaseShift List of satellites involved + */ + public PhaseShiftCorrection(final SatelliteSystem satSystemPhaseShift, + final ObservationType typeObsPhaseShift, + final double phaseShiftCorrection, + final List satsPhaseShift) { + this.satSystemPhaseShift = satSystemPhaseShift; + this.typeObsPhaseShift = typeObsPhaseShift; + this.phaseShiftCorrection = phaseShiftCorrection; + this.satsPhaseShift = satsPhaseShift; + } + + /** Get the Satellite System. + * @return Satellite System. + */ + public SatelliteSystem getSatelliteSystem() { + return satSystemPhaseShift; + } + + /** Get the Carrier Phase Observation Code. + *

          + * The observation code may be null for the uncorrected reference + * signal group + *

          + * @return Carrier Phase Observation Code. + */ + public ObservationType getTypeObs() { + return typeObsPhaseShift; + } + + /** Get the Phase Shift Corrections. + * @return Phase Shift Corrections (cycles) + */ + public double getCorrection() { + return phaseShiftCorrection; + } + + /** Get the list of satellites involved. + * @return List of satellites involved (if empty, all the sats are involved) + */ + public List getSatsCorrected() { + //If empty, all the satellites of this constellation are affected for this Observation type + return satsPhaseShift; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java new file mode 100644 index 0000000000..01a6baba35 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java @@ -0,0 +1,90 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.RinexFile; +import org.orekit.time.AbsoluteDate; + +/** Container for Rinex observation file. + * @author Luc Maisonobe + * @since 12.0 + */ +public class RinexObservation extends RinexFile { + + /** Observations. */ + private final List observations; + + /** Simple constructor. + */ + public RinexObservation() { + super(new RinexObservationHeader()); + this.observations = new ArrayList<>(); + } + + /** Get an unmodifiable view of the observations. + * @return unmodifiable view of the observations + */ + public List getObservationDataSets() { + return Collections.unmodifiableList(observations); + } + + /** Add an observations data set. + *

          + * Observations must be added chronologically, within header date range, and separated + * by an integer multiple of the {@link RinexObservationHeader#getInterval() interval} + * (ideally one interval, but entries at same dates and missing entries are allowed so + * any non-negative integer is allowed). + *

          + * @param observationsDataSet observations data set + */ + public void addObservationDataSet(final ObservationDataSet observationsDataSet) { + + final RinexObservationHeader header = getHeader(); + final AbsoluteDate current = observationsDataSet.getDate(); + + // check interval from previous observation + if (!observations.isEmpty()) { + final AbsoluteDate previous = observations.get(observations.size() - 1).getDate(); + final double factor = current.durationFrom(previous) / header.getInterval(); + final double acceptable = FastMath.max(0.0, FastMath.rint(factor)); + if (FastMath.abs(factor - acceptable) > 0.001) { + throw new OrekitIllegalArgumentException(OrekitMessages.INCONSISTENT_SAMPLING_DATE, + previous.shiftedBy(acceptable * header.getInterval()), + current); + } + } + + // check global range + final AbsoluteDate first = header.getTFirstObs(); + final AbsoluteDate last = header.getTLastObs(); + if (!current.isBetweenOrEqualTo(first, last)) { + throw new OrekitIllegalArgumentException(OrekitMessages.OUT_OF_RANGE_DATE, + current, first, last); + } + + observations.add(observationsDataSet); + + } + +} diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java new file mode 100644 index 0000000000..ea1a237b53 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java @@ -0,0 +1,840 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.orekit.files.rinex.AppliedDCBS; +import org.orekit.files.rinex.AppliedPCVS; +import org.orekit.files.rinex.section.RinexBaseHeader; +import org.orekit.files.rinex.utils.RinexFileType; +import org.orekit.gnss.ObservationType; +import org.orekit.gnss.SatInSystem; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.time.AbsoluteDate; + +/** Container for Rinex observation file header. + * @since 9.2 + */ +public class RinexObservationHeader extends RinexBaseHeader { + + /** Name of the Antenna Marker. */ + private String markerName; + + /** Number of Antenna marker. */ + private String markerNumber; + + /** Type of Antenna marker. */ + private String markerType; + + /** Name of Observer. */ + private String observerName; + + /** Name of Agency. */ + private String agencyName; + + /** Receiver Number. */ + private String receiverNumber; + + /** Receiver Type. */ + private String receiverType; + + /** Receiver version. */ + private String receiverVersion; + + /** Antenna Number. */ + private String antennaNumber; + + /** Antenna Type. */ + private String antennaType; + + /** Approximate Marker Position (WGS84). */ + private Vector3D approxPos; + + /** Antenna Height. */ + private double antennaHeight; + + /** Eccentricities of antenna center. */ + private Vector2D eccentricities; + + /** Position of antenna reference point for antenna on vehicle. */ + private Vector3D antRefPoint; + + /** Satellite system for average phasecenter position. + * @since 12.0 + */ + private SatelliteSystem phaseCenterSystem; + + /** Observation code of the average phasecenter position w/r to antenna reference point. */ + private String observationCode; + + /** Antenna phasecenter. + * North/East/Up (fixed station) or X/Y/Z in body fixed system (vehicle). */ + private Vector3D antennaPhaseCenter; + + /** Antenna B.Sight. + * Direction of the “vertical” antenna axis towards the GNSS satellites. */ + private Vector3D antennaBSight; + + /** Azimuth of the zero direction of a fixed antenna (degrees, from north). */ + private double antennaAzimuth; + + /** Zero direction of antenna. */ + private Vector3D antennaZeroDirection; + + /** Current center of mass (X,Y,Z, meters) of vehicle in body fixed coordinate system. */ + private Vector3D centerMass; + + /** Unit of the carrier to noise ratio observables Snn (if present) DBHZ: S/N given in dbHz. */ + private String signalStrengthUnit; + + /** Observation interval in seconds. */ + private double interval; + + /** Time of First observation record. */ + private AbsoluteDate tFirstObs; + + /** Time of last observation record. */ + private AbsoluteDate tLastObs; + + /** Real time-derived receiver clock offset. */ + private int clkOffset; + + /** List of applied differential code bias corrections. */ + private List listAppliedDCBS; + + /** List of antenna center variation corrections. */ + private List listAppliedPCVS; + + /** List of phase shift correction used to generate phases consistent w/r to cycle shifts. */ + private List phaseShiftCorrections; + + /** List of scale factor corrections. */ + private Map> scaleFactorCorrections; + + /** List of GLONASS satellite-channel associations. + * @since 12.0 + */ + private final List glonassChannels; + + /** Number of satellites. + * @since 12.0 + */ + private int nbSat; + + /** Number of observations per satellite. + * @since 12.0 + */ + private final Map> nbObsPerSat; + + /** Observation types for each satellite systems. + * @since 12.0 + */ + private final Map> mapTypeObs; + + /** Number of leap seconds since 6-Jan-1980. */ + private int leapSeconds; + + /** Future or past leap seconds ΔtLSF (BNK). + * i.e. future leap second if the week and day number are in the future. + */ + private int leapSecondsFuture; + + /** Respective leap second week number. + * For GPS, GAL, QZS and IRN, weeks since 6-Jan-1980. + * When BDS only file leap seconds specified, weeks since 1-Jan-2006 + */ + private int leapSecondsWeekNum; + + /** Respective leap second day number. */ + private int leapSecondsDayNum; + + /** Code phase bias correction for GLONASS C1C signal. + * @since 12.0 + */ + private double c1cCodePhaseBias; + + /** Code phase bias correction for GLONASS C1P signal. + * @since 12.0 + */ + private double c1pCodePhaseBias; + + /** Code phase bias correction for GLONASS C2C signal. + * @since 12.0 + */ + private double c2cCodePhaseBias; + + /** Code phase bias correction for GLONASS C2P signal. + * @since 12.0 + */ + private double c2pCodePhaseBias; + + /** Simple constructor. + */ + public RinexObservationHeader() { + super(RinexFileType.OBSERVATION); + antennaAzimuth = Double.NaN; + antennaHeight = Double.NaN; + eccentricities = Vector2D.ZERO; + clkOffset = -1; + nbSat = -1; + interval = Double.NaN; + leapSeconds = 0; + listAppliedDCBS = new ArrayList<>(); + listAppliedPCVS = new ArrayList<>(); + phaseShiftCorrections = new ArrayList<>(); + scaleFactorCorrections = new HashMap<>(); + glonassChannels = new ArrayList<>(); + nbObsPerSat = new HashMap<>(); + mapTypeObs = new HashMap<>(); + tLastObs = AbsoluteDate.FUTURE_INFINITY; + c1cCodePhaseBias = Double.NaN; + c1pCodePhaseBias = Double.NaN; + c2cCodePhaseBias = Double.NaN; + c2pCodePhaseBias = Double.NaN; + } + + /** Set name of the antenna marker. + * @param markerName name of the antenna marker + */ + public void setMarkerName(final String markerName) { + this.markerName = markerName; + } + + /** Get name of the antenna marker. + * @return name of the antenna marker + */ + public String getMarkerName() { + return markerName; + } + + /** Set number of the antenna marker. + * @param markerNumber number of the antenna marker + */ + public void setMarkerNumber(final String markerNumber) { + this.markerNumber = markerNumber; + } + + /** Get number of the antenna marker. + * @return number of the antenna marker + */ + public String getMarkerNumber() { + return markerNumber; + } + + /** Set name of the observer. + * @param observerName name of the observer + */ + public void setObserverName(final String observerName) { + this.observerName = observerName; + } + + /** Get name of the observer. + * @return name of the observer + */ + public String getObserverName() { + return observerName; + } + + /** + * Setter for the agency name. + * @param agencyName the agency name to set + */ + public void setAgencyName(final String agencyName) { + this.agencyName = agencyName; + } + + /** Get name of the agency. + * @return name of the agency + */ + public String getAgencyName() { + return agencyName; + } + + /** Set the number of the receiver. + * @param receiverNumber number of the receiver + */ + public void setReceiverNumber(final String receiverNumber) { + this.receiverNumber = receiverNumber; + } + + /** Get the number of the receiver. + * @return number of the receiver + */ + public String getReceiverNumber() { + return receiverNumber; + } + + /** Set the type of the receiver. + * @param receiverType type of the receiver + */ + public void setReceiverType(final String receiverType) { + this.receiverType = receiverType; + } + + /** Get the type of the receiver. + * @return type of the receiver + */ + public String getReceiverType() { + return receiverType; + } + + /** Set the version of the receiver. + * @param receiverVersion version of the receiver + */ + public void setReceiverVersion(final String receiverVersion) { + this.receiverVersion = receiverVersion; + } + + /** Get the version of the receiver. + * @return version of the receiver + */ + public String getReceiverVersion() { + return receiverVersion; + } + + /** Set the number of the antenna. + * @param antennaNumber number of the antenna + */ + public void setAntennaNumber(final String antennaNumber) { + this.antennaNumber = antennaNumber; + } + + /** Get the number of the antenna. + * @return number of the antenna + */ + public String getAntennaNumber() { + return antennaNumber; + } + + /** Set the type of the antenna. + * @param antennaType type of the antenna + */ + public void setAntennaType(final String antennaType) { + this.antennaType = antennaType; + } + + /** Get the type of the antenna. + * @return type of the antenna + */ + public String getAntennaType() { + return antennaType; + } + + /** Set the Approximate Marker Position. + * @param approxPos Approximate Marker Position + */ + public void setApproxPos(final Vector3D approxPos) { + this.approxPos = approxPos; + } + + /** Get the Approximate Marker Position. + * @return Approximate Marker Position + */ + public Vector3D getApproxPos() { + return approxPos; + } + + /** Set the antenna height. + * @param antennaHeight height of the antenna + */ + public void setAntennaHeight(final double antennaHeight) { + this.antennaHeight = antennaHeight; + } + + /** Get the antenna height. + * @return height of the antenna + */ + public double getAntennaHeight() { + return antennaHeight; + } + + /** Set the eccentricities of antenna center. + * @param eccentricities Eccentricities of antenna center + */ + public void setEccentricities(final Vector2D eccentricities) { + this.eccentricities = eccentricities; + } + + /** Get the eccentricities of antenna center. + * @return Eccentricities of antenna center + */ + public Vector2D getEccentricities() { + return eccentricities; + } + + /** Set the realtime-derived receiver clock offset. + * @param clkOffset realtime-derived receiver clock offset + */ + public void setClkOffset(final int clkOffset) { + this.clkOffset = clkOffset; + } + + /** Get the realtime-derived receiver clock offset. + * @return realtime-derived receiver clock offset + */ + public int getClkOffset() { + return clkOffset; + } + + /** Set the observation interval in seconds. + * @param interval Observation interval in seconds + */ + public void setInterval(final double interval) { + this.interval = interval; + } + + /** Get the observation interval in seconds. + * @return Observation interval in seconds + */ + public double getInterval() { + return interval; + } + + /** Set the time of First observation record. + * @param firstObs Time of First observation record + */ + public void setTFirstObs(final AbsoluteDate firstObs) { + this.tFirstObs = firstObs; + } + + /** Get the time of First observation record. + * @return Time of First observation record + */ + public AbsoluteDate getTFirstObs() { + return tFirstObs; + } + + /** Set the time of last observation record. + * @param lastObs Time of last observation record + */ + public void setTLastObs(final AbsoluteDate lastObs) { + this.tLastObs = lastObs; + } + + /** Get the time of last observation record. + * @return Time of last observation record + */ + public AbsoluteDate getTLastObs() { + return tLastObs; + } + + /** Set the Number of leap seconds since 6-Jan-1980. + * @param leapSeconds Number of leap seconds since 6-Jan-1980 + */ + public void setLeapSeconds(final int leapSeconds) { + this.leapSeconds = leapSeconds; + } + + /** Get the Number of leap seconds since 6-Jan-1980. + * @return Number of leap seconds since 6-Jan-1980 + */ + public int getLeapSeconds() { + return leapSeconds; + } + + /** Set type of the antenna marker. + * @param markerType type of the antenna marker + */ + public void setMarkerType(final String markerType) { + this.markerType = markerType; + } + + /** Get type of the antenna marker. + * @return type of the antenna marker + */ + public String getMarkerType() { + return markerType; + } + + /** Set the position of antenna reference point for antenna on vehicle. + * @param refPoint Position of antenna reference point for antenna on vehicle + */ + public void setAntennaReferencePoint(final Vector3D refPoint) { + this.antRefPoint = refPoint; + } + + /** Get the position of antenna reference point for antenna on vehicle. + * @return Position of antenna reference point for antenna on vehicle + */ + public Vector3D getAntennaReferencePoint() { + return antRefPoint; + } + + /** Set satellite system for average phase center. + * @param phaseCenterSystem satellite system for average phase center + * @since 12.0 + */ + public void setPhaseCenterSystem(final SatelliteSystem phaseCenterSystem) { + this.phaseCenterSystem = phaseCenterSystem; + } + + /** Get satellite system for average phase center. + * @return satellite system for average phase center + * @since 12.0 + */ + public SatelliteSystem getPhaseCenterSystem() { + return phaseCenterSystem; + } + + /** Set the observation code of the average phasecenter position w/r to antenna reference point. + * @param observationCode Observation code of the average phasecenter position w/r to antenna reference point + */ + public void setObservationCode(final String observationCode) { + this.observationCode = observationCode; + } + + /** Get the observation code of the average phasecenter position w/r to antenna reference point. + * @return Observation code of the average phasecenter position w/r to antenna reference point + */ + public String getObservationCode() { + return observationCode; + } + + /** Set the antenna phasecenter. + * @param antennaPhaseCenter Antenna phasecenter + */ + public void setAntennaPhaseCenter(final Vector3D antennaPhaseCenter) { + this.antennaPhaseCenter = antennaPhaseCenter; + } + + /** Get the antenna phasecenter. + * @return Antenna phasecenter + */ + public Vector3D getAntennaPhaseCenter() { + return antennaPhaseCenter; + } + + /** Set the antenna B.Sight. + * @param antennaBSight Antenna B.Sight + */ + public void setAntennaBSight(final Vector3D antennaBSight) { + this.antennaBSight = antennaBSight; + } + + /** Get the antenna B.Sight. + * @return Antenna B.Sight + */ + public Vector3D getAntennaBSight() { + return antennaBSight; + } + + /** Set the azimuth of the zero direction of a fixed antenna. + * @param antennaAzimuth Azimuth of the zero direction of a fixed antenna + */ + public void setAntennaAzimuth(final double antennaAzimuth) { + this.antennaAzimuth = antennaAzimuth; + } + + /** Get the azimuth of the zero direction of a fixed antenna. + * @return Azimuth of the zero direction of a fixed antenna + */ + public double getAntennaAzimuth() { + return antennaAzimuth; + } + + /** Set the zero direction of antenna. + * @param antennaZeroDirection Zero direction of antenna + */ + public void setAntennaZeroDirection(final Vector3D antennaZeroDirection) { + this.antennaZeroDirection = antennaZeroDirection; + } + + /** Get the zero direction of antenna. + * @return Zero direction of antenna + */ + public Vector3D getAntennaZeroDirection() { + return antennaZeroDirection; + } + + /** Set the current center of mass of vehicle in body fixed coordinate system. + * @param centerMass Current center of mass of vehicle in body fixed coordinate system + */ + public void setCenterMass(final Vector3D centerMass) { + this.centerMass = centerMass; + } + + /** Get the current center of mass of vehicle in body fixed coordinate system. + * @return Current center of mass of vehicle in body fixed coordinate system + */ + public Vector3D getCenterMass() { + return centerMass; + } + + /** Set the unit of the carrier to noise ratio observables. + * @param signalStrengthUnit Unit of the carrier to noise ratio observables + */ + public void setSignalStrengthUnit(final String signalStrengthUnit) { + this.signalStrengthUnit = signalStrengthUnit; + } + + /** Get the unit of the carrier to noise ratio observables. + * @return Unit of the carrier to noise ratio observables + */ + public String getSignalStrengthUnit() { + return signalStrengthUnit; + } + + /** Set the future or past leap seconds. + * @param leapSecondsFuture Future or past leap seconds + */ + public void setLeapSecondsFuture(final int leapSecondsFuture) { + this.leapSecondsFuture = leapSecondsFuture; + } + + /** Get the future or past leap seconds. + * @return Future or past leap seconds + */ + public int getLeapSecondsFuture() { + return leapSecondsFuture; + } + + /** Set the respective leap second week number. + * @param leapSecondsWeekNum Respective leap second week number + */ + public void setLeapSecondsWeekNum(final int leapSecondsWeekNum) { + this.leapSecondsWeekNum = leapSecondsWeekNum; + } + + /** Get the respective leap second week number. + * @return Respective leap second week number + */ + public int getLeapSecondsWeekNum() { + return leapSecondsWeekNum; + } + + /** Set the respective leap second day number. + * @param leapSecondsDayNum Respective leap second day number + */ + public void setLeapSecondsDayNum(final int leapSecondsDayNum) { + this.leapSecondsDayNum = leapSecondsDayNum; + } + + /** Get the respective leap second day number. + * @return Respective leap second day number + */ + public int getLeapSecondsDayNum() { + return leapSecondsDayNum; + } + + /** Add applied differential code bias corrections. + * @param appliedDCBS applied differential code bias corrections to add + */ + public void addAppliedDCBS(final AppliedDCBS appliedDCBS) { + listAppliedDCBS.add(appliedDCBS); + } + + /** Get the list of applied differential code bias corrections. + * @return list of applied differential code bias corrections + */ + public List getListAppliedDCBS() { + return Collections.unmodifiableList(listAppliedDCBS); + } + + /** Add antenna center variation corrections. + * @param appliedPCVS antenna center variation corrections + */ + public void addAppliedPCVS(final AppliedPCVS appliedPCVS) { + listAppliedPCVS.add(appliedPCVS); + } + + /** Get the list of antenna center variation corrections. + * @return List of antenna center variation corrections + */ + public List getListAppliedPCVS() { + return Collections.unmodifiableList(listAppliedPCVS); + } + + /** Add phase shift correction used to generate phases consistent w/r to cycle shifts. + * @param phaseShiftCorrection phase shift correction used to generate phases consistent w/r to cycle shifts + */ + public void addPhaseShiftCorrection(final PhaseShiftCorrection phaseShiftCorrection) { + phaseShiftCorrections.add(phaseShiftCorrection); + } + + /** Get the list of phase shift correction used to generate phases consistent w/r to cycle shifts. + * @return List of phase shift correction used to generate phases consistent w/r to cycle shifts + */ + public List getPhaseShiftCorrections() { + return Collections.unmodifiableList(phaseShiftCorrections); + } + + /** Add scale factor correction. + * @param satelliteSystem system to which this scaling factor applies + * @param scaleFactorCorrection scale factor correction + */ + public void addScaleFactorCorrection(final SatelliteSystem satelliteSystem, final ScaleFactorCorrection scaleFactorCorrection) { + List sfc = scaleFactorCorrections.get(satelliteSystem); + if (sfc == null) { + sfc = new ArrayList<>(); + scaleFactorCorrections.put(satelliteSystem, sfc); + } + sfc.add(scaleFactorCorrection); + } + + /** Get the list of scale factor correction. + * @param satelliteSystem system to which this scaling factor applies + * @return List of scale factor correction + */ + public List getScaleFactorCorrections(final SatelliteSystem satelliteSystem) { + final List sfc = scaleFactorCorrections.get(satelliteSystem); + return sfc == null ? Collections.emptyList() : Collections.unmodifiableList(sfc); + } + + /** Add GLONASS satellite/channel association. + * @param glonassChannel GLONASS satellite/channel association + * @since 12.0 + */ + public void addGlonassChannel(final GlonassSatelliteChannel glonassChannel) { + glonassChannels.add(glonassChannel); + } + + /** Get the list of GLONASS satellite/channel associations. + * @return List of GLONASS satellite/channel associations + * @since 12.0 + */ + public List getGlonassChannels() { + return Collections.unmodifiableList(glonassChannels); + } + + /** Set number of satellites. + * @param nbSat number of satellites + * @since 12.0 + */ + public void setNbSat(final int nbSat) { + this.nbSat = nbSat; + } + + /** Get number of satellites. + * @return number of satellites + * @since 12.0 + */ + public int getNbSat() { + return nbSat; + } + + /** Set number of observations for a satellite. + * @param sat satellite + * @param type observation type + * @param nbObs number of observations of this type for this satellite + * @since 12.0 + */ + public void setNbObsPerSatellite(final SatInSystem sat, final ObservationType type, final int nbObs) { + Map satNbObs = nbObsPerSat.get(sat); + if (satNbObs == null) { + satNbObs = new HashMap<>(); + nbObsPerSat.put(sat, satNbObs); + } + satNbObs.put(type, nbObs); + } + + /** Get an unmodifiable view of the map of number of observations per satellites. + * @return unmodifiable view of the map of number of observations per satellites + * @since 12.0 + */ + public Map> getNbObsPerSat() { + return Collections.unmodifiableMap(nbObsPerSat); + } + + /** Set number of observations for a satellite. + * @param system satellite system + * @param types observation types + * @since 12.0 + */ + public void setTypeObs(final SatelliteSystem system, final List types) { + mapTypeObs.put(system, new ArrayList<>(types)); + } + + /** Get an unmodifiable view of the map of observation types. + * @return unmodifiable view of the map of observation types + * @since 12.0 + */ + public Map> getTypeObs() { + return Collections.unmodifiableMap(mapTypeObs); + } + + /** Set the code phase bias correction for GLONASS {@link ObservationType#C1C} signal. + * @param c1cCodePhaseBias code phase bias correction for GLONASS {@link ObservationType#C1C} signal + * @since 12.0 + */ + public void setC1cCodePhaseBias(final double c1cCodePhaseBias) { + this.c1cCodePhaseBias = c1cCodePhaseBias; + } + + /** Get the code phase bias correction for GLONASS {@link ObservationType#C1C} signal. + * @return code phase bias correction for GLONASS {@link ObservationType#C1C} signal + * @since 12.0 + */ + public double getC1cCodePhaseBias() { + return c1cCodePhaseBias; + } + + /** Set the code phase bias correction for GLONASS {@link ObservationType#C1P} signal. + * @param c1pCodePhaseBias code phase bias correction for GLONASS {@link ObservationType#C1P} signal + * @since 12.0 + */ + public void setC1pCodePhaseBias(final double c1pCodePhaseBias) { + this.c1pCodePhaseBias = c1pCodePhaseBias; + } + + /** Get the code phase bias correction for GLONASS {@link ObservationType#C1P} signal. + * @return code phase bias correction for GLONASS {@link ObservationType#C1P} signal + * @since 12.0 + */ + public double getC1pCodePhaseBias() { + return c1pCodePhaseBias; + } + + /** Set the code phase bias correction for GLONASS {@link ObservationType#C2C} signal. + * @param c2cCodePhaseBias code phase bias correction for GLONASS {@link ObservationType#C2C} signal + * @since 12.0 + */ + public void setC2cCodePhaseBias(final double c2cCodePhaseBias) { + this.c2cCodePhaseBias = c2cCodePhaseBias; + } + + /** Get the code phase bias correction for GLONASS {@link ObservationType#C2C} signal. + * @return code phase bias correction for GLONASS {@link ObservationType#C2C} signal + * @since 12.0 + */ + public double getC2cCodePhaseBias() { + return c2cCodePhaseBias; + } + + /** Set the code phase bias correction for GLONASS {@link ObservationType#C2P} signal. + * @param c2pCodePhaseBias code phase bias correction for GLONASS {@link ObservationType#C2P} signal + * @since 12.0 + */ + public void setC2pCodePhaseBias(final double c2pCodePhaseBias) { + this.c2pCodePhaseBias = c2pCodePhaseBias; + } + + /** Get the code phase bias correction for GLONASS {@link ObservationType#C2P} signal. + * @return code phase bias correction for GLONASS {@link ObservationType#C2P} signal + * @since 12.0 + */ + public double getC2pCodePhaseBias() { + return c2pCodePhaseBias; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java new file mode 100644 index 0000000000..5e7a22c1cb --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java @@ -0,0 +1,1198 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.AppliedDCBS; +import org.orekit.files.rinex.AppliedPCVS; +import org.orekit.files.rinex.section.RinexLabels; +import org.orekit.files.rinex.utils.parsing.RinexUtils; +import org.orekit.gnss.ObservationTimeScale; +import org.orekit.gnss.ObservationType; +import org.orekit.gnss.SatInSystem; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScales; + +/** Parser for Rinex measurements files. + *

          + * Supported versions are: 2.00, 2.10, 2.11, 2.12 (unofficial), 2.20 (unofficial), + * 3.00, 3.01, 3.02, 3.03, 3.04, 3.05, and 4.00. + *

          + * @see rinex 2.0 + * @see rinex 2.10 + * @see rinex 2.11 + * @see unofficial rinex 2.12 + * @see unofficial rinex 2.20 + * @see rinex 3.00 + * @see rinex 3.01 + * @see rinex 3.02 + * @see rinex 3.03 + * @see rinex 3.04 + * @see rinex 3.05 + * @see rinex 4.00 + * @since 12.0 + */ +public class RinexObservationParser { + + /** Default name pattern for rinex 2 observation files. */ + public static final String DEFAULT_RINEX_2_NAMES = "^\\w{4}\\d{3}[0a-x](?:\\d{2})?\\.\\d{2}[oO]$"; + + /** Default name pattern for rinex 3 observation files. */ + public static final String DEFAULT_RINEX_3_NAMES = "^\\w{9}_\\w{1}_\\d{11}_\\d{2}\\w_\\d{2}\\w{1}_\\w{2}\\.rnx$"; + + /** Maximum number of satellites per line in Rinex 2 format . */ + private static final int MAX_SAT_PER_RINEX_2_LINE = 12; + + /** Maximum number of observations per line in Rinex 2 format. */ + private static final int MAX_OBS_PER_RINEX_2_LINE = 5; + + /** Set of time scales. */ + private final TimeScales timeScales; + + /** Simple constructor. + *

          + * This constructor uses the {@link DataContext#getDefault() default data context}. + *

          + */ + @DefaultDataContext + public RinexObservationParser() { + this(DataContext.getDefault().getTimeScales()); + } + + /** + * Create a RINEX loader/parser with the given source of RINEX auxiliary data files. + * @param timeScales the set of time scales to use when parsing dates. + * @since 12.0 + */ + public RinexObservationParser(final TimeScales timeScales) { + this.timeScales = timeScales; + } + + /** + * Parse RINEX observations messages. + * @param source source providing the data to parse + * @return parsed observations file + */ + public RinexObservation parse(final DataSource source) { + + Iterable candidateParsers = Collections.singleton(LineParser.VERSION); + + // placeholders for parsed data + final ParseInfo parseInfo = new ParseInfo(source.getName()); + + try (Reader reader = source.getOpener().openReaderOnce(); + BufferedReader br = new BufferedReader(reader)) { + ++parseInfo.lineNumber; + nextLine: + for (String line = br.readLine(); line != null; line = br.readLine()) { + for (final LineParser candidate : candidateParsers) { + if (candidate.canHandle.test(line)) { + try { + candidate.parsingMethod.parse(line, parseInfo); + ++parseInfo.lineNumber; + candidateParsers = candidate.allowedNextProvider.apply(parseInfo); + continue nextLine; + } catch (StringIndexOutOfBoundsException | NumberFormatException e) { + throw new OrekitException(e, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, source.getName(), line); + } + } + } + + // no parsers found for this line + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, source.getName(), line); + + } + } catch (IOException ioe) { + throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage()); + } + + return parseInfo.file; + + } + + /** Transient data used for parsing a RINEX observation messages file. + * @since 12.0 + */ + private class ParseInfo { + + /** Name of the data source. */ + private final String name; + + /** Set of time scales for parsing dates. */ + private final TimeScales timeScales; + + /** Current line number of the navigation message. */ + private int lineNumber; + + /** Rinex file. */ + private final RinexObservation file; + + /** Date of the observation. */ + private AbsoluteDate tObs; + + /** Receiver clock offset (seconds). */ + private double rcvrClkOffset; + + /** time scale for parsing dates. */ + private TimeScale timeScale; + + /** Number of observation types. */ + private int nbTypes; + + /** Number of satellites in the current observations block. */ + private int nbSatObs; + + /** Number of scaling factors. */ + private int nbObsScaleFactor; + + /** Index of satellite in current observation. */ + private int indexObsSat; + + /** Line number of start of next observation. */ + private int nextObsStartLineNumber; + + /** Current satellite system. */ + private SatelliteSystem currentSystem; + + /** Number of satellites affected by phase shifts. */ + private int phaseShiftNbSat; + + /** Number of GLONASS satellites. */ + private int nbGlonass; + + /** Satellites affected by phase shift. */ + private final List satPhaseShift; + + /** Type of observation affected by phase shift. */ + private ObservationType phaseShiftTypeObs; + + /** Phase shift correction. */ + private double corrPhaseShift; + + /** Indicator for completed header. */ + private boolean headerCompleted; + + /** Indicator for skipping special records (eventFlag from 2 to 5). */ + private boolean specialRecord; + + /** Indicator for skipping cyckle slip records (enventFlag == 6). */ + private boolean cycleSlip; + + /** Event flag. */ + private int eventFlag; + + /** Scaling factors. */ + private final List typesObsScaleFactor; + + /** Types of observations. */ + private final List typesObs; + + /** Observations. */ + private final List observations; + + /** Satellites in current observation. */ + private final List satObs; + + /** Current satellite. */ + private SatInSystem currentSat; + + /** Constructor, build the ParseInfo object. + * @param name name of the data source + */ + ParseInfo(final String name) { + // Initialize default values for fields + this.name = name; + this.timeScales = RinexObservationParser.this.timeScales; + this.file = new RinexObservation(); + this.lineNumber = 0; + this.tObs = AbsoluteDate.PAST_INFINITY; + this.timeScale = null; + this.nbTypes = -1; + this.nbSatObs = -1; + this.nbGlonass = -1; + this.phaseShiftNbSat = -1; + this.nbObsScaleFactor = -1; + this.nextObsStartLineNumber = -1; + this.typesObs = new ArrayList<>(); + this.observations = new ArrayList<>(); + this.satPhaseShift = new ArrayList<>(); + this.typesObsScaleFactor = new ArrayList<>(); + this.satObs = new ArrayList<>(); + } + + } + + /** Parsers for specific lines. */ + private enum LineParser { + + /** Parser for version, file type and satellite system. */ + VERSION(line -> RinexLabels.VERSION.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> RinexUtils.parseVersionFileTypeSatelliteSystem(line, parseInfo.name, parseInfo.file.getHeader(), + 2.00, 2.10, 2.11, 2.12, 2.20, + 3.00, 3.01, 3.02, 3.03, 3.04, 3.05, 4.00), + LineParser::headerNext), + + /** Parser for generating program and emiting agency. */ + PROGRAM(line -> RinexLabels.PROGRAM.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> RinexUtils.parseProgramRunByDate(line, parseInfo.lineNumber, parseInfo.name, + parseInfo.timeScales, parseInfo.file.getHeader()), + LineParser::headerNext), + + /** Parser for comments. */ + COMMENT(line -> RinexLabels.COMMENT.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> RinexUtils.parseComment(parseInfo.lineNumber, line, parseInfo.file), + LineParser::commentNext), + + /** Parser for marker name. */ + MARKER_NAME(line -> RinexLabels.MARKER_NAME.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setMarkerName(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for marker number. */ + MARKER_NUMBER(line -> RinexLabels.MARKER_NUMBER.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setMarkerNumber(RinexUtils.parseString(line, 0, 20)), + LineParser::headerNext), + + /** Parser for marker type. */ + MARKER_TYPE(line -> RinexLabels.MARKER_TYPE.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setMarkerType(RinexUtils.parseString(line, 0, 20)), + LineParser::headerNext), + + /** Parser for observer agency. */ + OBSERVER_AGENCY(line -> RinexLabels.OBSERVER_AGENCY.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setObserverName(RinexUtils.parseString(line, 0, 20)); + parseInfo.file.getHeader().setAgencyName(RinexUtils.parseString(line, 20, 40)); + }, + LineParser::headerNext), + + /** Parser for receiver number, type and version. */ + REC_NB_TYPE_VERS(line -> RinexLabels.REC_NB_TYPE_VERS.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setReceiverNumber(RinexUtils.parseString(line, 0, 20)); + parseInfo.file.getHeader().setReceiverType(RinexUtils.parseString(line, 20, 20)); + parseInfo.file.getHeader().setReceiverVersion(RinexUtils.parseString(line, 40, 20)); + }, + LineParser::headerNext), + + /** Parser for antenna number and type. */ + ANT_NB_TYPE(line -> RinexLabels.ANT_NB_TYPE.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setAntennaNumber(RinexUtils.parseString(line, 0, 20)); + parseInfo.file.getHeader().setAntennaType(RinexUtils.parseString(line, 20, 20)); + }, + LineParser::headerNext), + + /** Parser for approximative position. */ + APPROX_POSITION_XYZ(line -> RinexLabels.APPROX_POSITION_XYZ.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setApproxPos(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))); + }, + LineParser::headerNext), + + /** Parser for antenna reference point. */ + ANTENNA_DELTA_H_E_N(line -> RinexLabels.ANTENNA_DELTA_H_E_N.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setAntennaHeight(RinexUtils.parseDouble(line, 0, 14)); + parseInfo.file.getHeader().setEccentricities(new Vector2D(RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))); + }, + LineParser::headerNext), + + /** Parser for antenna reference point. */ + ANTENNA_DELTA_X_Y_Z(line -> RinexLabels.ANTENNA_DELTA_X_Y_Z.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setAntennaReferencePoint(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))); + }, + LineParser::headerNext), + + /** Parser for antenna phase center. */ + ANTENNA_PHASE_CENTER(line -> RinexLabels.ANTENNA_PHASE_CENTER.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setPhaseCenterSystem(SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1))); + parseInfo.file.getHeader().setObservationCode(RinexUtils.parseString(line, 2, 3)); + parseInfo.file.getHeader().setAntennaPhaseCenter(new Vector3D(RinexUtils.parseDouble(line, 5, 9), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))); + }, + LineParser::headerNext), + + /** Parser for antenna bore sight. */ + ANTENNA_B_SIGHT_XYZ(line -> RinexLabels.ANTENNA_B_SIGHT_XYZ.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setAntennaBSight(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))); + }, + LineParser::headerNext), + + /** Parser for antenna zero direction. */ + ANTENNA_ZERODIR_AZI(line -> RinexLabels.ANTENNA_ZERODIR_AZI.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setAntennaAzimuth(FastMath.toRadians(RinexUtils.parseDouble(line, 0, 14))), + LineParser::headerNext), + + /** Parser for antenna zero direction. */ + ANTENNA_ZERODIR_XYZ(line -> RinexLabels.ANTENNA_ZERODIR_XYZ.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setAntennaZeroDirection(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))), + LineParser::headerNext), + + /** Parser for wavelength factors. */ + WAVELENGTH_FACT_L1_2(line -> RinexLabels.WAVELENGTH_FACT_L1_2.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + // optional line in Rinex 2 header, not stored for now + }, + LineParser::headerNext), + + /** Parser for observations scale factor. */ + OBS_SCALE_FACTOR(line -> RinexLabels.OBS_SCALE_FACTOR.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + final int scaleFactor = FastMath.max(1, RinexUtils.parseInt(line, 0, 6)); + final int nbObsScaleFactor = RinexUtils.parseInt(line, 6, 6); + final List types = new ArrayList<>(nbObsScaleFactor); + for (int i = 0; i < nbObsScaleFactor; i++) { + types.add(ObservationType.valueOf(RinexUtils.parseString(line, 16 + (6 * i), 2))); + } + parseInfo.file.getHeader().addScaleFactorCorrection(parseInfo.file.getHeader().getSatelliteSystem(), + new ScaleFactorCorrection(scaleFactor, types)); + }, + LineParser::headerNext), + + /** Parser for center of mass. */ + CENTER_OF_MASS_XYZ(line -> RinexLabels.CENTER_OF_MASS_XYZ.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setCenterMass(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))); + }, + LineParser::headerNext), + + /** Parser for DOI. + * @since 12.0 + */ + DOI(line -> RinexLabels.DOI.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setDoi(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for license. + * @since 12.0 + */ + LICENSE(line -> RinexLabels.LICENSE.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setLicense(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for station information. + * @since 12.0 + */ + STATION_INFORMATION(line -> RinexLabels.STATION_INFORMATION.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setStationInformation(RinexUtils.parseString(line, 0, RinexUtils.LABEL_INDEX)), + LineParser::headerNext), + + /** Parser for number and types of observations. */ + SYS_NB_TYPES_OF_OBSERV(line -> RinexLabels.SYS_NB_TYPES_OF_OBSERV.matches(RinexUtils.getLabel(line)) || + RinexLabels.NB_TYPES_OF_OBSERV.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + final double version = parseInfo.file.getHeader().getFormatVersion(); + if (parseInfo.nbTypes < 0) { + // first line of types of observations + if (version < 3) { + // Rinex 2 has only one system + parseInfo.currentSystem = parseInfo.file.getHeader().getSatelliteSystem(); + parseInfo.nbTypes = RinexUtils.parseInt(line, 0, 6); + } else { + // Rinex 3 and above allow mixed systems + parseInfo.currentSystem = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)); + parseInfo.nbTypes = RinexUtils.parseInt(line, 3, 3); + if (parseInfo.currentSystem != parseInfo.file.getHeader().getSatelliteSystem() && + parseInfo.file.getHeader().getSatelliteSystem() != SatelliteSystem.MIXED) { + throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM, + parseInfo.lineNumber, parseInfo.name, + parseInfo.file.getHeader().getSatelliteSystem(), + parseInfo.currentSystem); + } + } + } + + final int firstIndex = version < 3 ? 10 : 7; + final int increment = version < 3 ? 6 : 4; + final int size = version < 3 ? 2 : 3; + for (int i = firstIndex; + (i + size) <= RinexUtils.LABEL_INDEX && parseInfo.typesObs.size() < parseInfo.nbTypes; + i += increment) { + final String type = RinexUtils.parseString(line, i, size); + try { + parseInfo.typesObs.add(ObservationType.valueOf(type)); + } catch (IllegalArgumentException iae) { + throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY, + type, parseInfo.name, parseInfo.lineNumber); + } + } + + if (parseInfo.typesObs.size() == parseInfo.nbTypes) { + // we have completed the list + parseInfo.file.getHeader().setTypeObs(parseInfo.currentSystem, parseInfo.typesObs); + parseInfo.typesObs.clear(); + parseInfo.nbTypes = -1; + } + + }, + LineParser::headerNbTypesObs), + + /** Parser for unit of signal strength. */ + SIGNAL_STRENGTH_UNIT(line -> RinexLabels.SIGNAL_STRENGTH_UNIT.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setSignalStrengthUnit(RinexUtils.parseString(line, 0, 20)), + LineParser::headerNext), + + /** Parser for observation interval. */ + INTERVAL(line -> RinexLabels.INTERVAL.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setInterval(RinexUtils.parseDouble(line, 0, 10)), + LineParser::headerNext), + + /** Parser for time of first observation. */ + TIME_OF_FIRST_OBS(line -> RinexLabels.TIME_OF_FIRST_OBS.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + if (parseInfo.file.getHeader().getSatelliteSystem() == SatelliteSystem.MIXED) { + // in case of mixed data, time scale must be specified in the Time of First Observation line + try { + parseInfo.timeScale = ObservationTimeScale. + valueOf(RinexUtils.parseString(line, 48, 3)). + getTimeScale(parseInfo.timeScales); + } catch (IllegalArgumentException iae) { + throw new OrekitException(iae, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, parseInfo.name, line); + } + } else { + final ObservationTimeScale observationTimeScale = parseInfo.file.getHeader().getSatelliteSystem().getObservationTimeScale(); + if (observationTimeScale == null) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, parseInfo.name, line); + } + parseInfo.timeScale = observationTimeScale.getTimeScale(parseInfo.timeScales); + } + parseInfo.file.getHeader().setTFirstObs(new AbsoluteDate(RinexUtils.parseInt(line, 0, 6), + RinexUtils.parseInt(line, 6, 6), + RinexUtils.parseInt(line, 12, 6), + RinexUtils.parseInt(line, 18, 6), + RinexUtils.parseInt(line, 24, 6), + RinexUtils.parseDouble(line, 30, 13), + parseInfo.timeScale)); + }, + LineParser::headerNext), + + /** Parser for time of last observation. */ + TIME_OF_LAST_OBS(line -> RinexLabels.TIME_OF_LAST_OBS.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setTLastObs(new AbsoluteDate(RinexUtils.parseInt(line, 0, 6), + RinexUtils.parseInt(line, 6, 6), + RinexUtils.parseInt(line, 12, 6), + RinexUtils.parseInt(line, 18, 6), + RinexUtils.parseInt(line, 24, 6), + RinexUtils.parseDouble(line, 30, 13), + parseInfo.timeScale)); + }, + LineParser::headerNext), + + /** Parser for indicator of receiver clock offset application. */ + RCV_CLOCK_OFFS_APPL(line -> RinexLabels.RCV_CLOCK_OFFS_APPL.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setClkOffset(RinexUtils.parseInt(line, 0, 6)), + LineParser::headerNext), + + /** Parser for differential code bias corrections. */ + SYS_DCBS_APPLIED(line -> RinexLabels.SYS_DCBS_APPLIED.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().addAppliedDCBS(new AppliedDCBS(SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)), + RinexUtils.parseString(line, 2, 17), + RinexUtils.parseString(line, 20, 40))), + LineParser::headerNext), + + /** Parser for phase center variations corrections. */ + SYS_PCVS_APPLIED(line -> RinexLabels.SYS_PCVS_APPLIED.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().addAppliedPCVS(new AppliedPCVS(SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)), + RinexUtils.parseString(line, 2, 17), + RinexUtils.parseString(line, 20, 40))), + LineParser::headerNext), + + /** Parser for scale factor. */ + SYS_SCALE_FACTOR(line -> RinexLabels.SYS_SCALE_FACTOR.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + + int scaleFactor = 1; + if (parseInfo.nbObsScaleFactor < 0) { + // first line of scale factor + parseInfo.currentSystem = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)); + scaleFactor = RinexUtils.parseInt(line, 2, 4); + parseInfo.nbObsScaleFactor = RinexUtils.parseInt(line, 8, 2); + } + + if (parseInfo.nbObsScaleFactor == 0) { + parseInfo.typesObsScaleFactor.addAll(parseInfo.file.getHeader().getTypeObs().get(parseInfo.currentSystem)); + } else { + for (int i = 11; i < RinexUtils.LABEL_INDEX && parseInfo.typesObsScaleFactor.size() < parseInfo.nbObsScaleFactor; i += 4) { + parseInfo.typesObsScaleFactor.add(ObservationType.valueOf(RinexUtils.parseString(line, i, 3))); + } + } + + if (parseInfo.typesObsScaleFactor.size() >= parseInfo.nbObsScaleFactor) { + // we have completed the list + parseInfo.file.getHeader().addScaleFactorCorrection(parseInfo.currentSystem, + new ScaleFactorCorrection(scaleFactor, + new ArrayList<>(parseInfo.typesObsScaleFactor))); + parseInfo.nbObsScaleFactor = -1; + parseInfo.typesObsScaleFactor.clear(); + } + + }, + LineParser::headerNext), + + /** Parser for phase shift. */ + SYS_PHASE_SHIFT(line -> RinexLabels.SYS_PHASE_SHIFT.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + + if (parseInfo.phaseShiftNbSat < 0) { + // first line of phase shift + parseInfo.currentSystem = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)); + final String to = RinexUtils.parseString(line, 2, 3); + parseInfo.phaseShiftTypeObs = to.isEmpty() ? null : ObservationType.valueOf(to.length() < 3 ? "L" + to : to); + parseInfo.corrPhaseShift = RinexUtils.parseDouble(line, 6, 8); + parseInfo.phaseShiftNbSat = RinexUtils.parseInt(line, 16, 2); + } + + for (int i = 19; i + 3 < RinexUtils.LABEL_INDEX && parseInfo.satPhaseShift.size() < parseInfo.phaseShiftNbSat; i += 4) { + final SatelliteSystem system = line.charAt(i) == ' ' ? + parseInfo.currentSystem : + SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, i, 1)); + final int prn = RinexUtils.parseInt(line, i + 1, 2); + parseInfo.satPhaseShift.add(new SatInSystem(system, + system == SatelliteSystem.SBAS ? + prn + 100 : + (system == SatelliteSystem.QZSS ? prn + 192 : prn))); + } + + if (parseInfo.satPhaseShift.size() == parseInfo.phaseShiftNbSat) { + // we have completed the list + parseInfo.file.getHeader().addPhaseShiftCorrection(new PhaseShiftCorrection(parseInfo.currentSystem, + parseInfo.phaseShiftTypeObs, + parseInfo.corrPhaseShift, + new ArrayList<>(parseInfo.satPhaseShift))); + parseInfo.phaseShiftNbSat = -1; + parseInfo.satPhaseShift.clear(); + } + + }, + LineParser::headerPhaseShift), + + /** Parser for GLONASS slot and frequency number. */ + GLONASS_SLOT_FRQ_NB(line -> RinexLabels.GLONASS_SLOT_FRQ_NB.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + + if (parseInfo.nbGlonass < 0) { + // first line of GLONASS satellite/frequency association + parseInfo.nbGlonass = RinexUtils.parseInt(line, 0, 3); + } + + for (int i = 4; + i < RinexUtils.LABEL_INDEX && parseInfo.file.getHeader().getGlonassChannels().size() < parseInfo.nbGlonass; + i += 7) { + final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, i, 1)); + final int prn = RinexUtils.parseInt(line, i + 1, 2); + final int k = RinexUtils.parseInt(line, i + 4, 2); + parseInfo.file.getHeader().addGlonassChannel(new GlonassSatelliteChannel(new SatInSystem(system, prn), k)); + } + + }, + LineParser::headerNext), + + /** Parser for GLONASS phase bias corrections. */ + GLONASS_COD_PHS_BIS(line -> RinexLabels.GLONASS_COD_PHS_BIS.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + + // C1C signal + final String c1c = RinexUtils.parseString(line, 1, 3); + if (c1c.length() > 0) { + parseInfo.file.getHeader().setC1cCodePhaseBias(RinexUtils.parseDouble(line, 5, 8)); + } + + // C1P signal + final String c1p = RinexUtils.parseString(line, 14, 3); + if (c1p.length() > 0) { + parseInfo.file.getHeader().setC1pCodePhaseBias(RinexUtils.parseDouble(line, 18, 8)); + } + + // C2C signal + final String c2c = RinexUtils.parseString(line, 27, 3); + if (c2c.length() > 0) { + parseInfo.file.getHeader().setC2cCodePhaseBias(RinexUtils.parseDouble(line, 31, 8)); + } + + // C2P signal + final String c2p = RinexUtils.parseString(line, 40, 3); + if (c2p.length() > 0) { + parseInfo.file.getHeader().setC2pCodePhaseBias(RinexUtils.parseDouble(line, 44, 8)); + } + + }, + LineParser::headerNext), + + /** Parser for leap seconds. */ + LEAP_SECONDS(line -> RinexLabels.LEAP_SECONDS.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + parseInfo.file.getHeader().setLeapSeconds(RinexUtils.parseInt(line, 0, 6)); + if (parseInfo.file.getHeader().getFormatVersion() >= 3.0) { + parseInfo.file.getHeader().setLeapSecondsFuture(RinexUtils.parseInt(line, 6, 6)); + parseInfo.file.getHeader().setLeapSecondsWeekNum(RinexUtils.parseInt(line, 12, 6)); + parseInfo.file.getHeader().setLeapSecondsDayNum(RinexUtils.parseInt(line, 18, 6)); + } + }, + LineParser::headerNext), + + /** Parser for number of satellites. */ + NB_OF_SATELLITES(line -> RinexLabels.NB_OF_SATELLITES.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> parseInfo.file.getHeader().setNbSat(RinexUtils.parseInt(line, 0, 6)), + LineParser::headerNext), + + /** Parser for PRN and number of observations . */ + PRN_NB_OF_OBS(line -> RinexLabels.PRN_NB_OF_OBS.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + final String systemName = RinexUtils.parseString(line, 3, 1); + if (systemName.length() > 0) { + final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(systemName); + final int prn = RinexUtils.parseInt(line, 4, 2); + parseInfo.currentSat = new SatInSystem(system, + system == SatelliteSystem.SBAS ? + prn + 100 : + (system == SatelliteSystem.QZSS ? prn + 192 : prn)); + parseInfo.nbTypes = 0; + } + final List types = parseInfo.file.getHeader().getTypeObs().get(parseInfo.currentSat.getSystem()); + + final int firstIndex = 6; + final int increment = 6; + final int size = 6; + for (int i = firstIndex; + (i + size) <= RinexUtils.LABEL_INDEX && parseInfo.nbTypes < types.size(); + i += increment) { + final String nb = RinexUtils.parseString(line, i, size); + if (nb.length() > 0) { + parseInfo.file.getHeader().setNbObsPerSatellite(parseInfo.currentSat, types.get(parseInfo.nbTypes), + RinexUtils.parseInt(line, i, size)); + } + ++parseInfo.nbTypes; + } + + }, + LineParser::headerNext), + + /** Parser for the end of header. */ + END(line -> RinexLabels.END.matches(RinexUtils.getLabel(line)), + (line, parseInfo) -> { + + parseInfo.headerCompleted = true; + + // get rinex format version + final double version = parseInfo.file.getHeader().getFormatVersion(); + + // check mandatory header fields + if (version < 3) { + if (parseInfo.file.getHeader().getMarkerName() == null || + parseInfo.file.getHeader().getObserverName() == null || + parseInfo.file.getHeader().getReceiverNumber() == null || + parseInfo.file.getHeader().getAntennaNumber() == null || + parseInfo.file.getHeader().getTFirstObs() == null || + version < 2.20 && parseInfo.file.getHeader().getApproxPos() == null || + version < 2.20 && Double.isNaN(parseInfo.file.getHeader().getAntennaHeight()) || + parseInfo.file.getHeader().getTypeObs().isEmpty()) { + throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, parseInfo.name); + } + + } else { + if (parseInfo.file.getHeader().getMarkerName() == null || + parseInfo.file.getHeader().getObserverName() == null || + parseInfo.file.getHeader().getReceiverNumber() == null || + parseInfo.file.getHeader().getAntennaNumber() == null || + Double.isNaN(parseInfo.file.getHeader().getAntennaHeight()) || + parseInfo.file.getHeader().getTFirstObs() == null || + parseInfo.file.getHeader().getTypeObs().isEmpty()) { + throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, parseInfo.name); + } + } + }, + LineParser::headerEndNext), + + /** Parser for Rinex 2 data list of satellites. */ + RINEX_2_DATA_SAT_LIST(line -> true, + (line, parseInfo) -> { + for (int index = 32; parseInfo.satObs.size() < parseInfo.nbSatObs && index < 68; index += 3) { + // add one PRN to the list of observed satellites + final SatelliteSystem system = line.charAt(index) == ' ' ? + parseInfo.file.getHeader().getSatelliteSystem() : + SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, index, 1)); + if (system != parseInfo.file.getHeader().getSatelliteSystem() && + parseInfo.file.getHeader().getSatelliteSystem() != SatelliteSystem.MIXED) { + throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM, + parseInfo.lineNumber, parseInfo.name, + parseInfo.file.getHeader().getSatelliteSystem(), + system); + } + final int prn = RinexUtils.parseInt(line, index + 1, 2); + final SatInSystem satellite = new SatInSystem(system, + system == SatelliteSystem.SBAS ? prn + 100 : prn); + parseInfo.satObs.add(satellite); + // note that we *must* use parseInfo.file.getHeader().getSatelliteSystem() as it was used to set up parseInfo.mapTypeObs + // and it may be MIXED to be applied to all satellites systems + final int nbObservables = parseInfo.file.getHeader().getTypeObs().get(parseInfo.file.getHeader().getSatelliteSystem()).size(); + final int nbLines = (nbObservables + MAX_OBS_PER_RINEX_2_LINE - 1) / MAX_OBS_PER_RINEX_2_LINE; + parseInfo.nextObsStartLineNumber += nbLines; + } + }, + LineParser::first2), + + /** Parser for Rinex 2 data first line. */ + RINEX_2_DATA_FIRST(line -> true, + (line, parseInfo) -> { + + // flag + parseInfo.eventFlag = RinexUtils.parseInt(line, 28, 1); + + // number of sats + parseInfo.nbSatObs = RinexUtils.parseInt(line, 29, 3); + final int nbLinesSat = (parseInfo.nbSatObs + MAX_SAT_PER_RINEX_2_LINE - 1) / MAX_SAT_PER_RINEX_2_LINE; + + if (parseInfo.eventFlag < 2) { + // regular observation + parseInfo.specialRecord = false; + parseInfo.cycleSlip = false; + final int nbSat = parseInfo.file.getHeader().getNbSat(); + if (nbSat != -1 && parseInfo.nbSatObs > nbSat) { + // we check that the number of Sat in the observation is consistent + throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS, + parseInfo.lineNumber, parseInfo.name, + parseInfo.nbSatObs, nbSat); + } + parseInfo.nextObsStartLineNumber = parseInfo.lineNumber + nbLinesSat; + + // read the Receiver Clock offset, if present + parseInfo.rcvrClkOffset = RinexUtils.parseDouble(line, 68, 12); + if (Double.isNaN(parseInfo.rcvrClkOffset)) { + parseInfo.rcvrClkOffset = 0.0; + } + + } else if (parseInfo.eventFlag < 6) { + // moving antenna / new site occupation / header information / external event + // here, number of sats means number of lines to skip + parseInfo.specialRecord = true; + parseInfo.cycleSlip = false; + parseInfo.nextObsStartLineNumber = parseInfo.lineNumber + parseInfo.nbSatObs + 1; + } else if (parseInfo.eventFlag == 6) { + // cycle slip, we will ignore it during observations parsing + parseInfo.specialRecord = false; + parseInfo.cycleSlip = true; + parseInfo.nextObsStartLineNumber = parseInfo.lineNumber + nbLinesSat; + } else { + // unknown event flag + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, parseInfo.name, line); + } + + // parse the list of satellites observed + parseInfo.satObs.clear(); + if (!parseInfo.specialRecord) { + + // observations epoch + parseInfo.tObs = new AbsoluteDate(RinexUtils.convert2DigitsYear(RinexUtils.parseInt(line, 1, 2)), + RinexUtils.parseInt(line, 4, 2), + RinexUtils.parseInt(line, 7, 2), + RinexUtils.parseInt(line, 10, 2), + RinexUtils.parseInt(line, 13, 2), + RinexUtils.parseDouble(line, 15, 11), + parseInfo.timeScale); + + // satellites list + RINEX_2_DATA_SAT_LIST.parsingMethod.parse(line, parseInfo); + + } + + // prepare handling of observations for current epoch + parseInfo.indexObsSat = 0; + parseInfo.observations.clear(); + + }, + LineParser::first2), + + /** Parser for Rinex 2 special record. */ + RINEX_2_IGNORED_SPECIAL_RECORD(line -> true, + (line, parseInfo) -> { + // nothing to do + }, + LineParser::ignore2), + + /** Parser for Rinex 2 observation line. */ + RINEX_2_OBSERVATION(line -> true, + (line, parseInfo) -> { + final List types = parseInfo.file.getHeader().getTypeObs().get(parseInfo.file.getHeader().getSatelliteSystem()); + for (int index = 0; + parseInfo.observations.size() < types.size() && index < 80; + index += 16) { + final ObservationData observationData; + if (parseInfo.cycleSlip) { + // we are in a cycle slip data block (eventFlag = 6), we just ignore everything + observationData = null; + } else { + // this is a regular observation line + final ObservationType type = types.get(parseInfo.observations.size()); + final double scaling = getScaling(parseInfo, type, parseInfo.currentSystem); + observationData = new ObservationData(type, + scaling * RinexUtils.parseDouble(line, index, 14), + RinexUtils.parseInt(line, index + 14, 1), + RinexUtils.parseInt(line, index + 15, 1)); + } + parseInfo.observations.add(observationData); + } + + if (parseInfo.observations.size() == types.size()) { + // we have finished handling observations/cycle slips for one satellite + if (!parseInfo.cycleSlip) { + parseInfo.file.addObservationDataSet(new ObservationDataSet(parseInfo.satObs.get(parseInfo.indexObsSat), + parseInfo.tObs, + parseInfo.eventFlag, + parseInfo.rcvrClkOffset, + new ArrayList<>(parseInfo.observations))); + } + parseInfo.indexObsSat++; + parseInfo.observations.clear(); + } + + }, + LineParser::observation2), + + /** Parser for Rinex 3 observation line. */ + RINEX_3_OBSERVATION(line -> true, + (line, parseInfo) -> { + final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(RinexUtils.parseString(line, 0, 1)); + final int prn = RinexUtils.parseInt(line, 1, 2); + final SatInSystem sat = new SatInSystem(system, + system == SatelliteSystem.SBAS ? + prn + 100 : + (system == SatelliteSystem.QZSS ? prn + 192 : prn)); + final List types = parseInfo.file.getHeader().getTypeObs().get(sat.getSystem()); + for (int index = 3; + parseInfo.observations.size() < types.size(); + index += 16) { + final ObservationData observationData; + if (parseInfo.specialRecord || parseInfo.cycleSlip) { + // we are in a special record (eventFlag < 6) or in a cycle slip data block (eventFlag = 6), we just ignore everything + observationData = null; + } else { + // this is a regular observation line + final ObservationType type = types.get(parseInfo.observations.size()); + final double scaling = getScaling(parseInfo, type, sat.getSystem()); + observationData = new ObservationData(type, + scaling * RinexUtils.parseDouble(line, index, 14), + RinexUtils.parseInt(line, index + 14, 1), + RinexUtils.parseInt(line, index + 15, 1)); + } + parseInfo.observations.add(observationData); + } + + if (!(parseInfo.specialRecord || parseInfo.cycleSlip)) { + parseInfo.file.addObservationDataSet(new ObservationDataSet(sat, + parseInfo.tObs, + parseInfo.eventFlag, + parseInfo.rcvrClkOffset, + new ArrayList<>(parseInfo.observations))); + } + parseInfo.observations.clear(); + + }, + LineParser::observation3), + + /** Parser for Rinex 3 data first line. */ + RINEX_3_DATA_FIRST(line -> line.startsWith(">"), + (line, parseInfo) -> { + + // flag + parseInfo.eventFlag = RinexUtils.parseInt(line, 31, 1); + + // number of sats + parseInfo.nbSatObs = RinexUtils.parseInt(line, 32, 3); + + if (parseInfo.eventFlag < 2) { + // regular observation + parseInfo.specialRecord = false; + parseInfo.cycleSlip = false; + final int nbSat = parseInfo.file.getHeader().getNbSat(); + if (nbSat != -1 && parseInfo.nbSatObs > nbSat) { + // we check that the number of Sat in the observation is consistent + throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS, + parseInfo.lineNumber, parseInfo.name, + parseInfo.nbSatObs, nbSat); + } + parseInfo.nextObsStartLineNumber = parseInfo.lineNumber + parseInfo.nbSatObs + 1; + + // read the Receiver Clock offset, if present + parseInfo.rcvrClkOffset = RinexUtils.parseDouble(line, 41, 15); + if (Double.isNaN(parseInfo.rcvrClkOffset)) { + parseInfo.rcvrClkOffset = 0.0; + } + + } else if (parseInfo.eventFlag < 6) { + // moving antenna / new site occupation / header information / external event + // here, number of sats means number of lines to skip + parseInfo.specialRecord = true; + parseInfo.cycleSlip = false; + parseInfo.nextObsStartLineNumber = parseInfo.lineNumber + parseInfo.nbSatObs + 1; + } else if (parseInfo.eventFlag == 6) { + // cycle slip, we will ignore it during observations parsing + parseInfo.specialRecord = false; + parseInfo.cycleSlip = true; + parseInfo.nextObsStartLineNumber = parseInfo.lineNumber + parseInfo.nbSatObs + 1; + } else { + // unknown event flag + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + parseInfo.lineNumber, parseInfo.name, line); + } + + // parse the list of satellites observed + parseInfo.satObs.clear(); + if (!parseInfo.specialRecord) { + + // observations epoch + parseInfo.tObs = new AbsoluteDate(RinexUtils.parseInt(line, 2, 4), + RinexUtils.parseInt(line, 7, 2), + RinexUtils.parseInt(line, 10, 2), + RinexUtils.parseInt(line, 13, 2), + RinexUtils.parseInt(line, 16, 2), + RinexUtils.parseDouble(line, 18, 11), + parseInfo.timeScale); + + } + + // prepare handling of observations for current epoch + parseInfo.observations.clear(); + + }, + parseInfo -> Collections.singleton(RINEX_3_OBSERVATION)); + + + /** Predicate for identifying lines that can be parsed. */ + private final Predicate canHandle; + + /** Parsing method. */ + private final ParsingMethod parsingMethod; + + /** Provider for next line parsers. */ + private final Function> allowedNextProvider; + + /** Simple constructor. + * @param canHandle predicate for identifying lines that can be parsed + * @param parsingMethod parsing method + * @param allowedNextProvider supplier for allowed parsers for next line + */ + LineParser(final Predicate canHandle, final ParsingMethod parsingMethod, + final Function> allowedNextProvider) { + this.canHandle = canHandle; + this.parsingMethod = parsingMethod; + this.allowedNextProvider = allowedNextProvider; + } + + /** Get the allowed parsers for next lines while parsing comments. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable commentNext(final ParseInfo parseInfo) { + return parseInfo.headerCompleted ? headerEndNext(parseInfo) : headerNext(parseInfo); + } + + /** Get the allowed parsers for next lines while parsing Rinex header. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable headerNext(final ParseInfo parseInfo) { + if (parseInfo.file.getHeader().getFormatVersion() < 3) { + // Rinex 2.x header entries + return Arrays.asList(PROGRAM, COMMENT, MARKER_NAME, MARKER_NUMBER, MARKER_TYPE, OBSERVER_AGENCY, + REC_NB_TYPE_VERS, ANT_NB_TYPE, APPROX_POSITION_XYZ, ANTENNA_DELTA_H_E_N, + ANTENNA_DELTA_X_Y_Z, ANTENNA_B_SIGHT_XYZ, WAVELENGTH_FACT_L1_2, OBS_SCALE_FACTOR, + CENTER_OF_MASS_XYZ, SYS_NB_TYPES_OF_OBSERV, INTERVAL, TIME_OF_FIRST_OBS, TIME_OF_LAST_OBS, + RCV_CLOCK_OFFS_APPL, LEAP_SECONDS, NB_OF_SATELLITES, PRN_NB_OF_OBS, END); + } else if (parseInfo.file.getHeader().getFormatVersion() < 4) { + // Rinex 3.x header entries + return Arrays.asList(PROGRAM, COMMENT, MARKER_NAME, MARKER_NUMBER, MARKER_TYPE, OBSERVER_AGENCY, + REC_NB_TYPE_VERS, ANT_NB_TYPE, APPROX_POSITION_XYZ, ANTENNA_DELTA_H_E_N, + ANTENNA_DELTA_X_Y_Z, ANTENNA_PHASE_CENTER, ANTENNA_B_SIGHT_XYZ, ANTENNA_ZERODIR_AZI, + ANTENNA_ZERODIR_XYZ, CENTER_OF_MASS_XYZ, SYS_NB_TYPES_OF_OBSERV, SIGNAL_STRENGTH_UNIT, + INTERVAL, TIME_OF_FIRST_OBS, TIME_OF_LAST_OBS, RCV_CLOCK_OFFS_APPL, + SYS_DCBS_APPLIED, SYS_PCVS_APPLIED, SYS_SCALE_FACTOR, SYS_PHASE_SHIFT, + GLONASS_SLOT_FRQ_NB, GLONASS_COD_PHS_BIS, LEAP_SECONDS, NB_OF_SATELLITES, + PRN_NB_OF_OBS, END); + } else { + // Rinex 4.x header entries + return Arrays.asList(PROGRAM, COMMENT, MARKER_NAME, MARKER_NUMBER, MARKER_TYPE, OBSERVER_AGENCY, + REC_NB_TYPE_VERS, ANT_NB_TYPE, APPROX_POSITION_XYZ, ANTENNA_DELTA_H_E_N, + ANTENNA_DELTA_X_Y_Z, ANTENNA_PHASE_CENTER, ANTENNA_B_SIGHT_XYZ, ANTENNA_ZERODIR_AZI, + ANTENNA_ZERODIR_XYZ, CENTER_OF_MASS_XYZ, DOI, LICENSE, STATION_INFORMATION, + SYS_NB_TYPES_OF_OBSERV, SIGNAL_STRENGTH_UNIT, INTERVAL, TIME_OF_FIRST_OBS, TIME_OF_LAST_OBS, + RCV_CLOCK_OFFS_APPL, SYS_DCBS_APPLIED, SYS_PCVS_APPLIED, SYS_SCALE_FACTOR, SYS_PHASE_SHIFT, + GLONASS_SLOT_FRQ_NB, GLONASS_COD_PHS_BIS, LEAP_SECONDS, NB_OF_SATELLITES, + PRN_NB_OF_OBS, END); + } + } + + /** Get the allowed parsers for next lines while parsing header end. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable headerEndNext(final ParseInfo parseInfo) { + return Collections.singleton(parseInfo.file.getHeader().getFormatVersion() < 3 ? + RINEX_2_DATA_FIRST : RINEX_3_DATA_FIRST); + } + + /** Get the allowed parsers for next lines while parsing types of observations. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable headerNbTypesObs(final ParseInfo parseInfo) { + if (parseInfo.typesObs.size() < parseInfo.nbTypes) { + return Arrays.asList(COMMENT, SYS_NB_TYPES_OF_OBSERV); + } else { + return headerNext(parseInfo); + } + } + + /** Get the allowed parsers for next lines while parsing phase shifts. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable headerPhaseShift(final ParseInfo parseInfo) { + if (parseInfo.satPhaseShift.size() < parseInfo.phaseShiftNbSat) { + return Arrays.asList(COMMENT, SYS_PHASE_SHIFT); + } else { + return headerNext(parseInfo); + } + } + + /** Get the allowed parsers for next lines while parsing Rinex 2 observations first lines. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable first2(final ParseInfo parseInfo) { + if (parseInfo.specialRecord) { + return Collections.singleton(RINEX_2_IGNORED_SPECIAL_RECORD); + } else if (parseInfo.satObs.size() < parseInfo.nbSatObs) { + return Collections.singleton(RINEX_2_DATA_SAT_LIST); + } else { + return Collections.singleton(RINEX_2_OBSERVATION); + } + } + + /** Get the allowed parsers for next lines while parsing Rinex 2 ignored special records. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable ignore2(final ParseInfo parseInfo) { + if (parseInfo.lineNumber < parseInfo.nextObsStartLineNumber) { + return Collections.singleton(RINEX_2_IGNORED_SPECIAL_RECORD); + } else { + return Arrays.asList(COMMENT, RINEX_2_DATA_FIRST); + } + } + + /** Get the allowed parsers for next lines while parsing Rinex 2 observations per se. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable observation2(final ParseInfo parseInfo) { + if (parseInfo.lineNumber < parseInfo.nextObsStartLineNumber) { + return Collections.singleton(RINEX_2_OBSERVATION); + } else { + return Arrays.asList(COMMENT, RINEX_2_DATA_FIRST); + } + } + + /** Get the allowed parsers for next lines while parsing Rinex 3 observations. + * @param parseInfo holder for transient data + * @return allowed parsers for next line + */ + private static Iterable observation3(final ParseInfo parseInfo) { + if (parseInfo.lineNumber < parseInfo.nextObsStartLineNumber) { + return Collections.singleton(RINEX_3_OBSERVATION); + } else { + return Arrays.asList(COMMENT, RINEX_3_DATA_FIRST); + } + } + + /** Get the scaling factor for an observation. + * @param parseInfo holder for transient data + * @param type type of observation + * @param system satellite system for the observation + * @return scaling factor + */ + private static double getScaling(final ParseInfo parseInfo, final ObservationType type, + final SatelliteSystem system) { + + for (final ScaleFactorCorrection scaleFactorCorrection : + parseInfo.file.getHeader().getScaleFactorCorrections(system)) { + // check if the next Observation Type to read needs to be scaled + if (scaleFactorCorrection.getTypesObsScaled().contains(type)) { + return 1.0 / scaleFactorCorrection.getCorrection(); + } + } + + // no scaling + return 1.0; + + } + + } + + /** Parsing method. */ + @FunctionalInterface + private interface ParsingMethod { + /** Parse a line. + * @param line line to parse + * @param parseInfo holder for transient data + */ + void parse(String line, ParseInfo parseInfo); + } + +} diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java new file mode 100644 index 0000000000..4d89f29161 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java @@ -0,0 +1,900 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.AppliedDCBS; +import org.orekit.files.rinex.AppliedPCVS; +import org.orekit.files.rinex.section.RinexComment; +import org.orekit.files.rinex.section.RinexLabels; +import org.orekit.gnss.ObservationTimeScale; +import org.orekit.gnss.ObservationType; +import org.orekit.gnss.SatInSystem; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; + +/** Writer for Rinex observation file. + *

          + * As RINEX file are organized in batches of observations at some dates, + * these observations are cached and a new batch is output only when + * a new date appears when calling {@link #writeObservationDataSet(ObservationDataSet)} + * or when the file is closed by calling the {@link #close() close} method. + * Failing to call {@link #close() close} would imply the last batch + * of measurements is not written. This is the reason why this class implements + * {@link AutoCloseable}, so the {@link #close() close} method can be called automatically in + * a {@code try-with-resources} statement. + *

          + * @author Luc Maisonobe + * @since 12.0 + */ +public class RinexObservationWriter implements AutoCloseable { + + /** Index of label in header lines. */ + private static final int LABEL_INDEX = 60; + + /** Format for one 1 digit integer field. */ + private static final String ONE_DIGIT_INTEGER = "%1d"; + + /** Format for one 2 digits integer field. */ + private static final String PADDED_TWO_DIGITS_INTEGER = "%02d"; + + /** Format for one 2 digits integer field. */ + private static final String TWO_DIGITS_INTEGER = "%2d"; + + /** Format for one 4 digits integer field. */ + private static final String PADDED_FOUR_DIGITS_INTEGER = "%04d"; + + /** Format for one 3 digits integer field. */ + private static final String THREE_DIGITS_INTEGER = "%3d"; + + /** Format for one 4 digits integer field. */ + private static final String FOUR_DIGITS_INTEGER = "%4d"; + + /** Format for one 6 digits integer field. */ + private static final String SIX_DIGITS_INTEGER = "%6d"; + + /** Format for one 8.3 digits float field. */ + private static final String EIGHT_THREE_DIGITS_FLOAT = "%8.3f"; + + /** Format for one 8.5 digits float field. */ + private static final String EIGHT_FIVE_DIGITS_FLOAT = "%8.5f"; + + /** Format for one 9.4 digits float field. */ + private static final String NINE_FOUR_DIGITS_FLOAT = "%9.4f"; + + /** Format for one 10.3 digits float field. */ + private static final String TEN_THREE_DIGITS_FLOAT = "%10.3f"; + + /** Format for one 11.7 digits float field. */ + private static final String ELEVEN_SEVEN_DIGITS_FLOAT = "%11.7f"; + + /** Format for one 12.9 digits float field. */ + private static final String TWELVE_NINE_DIGITS_FLOAT = "%12.9f"; + + /** Format for one 13.7 digits float field. */ + private static final String THIRTEEN_SEVEN_DIGITS_FLOAT = "%13.7f"; + + /** Format for one 14.3 digits float field. */ + private static final String FOURTEEN_THREE_DIGITS_FLOAT = "%14.3f"; + + /** Format for one 14.4 digits float field. */ + private static final String FOURTEEN_FOUR_DIGITS_FLOAT = "%14.4f"; + + /** Format for one 15.12 digits float field. */ + private static final String FIFTEEN_TWELVE_DIGITS_FLOAT = "%15.12f"; + + /** Threshold for considering measurements are at the sate time. + * (we know the RINEX files encode dates with a resolution of 0.1µs) + */ + private static final double EPS_DATE = 1.0e-8; + + /** Destination of generated output. */ + private final Appendable output; + + /** Output name for error messages. */ + private final String outputName; + + /** Time scale for writing dates. */ + private TimeScale timeScale; + + /** Saved header. */ + private RinexObservationHeader savedHeader; + + /** Saved comments. */ + private List savedComments; + + /** Pending observations. */ + private final List pending; + + /** Line number. */ + private int lineNumber; + + /** Column number. */ + private int column; + + /** Simple constructor. + * @param output destination of generated output + * @param outputName output name for error messages + */ + public RinexObservationWriter(final Appendable output, final String outputName) { + this.output = output; + this.outputName = outputName; + this.savedHeader = null; + this.savedComments = Collections.emptyList(); + this.pending = new ArrayList<>(); + this.lineNumber = 0; + this.column = 0; + } + + /** {@inheritDoc} */ + @Override + public void close() throws IOException { + processPending(); + } + + /** Write a complete observation file. + *

          + * This method calls {@link #prepareComments(List)} and + * {@link #writeHeader(RinexObservationHeader)} once and then loops on + * calling {@link #writeObservationDataSet(ObservationDataSet)} + * for all observation data sets in the file + *

          + * @param rinexObservation Rinex observation file to write + * @see #writeHeader(RinexObservationHeader) + * @see #writeObservationDataSet(ObservationDataSet) + * @exception IOException if an I/O error occurs. + */ + @DefaultDataContext + public void writeCompleteFile(final RinexObservation rinexObservation) + throws IOException { + prepareComments(rinexObservation.getComments()); + writeHeader(rinexObservation.getHeader()); + for (final ObservationDataSet observationDataSet : rinexObservation.getObservationDataSets()) { + writeObservationDataSet(observationDataSet); + } + } + + /** Prepare comments to be emitted at specified lines. + * @param comments comments to be emitted + */ + public void prepareComments(final List comments) { + savedComments = comments; + } + + /** Write header. + *

          + * This method must be called exactly once at the beginning + * (directly or by {@link #writeCompleteFile(RinexObservation)}) + *

          + * @param header header to write + * @exception IOException if an I/O error occurs. + */ + @DefaultDataContext + public void writeHeader(final RinexObservationHeader header) + throws IOException { + + // check header is written exactly once + if (savedHeader != null) { + throw new OrekitException(OrekitMessages.HEADER_ALREADY_WRITTEN, outputName); + } + savedHeader = header; + lineNumber = 1; + + final ObservationTimeScale observationTimeScale = header.getSatelliteSystem().getObservationTimeScale() != null ? + header.getSatelliteSystem().getObservationTimeScale() : + ObservationTimeScale.GPS; + timeScale = observationTimeScale.getTimeScale(TimeScalesFactory.getTimeScales()); + + // RINEX VERSION / TYPE + outputField("%9.2f", header.getFormatVersion(), 9); + outputField("", 20, true); + outputField("OBSERVATION DATA", 40, true); + outputField(header.getSatelliteSystem().getKey(), 41); + finishHeaderLine(RinexLabels.VERSION); + + // PGM / RUN BY / DATE + outputField(header.getProgramName(), 20, true); + outputField(header.getRunByName(), 40, true); + final DateTimeComponents dtc = header.getCreationDateComponents(); + if (header.getFormatVersion() < 3.0 && dtc.getTime().getSecond() < 0.5) { + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 42); + outputField('-', 43); + outputField(dtc.getDate().getMonthEnum().getUpperCaseAbbreviation(), 46, true); + outputField('-', 47); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getYear() % 100, 49); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 52); + outputField(':', 53); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 55); + outputField(header.getCreationTimeZone(), 58, true); + } else { + outputField(PADDED_FOUR_DIGITS_INTEGER, dtc.getDate().getYear(), 44); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getMonth(), 46); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 48); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 51); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 53); + outputField(PADDED_TWO_DIGITS_INTEGER, (int) FastMath.rint(dtc.getTime().getSecond()), 55); + outputField(header.getCreationTimeZone(), 59, false); + } + finishHeaderLine(RinexLabels.PROGRAM); + + // MARKER NAME + outputField(header.getMarkerName(), 60, true); + finishHeaderLine(RinexLabels.MARKER_NAME); + + // MARKER NUMBER + if (header.getMarkerNumber() != null) { + outputField(header.getMarkerNumber(), 20, true); + finishHeaderLine(RinexLabels.MARKER_NUMBER); + } + + // MARKER TYPE + if (header.getFormatVersion() >= 2.20) { + outputField(header.getMarkerType(), 20, true); + finishHeaderLine(RinexLabels.MARKER_TYPE); + } + + // OBSERVER / AGENCY + outputField(header.getObserverName(), 20, true); + outputField(header.getAgencyName(), 40, true); + finishHeaderLine(RinexLabels.OBSERVER_AGENCY); + + // REC # / TYPE / VERS + outputField(header.getReceiverNumber(), 20, true); + outputField(header.getReceiverType(), 40, true); + outputField(header.getReceiverVersion(), 60, true); + finishHeaderLine(RinexLabels.REC_NB_TYPE_VERS); + + // ANT # / TYPE + outputField(header.getAntennaNumber(), 20, true); + outputField(header.getAntennaType(), 40, true); + finishHeaderLine(RinexLabels.ANT_NB_TYPE); + + // APPROX POSITION XYZ + writeHeaderLine(header.getApproxPos(), RinexLabels.APPROX_POSITION_XYZ); + + // ANTENNA: DELTA H/E/N + if (!Double.isNaN(header.getAntennaHeight())) { + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getAntennaHeight(), 14); + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getEccentricities().getX(), 28); + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getEccentricities().getY(), 42); + finishHeaderLine(RinexLabels.ANTENNA_DELTA_H_E_N); + } + + // ANTENNA: DELTA X/Y/Z + writeHeaderLine(header.getAntennaReferencePoint(), RinexLabels.ANTENNA_DELTA_X_Y_Z); + + // ANTENNA: PHASECENTER + if (header.getAntennaPhaseCenter() != null) { + outputField(header.getPhaseCenterSystem().getKey(), 1); + outputField("", 2, true); + outputField(header.getObservationCode(), 5, true); + outputField(NINE_FOUR_DIGITS_FLOAT, header.getAntennaPhaseCenter().getX(), 14); + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getAntennaPhaseCenter().getY(), 28); + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getAntennaPhaseCenter().getZ(), 42); + finishHeaderLine(RinexLabels.ANTENNA_PHASE_CENTER); + } + + // ANTENNA: B.SIGHT XY + writeHeaderLine(header.getAntennaBSight(), RinexLabels.ANTENNA_B_SIGHT_XYZ); + + // ANTENNA: ZERODIR AZI + if (!Double.isNaN(header.getAntennaAzimuth())) { + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, FastMath.toDegrees(header.getAntennaAzimuth()), 14); + finishHeaderLine(RinexLabels.ANTENNA_ZERODIR_AZI); + } + + // ANTENNA: ZERODIR XYZ + writeHeaderLine(header.getAntennaZeroDirection(), RinexLabels.ANTENNA_ZERODIR_XYZ); + + // OBS SCALE FACTOR + if (FastMath.abs(header.getFormatVersion() - 2.20) < 0.001) { + for (final SatelliteSystem system : SatelliteSystem.values()) { + for (final ScaleFactorCorrection sfc : header.getScaleFactorCorrections(system)) { + if (sfc != null) { + outputField(SIX_DIGITS_INTEGER, (int) FastMath.round(sfc.getCorrection()), 6); + outputField(SIX_DIGITS_INTEGER, sfc.getTypesObsScaled().size(), 12); + for (int i = 0; i < sfc.getTypesObsScaled().size(); ++i) { + outputField(sfc.getTypesObsScaled().get(i).name(), 18 + 6 * i, false); + } + finishHeaderLine(RinexLabels.OBS_SCALE_FACTOR); + } + } + } + } + + // CENTER OF MASS: XYZ + writeHeaderLine(header.getCenterMass(), RinexLabels.CENTER_OF_MASS_XYZ); + + // DOI + writeHeaderLine(header.getDoi(), RinexLabels.DOI); + + // LICENSE OF USE + writeHeaderLine(header.getLicense(), RinexLabels.LICENSE); + + // STATION INFORMATION + writeHeaderLine(header.getStationInformation(), RinexLabels.STATION_INFORMATION); + + // SYS / # / OBS TYPES + for (Map.Entry> entry : header.getTypeObs().entrySet()) { + if (header.getFormatVersion() < 3.0) { + outputField(SIX_DIGITS_INTEGER, entry.getValue().size(), 6); + } else { + outputField(entry.getKey().getKey(), 1); + outputField(THREE_DIGITS_INTEGER, entry.getValue().size(), 6); + } + for (final ObservationType observationType : entry.getValue()) { + int next = column + (header.getFormatVersion() < 3.0 ? 6 : 4); + if (next > LABEL_INDEX) { + // we need to set up a continuation line + finishHeaderLine(header.getFormatVersion() < 3.0 ? + RinexLabels.NB_TYPES_OF_OBSERV : + RinexLabels.SYS_NB_TYPES_OF_OBSERV); + outputField("", 6, true); + next = column + (header.getFormatVersion() < 3.0 ? 6 : 4); + } + outputField(observationType.name(), next, false); + } + finishHeaderLine(header.getFormatVersion() < 3.0 ? + RinexLabels.NB_TYPES_OF_OBSERV : + RinexLabels.SYS_NB_TYPES_OF_OBSERV); + } + + // SIGNAL STRENGTH UNIT + writeHeaderLine(header.getSignalStrengthUnit(), RinexLabels.SIGNAL_STRENGTH_UNIT); + + // INTERVAL + if (!Double.isNaN(header.getInterval())) { + outputField(TEN_THREE_DIGITS_FLOAT, header.getInterval(), 10); + finishHeaderLine(RinexLabels.INTERVAL); + } + + // TIME OF FIRST OBS + final DateTimeComponents dtcFirst = header.getTFirstObs().getComponents(timeScale); + outputField(SIX_DIGITS_INTEGER, dtcFirst.getDate().getYear(), 6); + outputField(SIX_DIGITS_INTEGER, dtcFirst.getDate().getMonth(), 12); + outputField(SIX_DIGITS_INTEGER, dtcFirst.getDate().getDay(), 18); + outputField(SIX_DIGITS_INTEGER, dtcFirst.getTime().getHour(), 24); + outputField(SIX_DIGITS_INTEGER, dtcFirst.getTime().getMinute(), 30); + outputField(THIRTEEN_SEVEN_DIGITS_FLOAT, dtcFirst.getTime().getSecond(), 43); + outputField(observationTimeScale.name(), 51, false); + finishHeaderLine(RinexLabels.TIME_OF_FIRST_OBS); + + // TIME OF LAST OBS + if (!header.getTLastObs().equals(AbsoluteDate.FUTURE_INFINITY)) { + final DateTimeComponents dtcLast = header.getTLastObs().getComponents(timeScale); + outputField(SIX_DIGITS_INTEGER, dtcLast.getDate().getYear(), 6); + outputField(SIX_DIGITS_INTEGER, dtcLast.getDate().getMonth(), 12); + outputField(SIX_DIGITS_INTEGER, dtcLast.getDate().getDay(), 18); + outputField(SIX_DIGITS_INTEGER, dtcLast.getTime().getHour(), 24); + outputField(SIX_DIGITS_INTEGER, dtcLast.getTime().getMinute(), 30); + outputField(THIRTEEN_SEVEN_DIGITS_FLOAT, dtcLast.getTime().getSecond(), 43); + outputField(observationTimeScale.name(), 51, false); + finishHeaderLine(RinexLabels.TIME_OF_LAST_OBS); + } + + // RCV CLOCK OFFS APPL + if (header.getClkOffset() >= 0) { + outputField(SIX_DIGITS_INTEGER, header.getClkOffset(), 6); + finishHeaderLine(RinexLabels.RCV_CLOCK_OFFS_APPL); + } + + // SYS / DCBS APPLIED + for (final AppliedDCBS appliedDCBS : header.getListAppliedDCBS()) { + outputField(appliedDCBS.getSatelliteSystem().getKey(), 1); + outputField("", 2, true); + outputField(appliedDCBS.getProgDCBS(), 20, true); + outputField(appliedDCBS.getSourceDCBS(), 60, true); + finishHeaderLine(RinexLabels.SYS_DCBS_APPLIED); + } + + // SYS / PCVS APPLIED + for (final AppliedPCVS appliedPCVS : header.getListAppliedPCVS()) { + outputField(appliedPCVS.getSatelliteSystem().getKey(), 1); + outputField("", 2, true); + outputField(appliedPCVS.getProgPCVS(), 20, true); + outputField(appliedPCVS.getSourcePCVS(), 60, true); + finishHeaderLine(RinexLabels.SYS_PCVS_APPLIED); + } + + // SYS / SCALE FACTOR + if (header.getFormatVersion() >= 3.0) { + for (final SatelliteSystem system : SatelliteSystem.values()) { + for (final ScaleFactorCorrection sfc : header.getScaleFactorCorrections(system)) { + if (sfc != null) { + outputField(system.getKey(), 1); + outputField("", 2, true); + outputField(FOUR_DIGITS_INTEGER, (int) FastMath.rint(sfc.getCorrection()), 6); + if (sfc.getTypesObsScaled().size() < header.getTypeObs().get(system).size()) { + outputField("", 8, true); + outputField(TWO_DIGITS_INTEGER, sfc.getTypesObsScaled().size(), 10); + for (ObservationType observationType : sfc.getTypesObsScaled()) { + int next = column + 4; + if (next > LABEL_INDEX) { + // we need to set up a continuation line + finishHeaderLine(RinexLabels.SYS_SCALE_FACTOR); + outputField("", 10, true); + next = column + 4; + } + outputField("", next - 3, true); + outputField(observationType.name(), next, true); + } + } + finishHeaderLine(RinexLabels.SYS_SCALE_FACTOR); + } + } + } + } + + // SYS / PHASE SHIFT + for (final PhaseShiftCorrection psc : header.getPhaseShiftCorrections()) { + outputField(psc.getSatelliteSystem().getKey(), 1); + outputField(psc.getTypeObs().name(), 5, false); + outputField(EIGHT_FIVE_DIGITS_FLOAT, psc.getCorrection(), 14); + if (!psc.getSatsCorrected().isEmpty()) { + outputField(TWO_DIGITS_INTEGER, psc.getSatsCorrected().size(), 18); + for (final SatInSystem sis : psc.getSatsCorrected()) { + int next = column + 4; + if (next > LABEL_INDEX) { + // we need to set up a continuation line + finishHeaderLine(RinexLabels.SYS_PHASE_SHIFT); + outputField("", 18, true); + next = column + 4; + } + outputField(sis.getSystem().getKey(), next - 2); + outputField(PADDED_TWO_DIGITS_INTEGER, sis.getTwoDigitsRinexPRN(), next); + } + } + finishHeaderLine(RinexLabels.SYS_PHASE_SHIFT); + } + + if (header.getFormatVersion() >= 3.01) { + if (!header.getGlonassChannels().isEmpty()) { + // GLONASS SLOT / FRQ # + outputField(THREE_DIGITS_INTEGER, header.getGlonassChannels().size(), 3); + outputField("", 4, true); + for (final GlonassSatelliteChannel channel : header.getGlonassChannels()) { + int next = column + 7; + if (next > LABEL_INDEX) { + // we need to set up a continuation line + finishHeaderLine(RinexLabels.GLONASS_SLOT_FRQ_NB); + outputField("", 4, true); + next = column + 7; + } + outputField(channel.getSatellite().getSystem().getKey(), next - 6); + outputField(PADDED_TWO_DIGITS_INTEGER, channel.getSatellite().getPRN(), next - 4); + outputField(TWO_DIGITS_INTEGER, channel.getK(), next - 1); + outputField("", next, true); + } + } + finishHeaderLine(RinexLabels.GLONASS_SLOT_FRQ_NB); + } + + if (header.getFormatVersion() >= 3.0) { + // GLONASS COD/PHS/BIS + if (Double.isNaN(header.getC1cCodePhaseBias())) { + outputField("", 13, true); + } else { + outputField(ObservationType.C1C.name(), 4, false); + outputField("", 5, true); + outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC1cCodePhaseBias(), 13); + } + if (Double.isNaN(header.getC1pCodePhaseBias())) { + outputField("", 26, true); + } else { + outputField(ObservationType.C1P.name(), 17, false); + outputField("", 18, true); + outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC1pCodePhaseBias(), 26); + } + if (Double.isNaN(header.getC2cCodePhaseBias())) { + outputField("", 39, true); + } else { + outputField(ObservationType.C2C.name(), 30, false); + outputField("", 31, true); + outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC2cCodePhaseBias(), 39); + } + if (Double.isNaN(header.getC2pCodePhaseBias())) { + outputField("", 52, true); + } else { + outputField(ObservationType.C2P.name(), 43, false); + outputField("", 44, true); + outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC2pCodePhaseBias(), 52); + } + finishHeaderLine(RinexLabels.GLONASS_COD_PHS_BIS); + } + + // LEAP SECONDS + if (header.getLeapSeconds() > 0) { + outputField(SIX_DIGITS_INTEGER, header.getLeapSeconds(), 6); + if (header.getFormatVersion() >= 3.0) { + outputField(SIX_DIGITS_INTEGER, header.getLeapSecondsFuture(), 12); + outputField(SIX_DIGITS_INTEGER, header.getLeapSecondsWeekNum(), 18); + outputField(SIX_DIGITS_INTEGER, header.getLeapSecondsDayNum(), 24); + } + finishHeaderLine(RinexLabels.LEAP_SECONDS); + } + + // # OF SATELLITES + if (header.getNbSat() >= 0) { + outputField(SIX_DIGITS_INTEGER, header.getNbSat(), 6); + finishHeaderLine(RinexLabels.NB_OF_SATELLITES); + } + + // PRN / # OF OBS + for (final Map.Entry> entry1 : header.getNbObsPerSat().entrySet()) { + final SatInSystem sis = entry1.getKey(); + outputField(sis.getSystem().getKey(), 4); + outputField(PADDED_TWO_DIGITS_INTEGER, sis.getTwoDigitsRinexPRN(), 6); + for (final Map.Entry entry2 : entry1.getValue().entrySet()) { + int next = column + 6; + if (next > LABEL_INDEX) { + // we need to set up a continuation line + finishHeaderLine(RinexLabels.PRN_NB_OF_OBS); + outputField("", 6, true); + next = column + 6; + } + outputField(SIX_DIGITS_INTEGER, entry2.getValue(), next); + } + finishHeaderLine(RinexLabels.PRN_NB_OF_OBS); + } + + // END OF HEADER + writeHeaderLine("", RinexLabels.END); + + } + + /** Write one observation data set. + *

          + * Note that this writers output only regular observations, so + * the event flag is always set to 0 + *

          + * @param observationDataSet observation data set to write + * @exception IOException if an I/O error occurs. + */ + public void writeObservationDataSet(final ObservationDataSet observationDataSet) + throws IOException { + + // check header has already been written + if (savedHeader == null) { + throw new OrekitException(OrekitMessages.HEADER_NOT_WRITTEN, outputName); + } + + if (!pending.isEmpty() && observationDataSet.durationFrom(pending.get(0).getDate()) > EPS_DATE) { + // the specified observation belongs to the next batch + // we must process the current batch of pending observations + processPending(); + } + + // add the observation to the pending list, so it is written later on + pending.add(observationDataSet); + + } + + /** Process all pending measurements. + * @exception IOException if an I/O error occurs. + */ + private void processPending() throws IOException { + + if (!pending.isEmpty()) { + + // write the batch of pending observations + if (savedHeader.getFormatVersion() < 3.0) { + writePendingRinex2Observations(); + } else { + writePendingRinex34Observations(); + } + + // prepare for next batch + pending.clear(); + + } + + } + + /** Write one observation data set in RINEX 2 format. + * @exception IOException if an I/O error occurs. + */ + public void writePendingRinex2Observations() throws IOException { + + final ObservationDataSet first = pending.get(0); + + // EPOCH/SAT + final DateTimeComponents dtc = first.getDate().getComponents(timeScale); + outputField("", 1, true); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getYear() % 100, 3); + outputField("", 4, true); + outputField(TWO_DIGITS_INTEGER, dtc.getDate().getMonth(), 6); + outputField("", 7, true); + outputField(TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 9); + outputField("", 10, true); + outputField(TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 12); + outputField("", 13, true); + outputField(TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 15); + outputField(ELEVEN_SEVEN_DIGITS_FLOAT, dtc.getTime().getSecond(), 26); + + // event flag + outputField("", 28, true); + if (first.getEventFlag() == 0) { + outputField("", 29, true); + } else { + outputField(ONE_DIGIT_INTEGER, first.getEventFlag(), 29); + } + + // list of satellites and receiver clock offset + outputField(THREE_DIGITS_INTEGER, pending.size(), 32); + boolean offsetWritten = false; + final double clockOffset = first.getRcvrClkOffset(); + for (final ObservationDataSet ods : pending) { + int next = column + 3; + if (next > 68) { + // we need to set up a continuation line + if (clockOffset != 0.0) { + outputField(TWELVE_NINE_DIGITS_FLOAT, clockOffset, 80); + } + offsetWritten = true; + finishLine(); + outputField("", 32, true); + next = column + 3; + } + outputField(ods.getSatellite().getSystem().getKey(), next - 2); + outputField(PADDED_TWO_DIGITS_INTEGER, ods.getSatellite().getTwoDigitsRinexPRN(), next); + } + if (!offsetWritten && clockOffset != 0.0) { + outputField("", 68, true); + outputField(TWELVE_NINE_DIGITS_FLOAT, first.getRcvrClkOffset(), 80); + } + finishLine(); + + // observations per se + for (final ObservationDataSet ods : pending) { + for (final ObservationData od : ods.getObservationData()) { + int next = column + 16; + if (next > 80) { + // we need to set up a continuation line + finishLine(); + next = column + 16; + } + final double scaling = getScaling(od.getObservationType(), ods.getSatellite().getSystem()); + outputField(FOURTEEN_THREE_DIGITS_FLOAT, scaling * od.getValue(), next - 2); + if (od.getLossOfLockIndicator() == 0) { + outputField("", next - 1, true); + } else { + outputField(ONE_DIGIT_INTEGER, od.getLossOfLockIndicator(), next - 1); + } + if (od.getSignalStrength() == 0) { + outputField("", next, true); + } else { + outputField(ONE_DIGIT_INTEGER, od.getSignalStrength(), next); + } + } + finishLine(); + } + + } + + /** Write one observation data set in RINEX 3/4 format. + * @exception IOException if an I/O error occurs. + */ + public void writePendingRinex34Observations() + throws IOException { + + final ObservationDataSet first = pending.get(0); + + // EPOCH/SAT + final DateTimeComponents dtc = first.getDate().getComponents(timeScale); + outputField(">", 2, true); + outputField(FOUR_DIGITS_INTEGER, dtc.getDate().getYear(), 6); + outputField("", 7, true); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getMonth(), 9); + outputField("", 10, true); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 12); + outputField("", 13, true); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 15); + outputField("", 16, true); + outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 18); + outputField(ELEVEN_SEVEN_DIGITS_FLOAT, dtc.getTime().getSecond(), 29); + + // event flag + outputField("", 31, true); + if (first.getEventFlag() == 0) { + outputField("", 32, true); + } else { + outputField(ONE_DIGIT_INTEGER, first.getEventFlag(), 32); + } + + // number of satellites and receiver clock offset + outputField(THREE_DIGITS_INTEGER, pending.size(), 35); + if (first.getRcvrClkOffset() != 0.0) { + outputField("", 41, true); + outputField(FIFTEEN_TWELVE_DIGITS_FLOAT, first.getRcvrClkOffset(), 56); + } + finishLine(); + + // observations per se + for (final ObservationDataSet ods : pending) { + outputField(ods.getSatellite().getSystem().getKey(), 1); + outputField(PADDED_TWO_DIGITS_INTEGER, ods.getSatellite().getTwoDigitsRinexPRN(), 3); + for (final ObservationData od : ods.getObservationData()) { + final int next = column + 16; + final double scaling = getScaling(od.getObservationType(), ods.getSatellite().getSystem()); + outputField(FOURTEEN_THREE_DIGITS_FLOAT, scaling * od.getValue(), next - 2); + if (od.getLossOfLockIndicator() == 0) { + outputField("", next - 1, true); + } else { + outputField(ONE_DIGIT_INTEGER, od.getLossOfLockIndicator(), next - 1); + } + if (od.getSignalStrength() == 0) { + outputField("", next, true); + } else { + outputField(ONE_DIGIT_INTEGER, od.getSignalStrength(), next); + } + } + finishLine(); + } + + } + + /** Write one header string. + * @param s string data (may be null) + * @param label line label + * @throws IOException if an I/O error occurs. + */ + private void writeHeaderLine(final String s, final RinexLabels label) throws IOException { + if (s != null) { + outputField(s, s.length(), true); + finishHeaderLine(label); + } + } + + /** Write one header vector. + * @param vector vector data (may be null) + * @param label line label + * @throws IOException if an I/O error occurs. + */ + private void writeHeaderLine(final Vector3D vector, final RinexLabels label) throws IOException { + if (vector != null) { + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, vector.getX(), 14); + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, vector.getY(), 28); + outputField(FOURTEEN_FOUR_DIGITS_FLOAT, vector.getZ(), 42); + finishHeaderLine(label); + } + } + + /** Finish one header line. + * @param label line label + * @throws IOException if an I/O error occurs. + */ + private void finishHeaderLine(final RinexLabels label) throws IOException { + for (int i = column; i < LABEL_INDEX; ++i) { + output.append(' '); + } + output.append(label.getLabel()); + finishLine(); + } + + /** Finish one line. + * @throws IOException if an I/O error occurs. + */ + private void finishLine() throws IOException { + + // pending line + output.append(System.lineSeparator()); + lineNumber++; + column = 0; + + // emit comments that should be placed at next lines + for (final RinexComment comment : savedComments) { + if (comment.getLineNumber() == lineNumber) { + outputField(comment.getText(), LABEL_INDEX, true); + output.append(RinexLabels.COMMENT.getLabel()); + output.append(System.lineSeparator()); + lineNumber++; + column = 0; + } else if (comment.getLineNumber() > lineNumber) { + break; + } + } + + } + + /** Output one single character field. + * @param c field value + * @param next target column for next field + * @throws IOException if an I/O error occurs. + */ + private void outputField(final char c, final int next) throws IOException { + outputField(Character.toString(c), next, false); + } + + /** Output one integer field. + * @param format format to use + * @param value field value + * @param next target column for next field + * @throws IOException if an I/O error occurs. + */ + private void outputField(final String format, final int value, final int next) throws IOException { + outputField(String.format(Locale.US, format, value), next, false); + } + + /** Output one double field. + * @param format format to use + * @param value field value + * @param next target column for next field + * @throws IOException if an I/O error occurs. + */ + private void outputField(final String format, final double value, final int next) throws IOException { + if (Double.isNaN(value)) { + // NaN values are replaced by blank fields + outputField("", next, true); + } else { + outputField(String.format(Locale.US, format, value), next, false); + } + } + + /** Output one field. + * @param field field to output + * @param next target column for next field + * @param leftJustified if true, field is left-justified + * @throws IOException if an I/O error occurs. + */ + private void outputField(final String field, final int next, final boolean leftJustified) throws IOException { + final int padding = next - (field == null ? 0 : field.length()) - column; + if (leftJustified && field != null) { + output.append(field); + } + for (int i = 0; i < padding; ++i) { + output.append(' '); + } + if (!leftJustified && field != null) { + output.append(field); + } + column = next; + } + + /** Get the scaling factor for an observation. + * @param type type of observation + * @param system satellite system for the observation + * @return scaling factor + */ + private double getScaling(final ObservationType type, final SatelliteSystem system) { + + for (final ScaleFactorCorrection scaleFactorCorrection : savedHeader.getScaleFactorCorrections(system)) { + // check if the next Observation Type to read needs to be scaled + if (scaleFactorCorrection.getTypesObsScaled().contains(type)) { + return scaleFactorCorrection.getCorrection(); + } + } + + // no scaling + return 1.0; + + } + +} diff --git a/src/main/java/org/orekit/files/rinex/observation/ScaleFactorCorrection.java b/src/main/java/org/orekit/files/rinex/observation/ScaleFactorCorrection.java new file mode 100644 index 0000000000..ed835c1e03 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/ScaleFactorCorrection.java @@ -0,0 +1,59 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.observation; +import java.util.List; + +import org.orekit.gnss.ObservationType; + +/** Scale Factor to be applied. + * Contains the scale factors of 10 applied to the data before + * being stored into the RINEX file. + * @since 12.0 + */ +public class ScaleFactorCorrection { + + /** List of Observations types that have been scaled. */ + private final List typesObsScaleFactor; + + /** Factor to divide stored observations with before use. */ + private final double scaleFactor; + + /** Simple constructor. + * @param scaleFactor Factor to divide stored observations (1,10,100,1000) + * @param typesObsScaleFactor List of Observations types that have been scaled + */ + public ScaleFactorCorrection(final double scaleFactor, + final List typesObsScaleFactor) { + this.scaleFactor = scaleFactor; + this.typesObsScaleFactor = typesObsScaleFactor; + } + + /** Get the Scale Factor. + * @return Scale Factor + */ + public double getCorrection() { + return scaleFactor; + } + + /** Get the list of Observation Types scaled. + * @return List of Observation types scaled + */ + public List getTypesObsScaled() { + return typesObsScaleFactor; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/observation/package-info.java b/src/main/java/org/orekit/files/rinex/observation/package-info.java new file mode 100644 index 0000000000..bc895c55ba --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/observation/package-info.java @@ -0,0 +1,25 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package provides classes related to RINEX observation files. + * + * @author Luc Maisonobe + * @since 12.0 + * + */ +package org.orekit.files.rinex.observation; diff --git a/src/main/java/org/orekit/files/rinex/package-info.java b/src/main/java/org/orekit/files/rinex/package-info.java new file mode 100644 index 0000000000..f343cfa977 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/package-info.java @@ -0,0 +1,25 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package is gathers sub-packages providing parsers/writers for various RINEX files. + * + * @author Bryan Cazabonne + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.files.rinex; diff --git a/src/main/java/org/orekit/files/rinex/section/RinexBaseHeader.java b/src/main/java/org/orekit/files/rinex/section/RinexBaseHeader.java new file mode 100644 index 0000000000..bed4973388 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/section/RinexBaseHeader.java @@ -0,0 +1,253 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.section; + +import org.orekit.files.rinex.utils.RinexFileType; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateTimeComponents; + +/** Base container for Rinex headers. + * @since 12.0 + */ +public class RinexBaseHeader { + + /** File type . */ + private final RinexFileType fileType; + + /** Rinex format Version. */ + private double formatVersion; + + /** Satellite System of the Rinex file (G/R/S/E/M). */ + private SatelliteSystem satelliteSystem; + + /** Name of the program creating current file. */ + private String programName; + + /** Name of the creator of the current file. */ + private String runByName; + + /** Date of the file creation. */ + private DateTimeComponents creationDateComponents; + + /** Time zone of the file creation. */ + private String creationTimeZone; + + /** Creation date as absolute date. */ + private AbsoluteDate creationDate; + + /** Digital Object Identifier. + * @since 12.0 + */ + private String doi; + + /** License of use. + * @since 12.0 + */ + private String license; + + /** Station information. + * @since 12.0 + */ + private String stationInformation; + + /** Simple constructor. + * @param fileType file type + */ + protected RinexBaseHeader(final RinexFileType fileType) { + this.fileType = fileType; + this.formatVersion = Double.NaN; + } + + /** + * Get the file type. + * @return file type + */ + public RinexFileType getFileType() { + return fileType; + } + + /** + * Getter for the format version. + * @return the format version + */ + public double getFormatVersion() { + return formatVersion; + } + + /** + * Setter for the format version. + * @param formatVersion the format version to set + */ + public void setFormatVersion(final double formatVersion) { + this.formatVersion = formatVersion; + } + + /** + * Getter for the satellite system. + *

          + * Not specified for RINEX 2.X versions (value is null). + *

          + * @return the satellite system + */ + public SatelliteSystem getSatelliteSystem() { + return satelliteSystem; + } + + /** + * Setter for the satellite system. + * @param satelliteSystem the satellite system to set + */ + public void setSatelliteSystem(final SatelliteSystem satelliteSystem) { + this.satelliteSystem = satelliteSystem; + } + + /** + * Getter for the program name. + * @return the program name + */ + public String getProgramName() { + return programName; + } + + /** + * Setter for the program name. + * @param programName the program name to set + */ + public void setProgramName(final String programName) { + this.programName = programName; + } + + /** + * Getter for the run/by name. + * @return the run/by name + */ + public String getRunByName() { + return runByName; + } + + /** + * Setter for the run/by name. + * @param runByName the run/by name to set + */ + public void setRunByName(final String runByName) { + this.runByName = runByName; + } + + /** + * Getter for the creation date of the file as a string. + * @return the creation date + */ + public DateTimeComponents getCreationDateComponents() { + return creationDateComponents; + } + + /** + * Setter for the creation date as a string. + * @param creationDateComponents the creation date to set + */ + public void setCreationDateComponents(final DateTimeComponents creationDateComponents) { + this.creationDateComponents = creationDateComponents; + } + + /** + * Getter for the creation time zone of the file as a string. + * @return the creation time zone as a string + */ + public String getCreationTimeZone() { + return creationTimeZone; + } + + /** + * Setter for the creation time zone. + * @param creationTimeZone the creation time zone to set + */ + public void setCreationTimeZone(final String creationTimeZone) { + this.creationTimeZone = creationTimeZone; + } + + /** + * Getter for the creation date. + * @return the creation date + */ + public AbsoluteDate getCreationDate() { + return creationDate; + } + + /** + * Setter for the creation date. + * @param creationDate the creation date to set + */ + public void setCreationDate(final AbsoluteDate creationDate) { + this.creationDate = creationDate; + } + + /** + * Getter for the Digital Object Information. + * @return the Digital Object Information + * @since 12.0 + */ + public String getDoi() { + return doi; + } + + /** + * Setter for the Digital Object Information. + * @param doi the Digital Object Information to set + * @since 12.0 + */ + public void setDoi(final String doi) { + this.doi = doi; + } + + /** + * Getter for the license of use. + * @return the license of use + * @since 12.0 + */ + public String getLicense() { + return license; + } + + /** + * Setter for the license of use. + * @param license the license of use + * @since 12.0 + */ + public void setLicense(final String license) { + this.license = license; + } + + /** + * Getter for the station information. + * @return the station information + * @since 12.0 + */ + public String getStationInformation() { + return stationInformation; + } + + /** + * Setter for the station information. + * @param stationInformation the station information to set + * @since 12.0 + */ + public void setStationInformation(final String stationInformation) { + this.stationInformation = stationInformation; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/section/RinexComment.java b/src/main/java/org/orekit/files/rinex/section/RinexComment.java new file mode 100644 index 0000000000..4e0ff90fe8 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/section/RinexComment.java @@ -0,0 +1,55 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.section; + +/** Container for comment in RINEX file. + * @author Luc Maisonobe + * @since 12.0 + * + */ +public class RinexComment { + + /** Line number. */ + private final int lineNumber; + + /** Text. */ + private final String text; + + /** Simple constructor. + * @param lineNumber line number + * @param text text + */ + public RinexComment(final int lineNumber, final String text) { + this.lineNumber = lineNumber; + this.text = text; + } + + /** Get the line number. + * @return line number + */ + public int getLineNumber() { + return lineNumber; + } + + /** Get the text. + * @return text + */ + public String getText() { + return text; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/section/RinexLabels.java b/src/main/java/org/orekit/files/rinex/section/RinexLabels.java new file mode 100644 index 0000000000..58dc6935e5 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/section/RinexLabels.java @@ -0,0 +1,176 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.section; + +/** Labels for Rinex files. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum RinexLabels { + + /** Version, file type and satellite system. */ + VERSION("RINEX VERSION / TYPE"), + + /** Generating program and emiting agency. */ + PROGRAM("PGM / RUN BY / DATE"), + + /** Comments. */ + COMMENT("COMMENT"), + + /** Marker name. */ + MARKER_NAME("MARKER NAME"), + + /** Marker number. */ + MARKER_NUMBER("MARKER NUMBER"), + + /** Marker type. */ + MARKER_TYPE("MARKER TYPE"), + + /** Observer agency. */ + OBSERVER_AGENCY("OBSERVER / AGENCY"), + + /** Receiver number, type and version. */ + REC_NB_TYPE_VERS("REC # / TYPE / VERS"), + + /** Antenna number and type. */ + ANT_NB_TYPE("ANT # / TYPE"), + + /** Approximative position. */ + APPROX_POSITION_XYZ("APPROX POSITION XYZ"), + + /** Antenna reference point. */ + ANTENNA_DELTA_H_E_N("ANTENNA: DELTA H/E/N"), + + /** Antenna reference point. */ + ANTENNA_DELTA_X_Y_Z("ANTENNA: DELTA X/Y/Z"), + + /** Antenna phase center. */ + ANTENNA_PHASE_CENTER("ANTENNA: PHASECENTER"), + + /** Antenna bore sight. */ + ANTENNA_B_SIGHT_XYZ("ANTENNA: B.SIGHT XYZ"), + + /** Antenna zero direction. */ + ANTENNA_ZERODIR_AZI("ANTENNA: ZERODIR AZI"), + + /** Antenna zero direction. */ + ANTENNA_ZERODIR_XYZ("ANTENNA: ZERODIR XYZ"), + + /** Wavelength factors. */ + WAVELENGTH_FACT_L1_2("WAVELENGTH FACT L1/2"), + + /** Observations scale factor. */ + OBS_SCALE_FACTOR("OBS SCALE FACTOR"), + + /** Center of mass. */ + CENTER_OF_MASS_XYZ("CENTER OF MASS: XYZ"), + + /** DOI. */ + DOI("DOI"), + + /** Llicense. */ + LICENSE("LICENSE OF USE"), + + /** Station information.*/ + STATION_INFORMATION("STATION INFORMATION"), + + /** Number and types of observations. */ + NB_TYPES_OF_OBSERV("# / TYPES OF OBSERV"), + + /** Number and types of observations. */ + SYS_NB_TYPES_OF_OBSERV("SYS / # / OBS TYPES"), + + /** Unit of signal strength. */ + SIGNAL_STRENGTH_UNIT("SIGNAL STRENGTH UNIT"), + + /** Observation interval. */ + INTERVAL("INTERVAL"), + + /** Time of first observation. */ + TIME_OF_FIRST_OBS("TIME OF FIRST OBS"), + + /** Time of last observation. */ + TIME_OF_LAST_OBS("TIME OF LAST OBS"), + + /** Indicator of receiver clock offset application. */ + RCV_CLOCK_OFFS_APPL("RCV CLOCK OFFS APPL"), + + /** Differential code bias corrections. */ + SYS_DCBS_APPLIED("SYS / DCBS APPLIED"), + + /** Phase center variations corrections. */ + SYS_PCVS_APPLIED("SYS / PCVS APPLIED"), + + /** Scale factor. */ + SYS_SCALE_FACTOR("SYS / SCALE FACTOR"), + + /** Phase shift. */ + SYS_PHASE_SHIFT("SYS / PHASE SHIFT", "SYS / PHASE SHIFTS"), + + /** GLONASS slot and frequency number. */ + GLONASS_SLOT_FRQ_NB("GLONASS SLOT / FRQ #"), + + /** GLONASS phase bias corrections. */ + GLONASS_COD_PHS_BIS("GLONASS COD/PHS/BIS"), + + /** Leap seconds. */ + LEAP_SECONDS("LEAP SECONDS"), + + /** Number of satellites. */ + NB_OF_SATELLITES("# OF SATELLITES"), + + /** PRN and number of observations . */ + PRN_NB_OF_OBS("PRN / # OF OBS"), + + /** End of header. */ + END("END OF HEADER"); + + /** Labels. */ + private final String[] labels; + + /** Simple constructor. + *

          + * There may be several labels allowed, as some receiver generate files with typos… + * Only the first label is considered official + *

          + * @param labels labels + */ + RinexLabels(final String... labels) { + this.labels = labels.clone(); + } + + /** Check if label matches. + * @param label label to check + * @return true if label matches one of the allowed label + */ + public boolean matches(final String label) { + for (String allowed : labels) { + if (allowed.equals(label)) { + return true; + } + } + return false; + } + + /** Get the first label. + * @return first label + */ + public String getLabel() { + return labels[0]; + } + +} diff --git a/src/main/java/org/orekit/files/rinex/section/package-info.java b/src/main/java/org/orekit/files/rinex/section/package-info.java new file mode 100644 index 0000000000..f48c1dec70 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/section/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package contains class managing the sections in RINEX files. + * + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.files.rinex.section; diff --git a/src/main/java/org/orekit/files/rinex/utils/RinexFileType.java b/src/main/java/org/orekit/files/rinex/utils/RinexFileType.java new file mode 100644 index 0000000000..2064c44114 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/utils/RinexFileType.java @@ -0,0 +1,61 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.utils; + +import java.util.HashMap; +import java.util.Map; + +/** Enumerate for RINEX files types. + * @since 12.0 + */ +public enum RinexFileType { + + /** Rinex Observation. */ + OBSERVATION("O"), + + /** Rinex navigation (G is for Glonass navigation, in Rinex 2.X). */ + NAVIGATION("N", "G"); + + /** Parsing map. */ + private static final Map KEYS_MAP = new HashMap<>(); + static { + for (final RinexFileType type : values()) { + for (final String key : type.keys) { + KEYS_MAP.put(key, type); + } + } + } + + /** Key of the file type. */ + private final String[] keys; + + /** Simple constructor. + * @param keys keys of the file type + */ + RinexFileType(final String... keys) { + this.keys = keys.clone(); + } + + /** Parse the string to get the type. + * @param s string to parse (must correspond to a one-character key) + * @return the type corresponding to the string + */ + public static RinexFileType parseRinexFileType(final String s) { + return KEYS_MAP.get(s); + } + +} diff --git a/src/main/java/org/orekit/files/rinex/utils/package-info.java b/src/main/java/org/orekit/files/rinex/utils/package-info.java new file mode 100644 index 0000000000..29a62817f2 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/utils/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package contains utilities shared by many RINEX parsers/writers. + * + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.files.rinex.utils; diff --git a/src/main/java/org/orekit/files/rinex/utils/parsing/RinexUtils.java b/src/main/java/org/orekit/files/rinex/utils/parsing/RinexUtils.java new file mode 100644 index 0000000000..c75d9762e8 --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/utils/parsing/RinexUtils.java @@ -0,0 +1,324 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.rinex.utils.parsing; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.rinex.RinexFile; +import org.orekit.files.rinex.section.RinexBaseHeader; +import org.orekit.files.rinex.section.RinexComment; +import org.orekit.files.rinex.utils.RinexFileType; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.gnss.TimeSystem; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateComponents; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.Month; +import org.orekit.time.TimeComponents; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScales; + +/** Utilities for RINEX various messages files. + * @author Luc Maisonobe + * @since 12.0 + * + */ +public class RinexUtils { + + /** Index of label in header lines. */ + public static final int LABEL_INDEX = 60; + + /** Pattern for splitting date, time and time zone. */ + private static final Pattern SPLITTING_PATTERN = Pattern.compile("([0-9A-Za-z/-]+) *([0-9:]+) *([A-Z][A-Z0-9_-]*)?"); + + /** Pattern for dates with month abbrevation. */ + private static final Pattern DATE_DD_MMM_YY_PATTERN = Pattern.compile("([0-9]{2})-([A-Za-z]{3})-([0-9]{2})"); + + /** Pattern for dates in ISO-8601 complete representation (basic or extended). */ + private static final Pattern DATE_ISO_8601_PATTERN = Pattern.compile("([0-9]{4})-?([0-9]{2})-?([0-9]{2})"); + + /** Pattern for dates in european format. */ + private static final Pattern DATE_EUROPEAN_PATTERN = Pattern.compile("([0-9]{2})/([0-9]{2})/([0-9]{2})"); + + /** Pattern for time. */ + private static final Pattern TIME_PATTERN = Pattern.compile("([0-9]{2}):?([0-9]{2})(?::?([0-9]{2}))?"); + + /** Private constructor. + *

          This class is a utility class, it should neither have a public + * nor a default constructor. This private constructor prevents + * the compiler from generating one automatically.

          + */ + private RinexUtils() { + } + + /** Get the trimmed label from a header line. + * @param line header line to parse + * @return trimmed label + */ + public static String getLabel(final String line) { + return line.length() < LABEL_INDEX ? "" : line.substring(LABEL_INDEX).trim(); + } + + /** Check if a header line matches an expected label. + * @param line header line to check + * @param label expected label + * @return true if line matches expected label + */ + public static boolean matchesLabel(final String line, final String label) { + return getLabel(line).equals(label); + } + + /** Parse version, file type and satellite system. + * @param line line to parse + * @param name file name (for error message generation) + * @param header header to fill with parsed data + * @param supportedVersions supported versions + */ + public static void parseVersionFileTypeSatelliteSystem(final String line, final String name, + final RinexBaseHeader header, + final double... supportedVersions) { + + // Rinex version + final double parsedVersion = parseDouble(line, 0, 9); + + boolean found = false; + for (final double supported : supportedVersions) { + if (FastMath.abs(parsedVersion - supported) < 1.0e-4) { + found = true; + break; + } + } + if (!found) { + final StringBuilder builder = new StringBuilder(); + for (final double supported : supportedVersions) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(supported); + } + throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT_VERSION, + parsedVersion, name, builder.toString()); + } + header.setFormatVersion(parsedVersion); + + // File type + if (header.getFileType() != RinexFileType.parseRinexFileType(parseString(line, 20, 1))) { + throw new OrekitException(OrekitMessages.WRONG_PARSING_TYPE, name); + } + + // Satellite system + switch (header.getFileType()) { + case OBSERVATION: + // for observation files, the satellite system is in column 40, and empty defaults to GPS + header.setSatelliteSystem(SatelliteSystem.parseSatelliteSystemWithGPSDefault(parseString(line, 40, 1))); + break; + case NAVIGATION: { + if (header.getFormatVersion() < 3.0) { + // the satellite system is hidden within the entry, with GPS as default + + // set up default + header.setSatelliteSystem(SatelliteSystem.GPS); + + // look if default is overridden somewhere in the entry + final String entry = parseString(line, 0, LABEL_INDEX).toUpperCase(); + for (final SatelliteSystem satelliteSystem : SatelliteSystem.values()) { + if (entry.contains(satelliteSystem.name())) { + // we found a satellite system hidden in the middle of the line + header.setSatelliteSystem(satelliteSystem); + break; + } + } + + } else { + // the satellite system is in column 40 for 3.X and later + header.setSatelliteSystem(SatelliteSystem.parseSatelliteSystemWithGPSDefault(parseString(line, 40, 1))); + } + break; + } + default: + // this should never happen + throw new OrekitInternalError(null); + } + + } + + /** Parse program, run/by and date. + * @param line line to parse + * @param lineNumber line number + * @param name file name (for error message generation) + * @param timeScales the set of time scales used for parsing dates. + * @param header header to fill with parsed data + */ + public static void parseProgramRunByDate(final String line, final int lineNumber, + final String name, final TimeScales timeScales, + final RinexBaseHeader header) { + + // Name of the generating program + header.setProgramName(parseString(line, 0, 20)); + + // Name of the run/by name + header.setRunByName(parseString(line, 20, 20)); + + // there are several variations for date formatting in the PGM / RUN BY / DATE line + + // in versions 2.x, the pattern is expected to be: + // XXRINEXO V9.9 AIUB 24-MAR-01 14:43 PGM / RUN BY / DATE + // however, we have also found this: + // teqc 2016Nov7 root 20180130 10:38:06UTCPGM / RUN BY / DATE + // BJFMTLcsr UTCSR 2007-09-30 05:30:06 PGM / RUN BY / DATE + // NEODIS TAS 27/05/22 10:28 PGM / RUN BY / DATE + + // in versions 3.x, the pattern is expected to be: + // sbf2rin-11.3.3 20180130 002558 LCL PGM / RUN BY / DATE + // however, we have also found: + // NetR9 5.03 Receiver Operator 11-JAN-16 00:00:00 PGM / RUN BY / DATE + + // so we cannot rely on the format version, we have to check several variations + final Matcher splittingMatcher = SPLITTING_PATTERN.matcher(parseString(line, 40, 20)); + if (splittingMatcher.matches()) { + + // date part + final DateComponents dc; + final Matcher abbrevMatcher = DATE_DD_MMM_YY_PATTERN.matcher(splittingMatcher.group(1)); + if (abbrevMatcher.matches()) { + // hoping this obsolete format will not be used past year 2079… + dc = new DateComponents(convert2DigitsYear(Integer.parseInt(abbrevMatcher.group(3))), + Month.parseMonth(abbrevMatcher.group(2)).getNumber(), + Integer.parseInt(abbrevMatcher.group(1))); + } else { + final Matcher isoMatcher = DATE_ISO_8601_PATTERN.matcher(splittingMatcher.group(1)); + if (isoMatcher.matches()) { + dc = new DateComponents(Integer.parseInt(isoMatcher.group(1)), + Integer.parseInt(isoMatcher.group(2)), + Integer.parseInt(isoMatcher.group(3))); + } else { + final Matcher europeanMatcher = DATE_EUROPEAN_PATTERN.matcher(splittingMatcher.group(1)); + if (europeanMatcher.matches()) { + dc = new DateComponents(convert2DigitsYear(Integer.parseInt(europeanMatcher.group(3))), + Integer.parseInt(europeanMatcher.group(2)), + Integer.parseInt(europeanMatcher.group(1))); + } else { + dc = null; + } + } + } + + // time part + final TimeComponents tc; + final Matcher timeMatcher = TIME_PATTERN.matcher(splittingMatcher.group(2)); + if (timeMatcher.matches()) { + tc = new TimeComponents(Integer.parseInt(timeMatcher.group(1)), + Integer.parseInt(timeMatcher.group(2)), + timeMatcher.group(3) != null ? Integer.parseInt(timeMatcher.group(3)) : 0); + } else { + tc = null; + } + + // zone part + final String zone = splittingMatcher.groupCount() > 2 ? splittingMatcher.group(3) : ""; + + if (dc != null && tc != null) { + // we successfully parsed everything + final DateTimeComponents dtc = new DateTimeComponents(dc, tc); + header.setCreationDateComponents(dtc); + final TimeScale timeScale = zone == null ? + timeScales.getUTC() : + TimeSystem.parseTimeSystem(zone).getTimeScale(timeScales); + header.setCreationDate(new AbsoluteDate(dtc, timeScale)); + header.setCreationTimeZone(zone); + return; + } + + } + + // we were not able to extract date + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + + } + + /** Parse a comment. + * @param lineNumber line number + * @param line line to parse + * @param rinexFile rinex file + */ + public static void parseComment(final int lineNumber, final String line, final RinexFile rinexFile) { + rinexFile.addComment(new RinexComment(lineNumber, parseString(line, 0, 60))); + } + + /** + * Parse a double value. + * @param line line to parse + * @param startIndex start index + * @param size size of the value + * @return the parsed value + */ + public static double parseDouble(final String line, final int startIndex, final int size) { + final String subString = parseString(line, startIndex, size); + if (subString == null || subString.isEmpty()) { + return Double.NaN; + } else { + return Double.parseDouble(subString.replace('D', 'E').trim()); + } + } + + /** + * Parse an integer value. + * @param line line to parse + * @param startIndex start index + * @param size size of the value + * @return the parsed value + */ + public static int parseInt(final String line, final int startIndex, final int size) { + final String subString = parseString(line, startIndex, size); + if (subString == null || subString.isEmpty()) { + return 0; + } else { + return Integer.parseInt(subString.trim()); + } + } + + /** + * Parse a string value. + * @param line line to parse + * @param startIndex start index + * @param size size of the value + * @return the parsed value + */ + public static String parseString(final String line, final int startIndex, final int size) { + if (line.length() > startIndex) { + return line.substring(startIndex, FastMath.min(line.length(), startIndex + size)).trim(); + } else { + return null; + } + } + + /** Convert a 2 digits year to a complete year. + * @param yy year between 0 and 99 + * @return complete year + * @since 12.0 + */ + public static int convert2DigitsYear(final int yy) { + return yy >= 80 ? (yy + 1900) : (yy + 2000); + } + +} diff --git a/src/main/java/org/orekit/files/rinex/utils/parsing/package-info.java b/src/main/java/org/orekit/files/rinex/utils/parsing/package-info.java new file mode 100644 index 0000000000..0827b10f0c --- /dev/null +++ b/src/main/java/org/orekit/files/rinex/utils/parsing/package-info.java @@ -0,0 +1,25 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package contains classes related to the parsing + * of RINEX files. + * + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.files.rinex.utils.parsing; diff --git a/src/main/java/org/orekit/files/sinex/Dcb.java b/src/main/java/org/orekit/files/sinex/Dcb.java new file mode 100644 index 0000000000..02f769fb49 --- /dev/null +++ b/src/main/java/org/orekit/files/sinex/Dcb.java @@ -0,0 +1,202 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.sinex; + +import java.util.HashMap; +import java.util.HashSet; + +import org.hipparchus.util.Pair; +import org.orekit.gnss.ObservationType; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeSpanMap; + +/** + * Class to store DCB Solution data parsed in the SinexLoader. + *

          + * This class is made to handle both station and satellite DCB data. + * Bias values are stored in TimeSpanMaps associated with a given pair + * of observation codes. Those TimeSpanMaps are stored in a Map, which + * associate a pair of observation code (as a HashSet of ObservationType) + * to a TimeSpanMap, encapsulated in a DCBCode object. + *

          + * @author Louis Aucouturier + * @since 12.0 + */ +public class Dcb { + + /** Ensemble of observation code pairs available for the satellite. */ + private HashSet> observationSets; + + /** + * Ensemble of DCBCode object, identifiable by observation code pairs, + * each containing the corresponding TimeSpanMap of biases (DCB). + */ + private HashMap, DcbCode> dcbCodeMap; + + /** Simple constructor. */ + public Dcb() { + this.observationSets = new HashSet<>(); + this.dcbCodeMap = new HashMap<>(); + } + + // Class to store the TimeSpanMap per DCB Observation Code set + private static class DcbCode { + + /** TimeSpanMap containing the DCB bias values. */ + private TimeSpanMap dcbMap; + + /** + * Simple constructor. + */ + DcbCode() { + this.dcbMap = new TimeSpanMap<>(null); + } + + /** + * Getter for the TimeSpanMap of DCB values. + * + * @return a time span map containing DCB values, for the observation code pair + * corresponding to this DCBCode object + */ + public TimeSpanMap getDcbTimeMap() { + return dcbMap; + } + + } + + /** + * Add the content of a DCB line to the DCBSatellite object. + *

          + * The method check the presence of a Code pair in a map, and add + * values to the corresponding TimeSpanMap. + *

          + * @param obs1 String corresponding to the first code used for the DCB computation + * @param obs2 String corresponding to the second code used for the DCB computation + * @param spanBegin Absolute Date corresponding to the beginning of the validity span for this bias value + * @param spanEnd Absolute Date corresponding to the end of the validity span for this bias value + * @param biasValue DCB bias value expressed in S.I. units + */ + public void addDcbLine(final String obs1, final String obs2, + final AbsoluteDate spanBegin, final AbsoluteDate spanEnd, + final double biasValue) { + + // Setting a pair of observation type. + final Pair observationPair = new Pair<>(ObservationType.valueOf(obs1), ObservationType.valueOf(obs2)); + + // If not present add a new DCBCode to the map, identified by the Observation Pair. + // Then add the bias value and validity period. + if (observationSets.add(observationPair)) { + dcbCodeMap.put(observationPair, new DcbCode()); + } + + dcbCodeMap.get(observationPair).getDcbTimeMap().addValidBetween(biasValue, spanBegin, spanEnd); + } + + /** + * Get the value of the Differential Code Bias for a given observation pair + * and a at a given date. + * + * @param obs1 string corresponding to the first code used for the DCB computation + * @param obs2 string corresponding to the second code used for the DCB computation + * @param date date at which to obtain the DCB + * @return the value of the DCB in S.I. units + */ + public double getDcb(final String obs1, final String obs2, final AbsoluteDate date) { + return getDcb(ObservationType.valueOf(obs1), ObservationType.valueOf(obs2), date); + } + + /** + * Get the value of the Differential Code Bias for a given observation pair + * and a at a given date. + * + * @param obs1 first observation type + * @param obs2 second observation type + * @param date date at which to obtain the DCB + * @return the value of the DCB in S.I. units + */ + public double getDcb(final ObservationType obs1, final ObservationType obs2, final AbsoluteDate date) { + return getTimeSpanMap(obs1, obs2).get(date); + } + + /** + * Get all available observation code pairs for the satellite. + * + * @return HashSet(HashSet(ObservationType)) Observation code pairs obtained. + */ + public HashSet> getAvailableObservationPairs() { + return observationSets; + } + + /** + * Get the minimum valid date for a given observation pair. + * + * @param obs1 sString corresponding to the first code used for the DCB computation + * @param obs2 string corresponding to the second code used for the DCB computation + * @return minimum valid date for the observation pair + */ + public AbsoluteDate getMinimumValidDateForObservationPair(final String obs1, final String obs2) { + return getMinimumValidDateForObservationPair(ObservationType.valueOf(obs1), ObservationType.valueOf(obs2)); + } + + /** + * Get the minimum valid date for a given observation pair. + * + * @param obs1 first observation type + * @param obs2 second observation type + * @return minimum valid date for the observation pair + */ + public AbsoluteDate getMinimumValidDateForObservationPair(final ObservationType obs1, final ObservationType obs2) { + return getTimeSpanMap(obs1, obs2).getFirstTransition().getDate(); + } + + /** + * Get the maximum valid date for a given observation pair. + * + * @param obs1 string corresponding to the first code used for the DCB computation + * @param obs2 string corresponding to the second code used for the DCB computation + * @return maximum valid date for the observation pair + */ + public AbsoluteDate getMaximumValidDateForObservationPair(final String obs1, final String obs2) { + return getMaximumValidDateForObservationPair(ObservationType.valueOf(obs1), ObservationType.valueOf(obs2)); + } + + /** + * Get the maximum valid date for a given observation pair. + * + * @param obs1 first observation type + * @param obs2 second observation type + * @return maximum valid date for the observation pair + */ + public AbsoluteDate getMaximumValidDateForObservationPair(final ObservationType obs1, final ObservationType obs2) { + return getTimeSpanMap(obs1, obs2).getLastTransition().getDate(); + } + + /** + * Return the TimeSpanMap object for a given observation code pair, + * for further operation on the object directly. + * + * @param obs1 first observation type + * @param obs2 second observation type + * @return the time span map for a given observation code pair + */ + private TimeSpanMap getTimeSpanMap(final ObservationType obs1, final ObservationType obs2) { + return dcbCodeMap.get(new Pair<>(obs1, obs2)).getDcbTimeMap(); + } + +} + diff --git a/src/main/java/org/orekit/files/sinex/DcbDescription.java b/src/main/java/org/orekit/files/sinex/DcbDescription.java new file mode 100644 index 0000000000..11810ed5b0 --- /dev/null +++ b/src/main/java/org/orekit/files/sinex/DcbDescription.java @@ -0,0 +1,153 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.sinex; + +import org.orekit.gnss.TimeSystem; + +/** + * Class to store the DCB description parameters. + *

          + * This class gives important parameters from the analysis and defines the fields in the block ’BIAS/SOLUTION’ + * of the loaded Sinex file. + *

          + * @author Louis Aucouturier + * @since 12.0 + */ +public class DcbDescription { + + /** Determination mode used to generate the bias results. */ + private String determinationMethod; + + /** Describes how the included GNSS bias values have to be interpreted and applied. */ + private String biasMode; + + /** Time system. */ + private TimeSystem timeSystem; + + /** Observation sampling interval used for data analysis (s). */ + private int observationSampling; + + /** Parameter spacing interval between the bias value (s). */ + private int parameterSpacing; + + /** Simple constructor. */ + public DcbDescription() { + this.determinationMethod = ""; + this.observationSampling = -1; + this.parameterSpacing = -1; + } + + /** + * Get the determination mode used to generate the bias results. + *

          + * This value is optional. If the value is not present in the file, the method returns an empty string. + *

          + * @return the determination mode used to generate the bias results. + */ + public final String getDeterminationMethod() { + return determinationMethod; + } + + /** + * Get the bias mode + *

          + * The bias mode describes how the included GNSS bias values have to be interpreted and applied. + *

          + * @return the bias mode + */ + public final String getBiasMode() { + return biasMode; + } + + /** + * Get the time system for DCB data. + * + * @return the time system + */ + public final TimeSystem getTimeSystem() { + return timeSystem; + } + + /** + * Get the observation sampling interval used for data analysis. + *

          + * This value is optional. If the value is not present in the file, the method returns -1. + *

          + * @return the observation sampling interval used for data analysis in seconds + */ + public final int getObservationSampling() { + return observationSampling; + } + + /** + * Get the parameter spacing interval between the bias value. + *

          + * This value is optional. If the value is not present in the file, the method returns -1. + *

          + * @return the pParameter spacing interval between the bias value in seconds + */ + public final int getParameterSpacing() { + return parameterSpacing; + } + + /** + * Set the determination mode used to generate the bias results. + * + * @param determinationMethod the determination method to set + */ + public void setDeterminationMethod(final String determinationMethod) { + this.determinationMethod = determinationMethod; + } + + /** + * Set the bias mode. + * + * @param biasMode the bias mode to set + */ + public void setBiasMode(final String biasMode) { + this.biasMode = biasMode; + } + + /** + * Set the time system used for DCB data. + * + * @param timeSystem the time system to set + */ + public void setTimeSystem(final TimeSystem timeSystem) { + this.timeSystem = timeSystem; + } + + /** + * Set the observation sampling interval used for data analysis. + * + * @param observationSampling the observation sampling to set in seconds + */ + public void setObservationSampling(final int observationSampling) { + this.observationSampling = observationSampling; + } + + /** + * Set the parameter spacing interval between the bias value. + * + * @param parameterSpacing the parameter spacing to set in seconds + */ + public void setParameterSpacing(final int parameterSpacing) { + this.parameterSpacing = parameterSpacing; + } + +} diff --git a/src/main/java/org/orekit/files/sinex/DcbSatellite.java b/src/main/java/org/orekit/files/sinex/DcbSatellite.java new file mode 100644 index 0000000000..89766b59fb --- /dev/null +++ b/src/main/java/org/orekit/files/sinex/DcbSatellite.java @@ -0,0 +1,111 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.sinex; + +import org.orekit.gnss.SatelliteSystem; + +/** + * Class based on DCB, used to store the data parsed in {@link SinexLoader} + * for Differential Code Biases computed for satellites. + *

          + * Satellites and stations have differentiated classes as stations might have multiple satellite systems. + * The data are stored in a single DCB object. + *

          + * @author Louis Aucouturier + * @since 12.0 + */ +public class DcbSatellite { + + /** Satellite PRN identifier. + *

          + * Satellite PRN and station id are present in order to allow stations to be associated with + * a satellite system stored in the PRN, as done in the Sinex file. + *

          + */ + private String prn; + + /** DCB description container. */ + private DcbDescription description; + + /** DCB solution data. */ + private Dcb dcb; + + /** + * Constructor for the DCBSatellite class. + * + * @param prn satellite PRN identifier + */ + public DcbSatellite(final String prn) { + this.prn = prn; + this.description = null; + this.dcb = new Dcb(); + } + + /** + * Get the data contained in "DCB/DESCRIPTION" block of the Sinex file. + *

          + * This block gives important parameters from the analysis and defines + * the fields in the block ’BIAS/SOLUTION’ + *

          + * @return the "DCB/DESCRIPTION" parameters. + */ + public DcbDescription getDescription() { + return description; + } + + /** + * Set the data contained in "DCB/DESCRIPTION" block of the Sinex file. + * + * @param description the "DCB/DESCRIPTION" parameters to set + */ + public void setDescription(final DcbDescription description) { + this.description = description; + } + + /** + * Get the DCB data for the current satellite. + * + * @return the DCB data for the current satellite + */ + public Dcb getDcbData() { + return dcb; + } + + /** + * Return the satellite PRN, as a String. + *

          + * Example of satellite PRN: "G01" + *

          + * @return the satellite PRN + */ + public String getPRN() { + return prn; + } + + /** + * Get the satellite sytem corresponding to the satellite. + *

          + * Satellite system is extracted from the first letter of the PRN. + *

          + * @return the satellite from which the DCB are extracted. + */ + public SatelliteSystem getSatelliteSytem() { + return SatelliteSystem.parseSatelliteSystem(prn); + } + +} diff --git a/src/main/java/org/orekit/files/sinex/DcbStation.java b/src/main/java/org/orekit/files/sinex/DcbStation.java new file mode 100644 index 0000000000..ebd31dc3c7 --- /dev/null +++ b/src/main/java/org/orekit/files/sinex/DcbStation.java @@ -0,0 +1,117 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.files.sinex; + +import java.util.HashMap; + +import org.orekit.gnss.SatelliteSystem; + +/** + * Class based on DCB, used to store the data parsed in {@link SinexLoader} + * for Differential Code Biases computed for stations. + *

          + * Satellites and stations have differentiated classes as stations might have multiple satellite systems. + * The data are stored in a Map of DCB, identified by the {@link SatelliteSystem} + *

          + * @author Louis Aucouturier + * @since 12.0 + */ +public class DcbStation { + + /** Site code. */ + private String siteCode; + + /** DCB description container. */ + private DcbDescription description; + + /** Map containing DCB objects as a function of the satellite system. */ + private HashMap dcbMap; + + /** + * Simple constructor. + * @param siteCode the site code (station identifier) + */ + public DcbStation(final String siteCode) { + this.siteCode = siteCode; + this.description = null; + this.dcbMap = new HashMap(); + } + + /** + * Get the site code (station identifier). + * + * @return the site code + */ + public String getSiteCode() { + return siteCode; + } + + /** + * Get the data contained in "DCB/DESCRIPTION" block of the Sinex file. + *

          + * This block gives important parameters from the analysis and defines + * the fields in the block ’BIAS/SOLUTION’ + *

          + * @return the "DCB/DESCRIPTION" parameters. + */ + public DcbDescription getDescription() { + return description; + } + + /** + * Set the data contained in "DCB/DESCRIPTION" block of the Sinex file. + * + * @param description the "DCB/DESCRIPTION" parameters to set + */ + public void setDescription(final DcbDescription description) { + this.description = description; + } + + /** + * Get the DCB data for a given satellite system. + * + * @param satelliteSystem satellite system + * @return the DCB data corresponding to the satellite system + * (can be null is no DCB available) + */ + public Dcb getDcbData(final SatelliteSystem satelliteSystem) { + return dcbMap.get(satelliteSystem); + } + + /** + * Add the DCB data corresponding to a satellite system. + *

          + * If the instance previously contained DCB data for the satellite system, the old value is replaced. + *

          + * @param satelliteSystem satellite system for which the DCB is added + * @param dcb DCB data + */ + public void addDcb(final SatelliteSystem satelliteSystem, final Dcb dcb) { + dcbMap.put(satelliteSystem, dcb); + } + + /** + * Get the satellite systems available for the station. + * + * @return a Set containing all SatelliteSystems available for DCB computation. + */ + public Iterable getAvailableSatelliteSystems() { + return dcbMap.keySet(); + } + +} diff --git a/src/main/java/org/orekit/files/sinex/SinexEopEntry.java b/src/main/java/org/orekit/files/sinex/SinexEopEntry.java index 0be30b8030..ce01108805 100644 --- a/src/main/java/org/orekit/files/sinex/SinexEopEntry.java +++ b/src/main/java/org/orekit/files/sinex/SinexEopEntry.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,10 +31,10 @@ */ public class SinexEopEntry implements TimeStamped { - /** Length of day. */ + /** Length of day (seconds). */ private double lod; - /** UT1-UTC. */ + /** UT1-UTC (seconds). */ private double ut1MinusUtc; /** X polar motion (radians). */ @@ -74,7 +74,7 @@ public AbsoluteDate getDate() { /** * Get the length of day. - * @return the length of day + * @return the length of day in seconds */ public double getLod() { return lod; @@ -82,7 +82,7 @@ public double getLod() { /** * Set the length of day. - * @param lod the length of day to set + * @param lod the length of day to set in seconds */ public void setLod(final double lod) { this.lod = lod; @@ -90,7 +90,7 @@ public void setLod(final double lod) { /** * Get the UT1-UTC offset. - * @return the UT1-UTC offset + * @return the UT1-UTC offset in seconds */ public double getUt1MinusUtc() { return ut1MinusUtc; @@ -98,7 +98,7 @@ public double getUt1MinusUtc() { /** * Set the UT1-UTC offset. - * @param ut1MinusUtc the value to set + * @param ut1MinusUtc the value to set in seconds */ public void setUt1MinusUtc(final double ut1MinusUtc) { this.ut1MinusUtc = ut1MinusUtc; @@ -138,7 +138,7 @@ public void setyPo(final double yPo) { /** * Get the nutation correction in longitude. - * @return the nutation correction in longitude + * @return the nutation correction in longitude in radians */ public double getNutLn() { return nutLn; @@ -146,7 +146,7 @@ public double getNutLn() { /** * Set the nutation correction in longitude. - * @param nutLn the nutation correction in longitude to set + * @param nutLn the nutation correction in longitude to set in radians */ public void setNutLn(final double nutLn) { this.nutLn = nutLn; @@ -219,7 +219,7 @@ public EOPEntry toEopEntry(final IERSConventions.NutationCorrectionConverter con // Create a new EOPEntry object storing the extracted data, then add it to the list of EOPEntries. return new EOPEntry(mjd, ut1MinusUtc, lod, - xPo, yPo, + xPo, yPo, Double.NaN, Double.NaN, equinox[0], equinox[1], nro[0], nro[1], version, epoch); diff --git a/src/main/java/org/orekit/files/sinex/SinexLoader.java b/src/main/java/org/orekit/files/sinex/SinexLoader.java index e4ae465893..df06ea69f5 100644 --- a/src/main/java/org/orekit/files/sinex/SinexLoader.java +++ b/src/main/java/org/orekit/files/sinex/SinexLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,6 +31,7 @@ import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.hipparchus.exception.DummyLocalizable; @@ -45,12 +46,15 @@ import org.orekit.errors.OrekitMessages; import org.orekit.files.sinex.Station.ReferenceSystem; import org.orekit.frames.EOPEntry; -import org.orekit.frames.EOPHistoryLoader; +import org.orekit.frames.EopHistoryLoader; import org.orekit.frames.ITRFVersion; +import org.orekit.gnss.SatelliteSystem; +import org.orekit.gnss.TimeSystem; import org.orekit.time.AbsoluteDate; import org.orekit.time.ChronologicalComparator; import org.orekit.time.DateComponents; import org.orekit.time.TimeScale; +import org.orekit.time.TimeScales; import org.orekit.time.TimeStamped; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; @@ -60,8 +64,9 @@ /** * Loader for Solution INdependent EXchange (SINEX) files. *

          - * For now only few keys are supported: SITE/ID, SITE/ECCENTRICITY, SOLUTION/EPOCHS and SOLUTION/ESTIMATE. - * They represent the minimum set of parameters that are interesting to consider in a SINEX file. + * The loader can be used to load several data types contained in Sinex files. + * The current supported data are: station coordinates, site eccentricities, EOP, and Difference Code Bias (DCB). + * Several instances of Sinex loader must be created in order to parse different data types. *

          *

          * The parsing of EOP parameters for multiple files in different SinexLoader object, fed into the default DataContext @@ -70,11 +75,10 @@ * overlap will lead to inconsistencies in the final EOPHistory object. Multiple files can be parsed using a single SinexLoader * with a regex to overcome this issue. *

          - * * @author Bryan Cazabonne * @since 10.3 */ -public class SinexLoader implements EOPHistoryLoader { +public class SinexLoader implements EopHistoryLoader { /** Length of day. */ private static final String LOD = "LOD"; @@ -101,7 +105,10 @@ public class SinexLoader implements EOPHistoryLoader { private static final String NUT_Y = "NUT_Y"; /** 00:000:00000 epoch. */ - private static final String DEFAULT_EPOCH = "00:000:00000"; + private static final String DEFAULT_EPOCH_TWO_DIGITS = "00:000:00000"; + + /** 0000:000:00000 epoch. */ + private static final String DEFAULT_EPOCH_FOUR_DIGITS = "0000:000:00000"; /** Pattern for delimiting regular expressions. */ private static final Pattern SEPARATOR = Pattern.compile(":"); @@ -110,7 +117,10 @@ public class SinexLoader implements EOPHistoryLoader { private static final Pattern PATTERN_SPACE = Pattern.compile("\\s+"); /** Pattern to check beginning of SINEX files.*/ - private static final Pattern PATTERN_BEGIN = Pattern.compile("(%=).*"); + private static final Pattern PATTERN_BEGIN = Pattern.compile("%=(?:SNX|BIA) \\d\\.\\d\\d ..." + + " (\\d{2,4}:\\d{3}:\\d{5}) ..." + + " (\\d{2,4}:\\d{3}:\\d{5}) (\\d{2,4}:\\d{3}:\\d{5})" + + " . .*"); /** List of all EOP parameter types. */ private static final List EOP_TYPES = Arrays.asList(LOD, UT, XPO, YPO, NUT_LN, NUT_OB, NUT_X, NUT_Y); @@ -121,30 +131,48 @@ public class SinexLoader implements EOPHistoryLoader { /** End time of the data used in the Sinex solution.*/ private AbsoluteDate endDate; + /** SINEX file creation date as extracted for the first line. */ + private AbsoluteDate creationDate; + /** Station data. * Key: Site code */ private final Map stations; + /** + * DCB data. + * Key: Site code + */ + private final Map dcbStations; + + /** + * DCB data. + * Key: Satellite PRN + */ + private final Map dcbSatellites; + + /** DCB description. */ + private final DcbDescription dcbDescription; + /** Data set. */ - private Map map; + private Map eop; /** ITRF Version used for EOP parsing. */ private ITRFVersion itrfVersionEop; - /** UTC time scale. */ - private final TimeScale utc; + /** Time scales. */ + private final TimeScales scales; /** Simple constructor. This constructor uses the {@link DataContext#getDefault() * default data context}. * @param supportedNames regular expression for supported files names - * @see #SinexLoader(String, DataProvidersManager, TimeScale) + * @see #SinexLoader(String, DataProvidersManager, TimeScales) */ @DefaultDataContext public SinexLoader(final String supportedNames) { this(supportedNames, DataContext.getDefault().getDataProvidersManager(), - DataContext.getDefault().getTimeScales().getUTC()); + DataContext.getDefault().getTimeScales()); } /** @@ -156,15 +184,25 @@ public SinexLoader(final String supportedNames) { *

          * @param supportedNames regular expression for supported files names * @param dataProvidersManager provides access to auxiliary data. - * @param utc UTC time scale + * @param scales time scales */ public SinexLoader(final String supportedNames, final DataProvidersManager dataProvidersManager, - final TimeScale utc) { - this.utc = utc; - this.stations = new HashMap<>(); + final TimeScales scales) { + // Common data + this.scales = scales; + this.creationDate = AbsoluteDate.FUTURE_INFINITY; + // DCB parameters + this.dcbDescription = new DcbDescription(); + this.dcbStations = new HashMap<>(); + this.dcbSatellites = new HashMap<>(); + // EOP parameters + this.eop = new HashMap<>(); this.itrfVersionEop = ITRFVersion.ITRF_2014; - this.map = new HashMap<>(); + // Station data + this.stations = new HashMap<>(); + + // Read the file dataProvidersManager.feed(supportedNames, new Parser()); } @@ -177,11 +215,11 @@ public SinexLoader(final String supportedNames, * method. *

          * @param source source for the RINEX data - * @see #SinexLoader(String, DataProvidersManager, TimeScale) + * @see #SinexLoader(String, DataProvidersManager, TimeScales) */ @DefaultDataContext public SinexLoader(final DataSource source) { - this(source, DataContext.getDefault().getTimeScales().getUTC()); + this(source, DataContext.getDefault().getTimeScales()); } /** @@ -192,14 +230,24 @@ public SinexLoader(final DataSource source) { * method. *

          * @param source source for the RINEX data - * @param utc UTC time scale + * @param scales time scales */ - public SinexLoader(final DataSource source, final TimeScale utc) { + public SinexLoader(final DataSource source, final TimeScales scales) { try { - this.utc = utc; - this.stations = new HashMap<>(); + // Common data + this.scales = scales; + this.creationDate = AbsoluteDate.FUTURE_INFINITY; + // EOP data this.itrfVersionEop = ITRFVersion.ITRF_2014; - this.map = new HashMap<>(); + this.eop = new HashMap<>(); + // DCB data + this.dcbStations = new HashMap<>(); + this.dcbSatellites = new HashMap<>(); + this.dcbDescription = new DcbDescription(); + // Station data + this.stations = new HashMap<>(); + + // Read the file try (InputStream is = source.getOpener().openStreamOnce(); BufferedInputStream bis = new BufferedInputStream(is)) { new Parser().loadData(bis, source.getName()); @@ -227,6 +275,33 @@ public ITRFVersion getITRFVersion() { return itrfVersionEop; } + /** + * Get the creation date of the parsed SINEX file. + * @return SINEX file creation date as an AbsoluteDate + * @since 12.0 + */ + public AbsoluteDate getCreationDate() { + return creationDate; + } + + /** + * Get the file epoch start time. + * @return the file epoch start time + * @since 12.0 + */ + public AbsoluteDate getFileEpochStartTime() { + return startDate; + } + + /** + * Get the file epoch end time. + * @return the file epoch end time + * @since 12.0 + */ + public AbsoluteDate getFileEpochEndTime() { + return endDate; + } + /** * Get the parsed station data. * @return unmodifiable view of parsed station data @@ -241,11 +316,12 @@ public Map getStations() { * @since 11.2 */ public Map getParsedEop() { - return Collections.unmodifiableMap(map); + return Collections.unmodifiableMap(eop); } /** * Get the station corresponding to the given site code. + * * @param siteCode site code * @return the corresponding station */ @@ -258,7 +334,28 @@ public Station getStation(final String siteCode) { public void fillHistory(final NutationCorrectionConverter converter, final SortedSet history) { // Fill the history set with the content of the parsed data - history.addAll(getEopList(converter)); + // According to Sinex standard, data are given in UTC + history.addAll(getEopList(converter, scales.getUTC())); + } + + /** + * Get the DCB data for a given station. + * @param siteCode site code + * @return DCB data for the station + * @since 12.0 + */ + public DcbStation getDcbStation(final String siteCode) { + return dcbStations.get(siteCode); + } + + /** + * Get the DCB data for a given satellite identified by its PRN. + * @param prn the satellite PRN (e.g. "G01" for GPS 01) + * @return the DCB data for the satellite + * @since 12.0 + */ + public DcbSatellite getDcbSatellite(final String prn) { + return dcbSatellites.get(prn); } /** Parser for SINEX files. */ @@ -280,15 +377,24 @@ public void loadData(final InputStream input, final String name) throws IOException, ParseException { // Useful parameters - int lineNumber = 0; - String line = null; - boolean inId = false; - boolean inEcc = false; - boolean inEpoch = false; - boolean inEstimate = false; - boolean firstEcc = true; - Vector3D position = Vector3D.ZERO; - Vector3D velocity = Vector3D.ZERO; + int lineNumber = 0; + String line = null; + boolean inDcbDesc = false; + boolean inDcbSol = false; + boolean inId = false; + boolean inAntenna = false; + boolean inEcc = false; + boolean inEpoch = false; + boolean inEstimate = false; + Vector3D position = Vector3D.ZERO; + Vector3D velocity = Vector3D.ZERO; + String startDateString = ""; + String endDateString = ""; + String creationDateString = ""; + + // According to Sinex standard, the epochs are given in UTC scale. + // Except for DCB files for which a TIME_SYSTEM key is present. + TimeScale scale = scales.getUTC(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { @@ -299,210 +405,331 @@ public void loadData(final InputStream input, final String name) // They represent the minimum set of parameters that are interesting to consider in a SINEX file // Other keys can be added depending user needs - // The first line is parsed in order to get the creation date of the file, which might be used - // in the case of an absent date as the final date of the data. - // Its position is fixed in the file, at the first line, in the 4th column. - if (lineNumber == 1 && PATTERN_BEGIN.matcher(line).matches()) { - final String[] splitFirstLine = PATTERN_SPACE.split(line); - final AbsoluteDate fileStartDate = stringEpochToAbsoluteDate(splitFirstLine[5]); - final AbsoluteDate fileEndDate = stringEpochToAbsoluteDate(splitFirstLine[6]); - if (startDate == null) { - // First data loading, needs to initialize the start and end dates for EOP history - startDate = fileStartDate; - endDate = fileEndDate; + // The first line is parsed in order to get the creation, start and end dates of the file + if (lineNumber == 1) { + final Matcher matcher = PATTERN_BEGIN.matcher(line); + if (matcher.matches()) { + + creationDateString = matcher.group(1); + startDateString = matcher.group(2); + endDateString = matcher.group(3); + creationDate = stringEpochToAbsoluteDate(creationDateString, false, scale); + + if (startDate == null) { + // First data loading, needs to initialize the start and end dates for EOP history + startDate = stringEpochToAbsoluteDate(startDateString, true, scale); + endDate = stringEpochToAbsoluteDate(endDateString, false, scale); + } + } else { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); } - // In case of multiple files used for EOP history, the start and end dates can be updated - startDate = fileStartDate.isBefore(startDate) ? fileStartDate : startDate; - endDate = fileEndDate.isAfter(endDate) ? fileEndDate : endDate; - } + } else { + switch (line.trim()) { + case "+SITE/ID" : + // Start of site id. data + inId = true; + break; + case "-SITE/ID" : + // End of site id. data + inId = false; + break; + case "+SITE/ANTENNA" : + // Start of site antenna data + inAntenna = true; + break; + case "-SITE/ANTENNA" : + // End of site antenna data + inAntenna = false; + break; + case "+SITE/ECCENTRICITY" : + // Start of antenna eccentricities data + inEcc = true; + break; + case "-SITE/ECCENTRICITY" : + // End of antenna eccentricities data + inEcc = false; + break; + case "+SOLUTION/EPOCHS" : + // Start of epoch data + inEpoch = true; + break; + case "-SOLUTION/EPOCHS" : + // End of epoch data + inEpoch = false; + break; + case "+SOLUTION/ESTIMATE" : + // Start of coordinates data + inEstimate = true; + break; + case "-SOLUTION/ESTIMATE" : + // End of coordinates data + inEstimate = false; + break; + case "+BIAS/DESCRIPTION" : + // Start of Bias description block data + inDcbDesc = true; + break; + case "-BIAS/DESCRIPTION" : + // End of Bias description block data + inDcbDesc = false; + break; + case "+BIAS/SOLUTION" : + // Start of Bias solution block data + inDcbSol = true; + break; + case "-BIAS/SOLUTION" : + // End of Bias solution block data + inDcbSol = false; + break; + default: + if (line.startsWith(COMMENT)) { + // ignore that line + } else { + // parsing data + if (inId) { + // read site id. data + final Station station = new Station(); + station.setSiteCode(parseString(line, 1, 4)); + station.setDomes(parseString(line, 9, 9)); + // add the station to the map + addStation(station); + } else if (inAntenna) { + + // read antenna type data + final Station station = getStation(parseString(line, 1, 4)); + + final AbsoluteDate start = stringEpochToAbsoluteDate(parseString(line, 16, 12), true, scale); + final AbsoluteDate end = stringEpochToAbsoluteDate(parseString(line, 29, 12), false, scale); + + // antenna type + final String type = parseString(line, 42, 20); + + // special implementation for the first entry + if (station.getAntennaTypeTimeSpanMap().getSpansNumber() == 1) { + // we want null values outside validity limits of the station + station.addAntennaTypeValidBefore(type, end); + station.addAntennaTypeValidBefore(null, start); + } else { + station.addAntennaTypeValidBefore(type, end); + } - switch (line.trim()) { - case "+SITE/ID" : - // Start of site id. data - inId = true; - break; - case "-SITE/ID" : - // End of site id. data - inId = false; - break; - case "+SITE/ECCENTRICITY" : - // Start of antenna eccentricities data - inEcc = true; - break; - case "-SITE/ECCENTRICITY" : - // End of antenna eccentricities data - inEcc = false; - break; - case "+SOLUTION/EPOCHS" : - // Start of epoch data - inEpoch = true; - break; - case "-SOLUTION/EPOCHS" : - // End of epoch data - inEpoch = false; - break; - case "+SOLUTION/ESTIMATE" : - // Start of coordinates data - inEstimate = true; - break; - case "-SOLUTION/ESTIMATE" : - // Start of coordinates data - inEstimate = false; - break; - default: - if (line.startsWith(COMMENT)) { - // ignore that line - } else { - // parsing data - if (inId) { - // read site id. data - final Station station = new Station(); - station.setSiteCode(parseString(line, 1, 4)); - station.setDomes(parseString(line, 9, 9)); - // add the station to the map - addStation(station); - } else if (inEcc) { - - // read antenna eccentricities data - final Station station = getStation(parseString(line, 1, 4)); - - // check if it is the first eccentricity entry for this station - if (station.getEccentricitiesTimeSpanMap().getSpansNumber() == 1) { - // we are parsing eccentricity data for a new station - firstEcc = true; - } + } else if (inEcc) { - // start and end of validity for the current entry - final AbsoluteDate start = stringEpochToAbsoluteDate(parseString(line, 16, 12)); - final AbsoluteDate end = stringEpochToAbsoluteDate(parseString(line, 29, 12)); - - // reference system UNE or XYZ - station.setEccRefSystem(ReferenceSystem.getEccRefSystem(parseString(line, 42, 3))); - - // eccentricity vector - final Vector3D eccStation = new Vector3D(parseDouble(line, 46, 8), - parseDouble(line, 55, 8), - parseDouble(line, 64, 8)); - - // special implementation for the first entry - if (firstEcc) { - // we want null values outside validity limits of the station - station.addStationEccentricitiesValidBefore(eccStation, end); - station.addStationEccentricitiesValidBefore(null, start); - // we parsed the first entry, set the flag to false - firstEcc = false; - } else { - station.addStationEccentricitiesValidBefore(eccStation, end); - } + // read antenna eccentricities data + final Station station = getStation(parseString(line, 1, 4)); + + final AbsoluteDate start = stringEpochToAbsoluteDate(parseString(line, 16, 12), true, scale); + final AbsoluteDate end = stringEpochToAbsoluteDate(parseString(line, 29, 12), false, scale); + + // reference system UNE or XYZ + station.setEccRefSystem(ReferenceSystem.getEccRefSystem(parseString(line, 42, 3))); + + // eccentricity vector + final Vector3D eccStation = new Vector3D(parseDouble(line, 46, 8), + parseDouble(line, 55, 8), + parseDouble(line, 64, 8)); + + // special implementation for the first entry + if (station.getEccentricitiesTimeSpanMap().getSpansNumber() == 1) { + // we want null values outside validity limits of the station + station.addStationEccentricitiesValidBefore(eccStation, end); + station.addStationEccentricitiesValidBefore(null, start); + } else { + station.addStationEccentricitiesValidBefore(eccStation, end); + } - // update the last known eccentricities entry - station.setEccentricities(eccStation); - - } else if (inEpoch) { - // read epoch data - final Station station = getStation(parseString(line, 1, 4)); - station.setValidFrom(stringEpochToAbsoluteDate(parseString(line, 16, 12))); - station.setValidUntil(stringEpochToAbsoluteDate(parseString(line, 29, 12))); - } else if (inEstimate) { - final Station station = getStation(parseString(line, 14, 4)); - final AbsoluteDate currentDate = stringEpochToAbsoluteDate(parseString(line, 27, 12)); - final String dataType = parseString(line, 7, 6); - // check if this station exists or if we are parsing EOP - if (station != null || EOP_TYPES.contains(dataType)) { - // switch on coordinates data + } else if (inEpoch) { + // read epoch data + final Station station = getStation(parseString(line, 1, 4)); + station.setValidFrom(stringEpochToAbsoluteDate(parseString(line, 16, 12), true, scale)); + station.setValidUntil(stringEpochToAbsoluteDate(parseString(line, 29, 12), false, scale)); + } else if (inEstimate) { + final Station station = getStation(parseString(line, 14, 4)); + final AbsoluteDate currentDate = stringEpochToAbsoluteDate(parseString(line, 27, 12), false, scale); + final String dataType = parseString(line, 7, 6); + // check if this station exists or if we are parsing EOP + if (station != null || EOP_TYPES.contains(dataType)) { + // switch on coordinates data + switch (dataType) { + case "STAX": + // station X coordinate + final double x = parseDouble(line, 47, 22); + position = new Vector3D(x, position.getY(), position.getZ()); + station.setPosition(position); + break; + case "STAY": + // station Y coordinate + final double y = parseDouble(line, 47, 22); + position = new Vector3D(position.getX(), y, position.getZ()); + station.setPosition(position); + break; + case "STAZ": + // station Z coordinate + final double z = parseDouble(line, 47, 22); + position = new Vector3D(position.getX(), position.getY(), z); + station.setPosition(position); + // set the reference epoch (identical for all coordinates) + station.setEpoch(currentDate); + // reset position vector + position = Vector3D.ZERO; + break; + case "VELX": + // station X velocity (value is in m/y) + final double vx = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; + velocity = new Vector3D(vx, velocity.getY(), velocity.getZ()); + station.setVelocity(velocity); + break; + case "VELY": + // station Y velocity (value is in m/y) + final double vy = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; + velocity = new Vector3D(velocity.getX(), vy, velocity.getZ()); + station.setVelocity(velocity); + break; + case "VELZ": + // station Z velocity (value is in m/y) + final double vz = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; + velocity = new Vector3D(velocity.getX(), velocity.getY(), vz); + station.setVelocity(velocity); + // reset position vector + velocity = Vector3D.ZERO; + break; + case XPO: + // X polar motion + final double xPo = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setxPo(xPo); + break; + case YPO: + // Y polar motion + final double yPo = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setyPo(yPo); + break; + case LOD: + // length of day + final double lod = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setLod(lod); + break; + case UT: + // delta time UT1-UTC + final double dt = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setUt1MinusUtc(dt); + break; + case NUT_LN: + // nutation correction in longitude + final double nutLn = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setNutLn(nutLn); + break; + case NUT_OB: + // nutation correction in obliquity + final double nutOb = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setNutOb(nutOb); + break; + case NUT_X: + // nutation correction X + final double nutX = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setNutX(nutX); + break; + case NUT_Y: + // nutation correction Y + final double nutY = parseDoubleWithUnit(line, 40, 4, 47, 21); + getSinexEopEntry(currentDate).setNutY(nutY); + break; + default: + // ignore that field + break; + } + } + } else if (inDcbDesc) { + // Determining the data type for the DCBDescription object + final String[] splitLine = PATTERN_SPACE.split(line.trim()); + final String dataType = splitLine[0]; + final String data = splitLine[1]; switch (dataType) { - case "STAX": - // station X coordinate - final double x = parseDouble(line, 47, 22); - position = new Vector3D(x, position.getY(), position.getZ()); - station.setPosition(position); - break; - case "STAY": - // station Y coordinate - final double y = parseDouble(line, 47, 22); - position = new Vector3D(position.getX(), y, position.getZ()); - station.setPosition(position); - break; - case "STAZ": - // station Z coordinate - final double z = parseDouble(line, 47, 22); - position = new Vector3D(position.getX(), position.getY(), z); - station.setPosition(position); - // set the reference epoch (identical for all coordinates) - station.setEpoch(currentDate); - // reset position vector - position = Vector3D.ZERO; - break; - case "VELX": - // station X velocity (value is in m/y) - final double vx = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; - velocity = new Vector3D(vx, velocity.getY(), velocity.getZ()); - station.setVelocity(velocity); - break; - case "VELY": - // station Y velocity (value is in m/y) - final double vy = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; - velocity = new Vector3D(velocity.getX(), vy, velocity.getZ()); - station.setVelocity(velocity); - break; - case "VELZ": - // station Z velocity (value is in m/y) - final double vz = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; - velocity = new Vector3D(velocity.getX(), velocity.getY(), vz); - station.setVelocity(velocity); - // reset position vector - velocity = Vector3D.ZERO; - break; - case XPO: - // X polar motion - final double xPo = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setxPo(xPo); - break; - case YPO: - // Y polar motion - final double yPo = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setyPo(yPo); - break; - case LOD: - // length of day - final double lod = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setLod(lod); + case "OBSERVATION_SAMPLING": + dcbDescription.setObservationSampling(Integer.parseInt(data)); break; - case UT: - // delta time UT1-UTC - final double dt = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setUt1MinusUtc(dt); + case "PARAMETER_SPACING": + dcbDescription.setParameterSpacing(Integer.parseInt(data)); break; - case NUT_LN: - // nutation correction in longitude - final double nutLn = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setNutLn(nutLn); + case "DETERMINATION_METHOD": + dcbDescription.setDeterminationMethod(data); break; - case NUT_OB: - // nutation correction in obliquity - final double nutOb = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setNutOb(nutOb); + case "BIAS_MODE": + dcbDescription.setBiasMode(data); break; - case NUT_X: - // nutation correction X - final double nutX = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setNutX(nutX); - break; - case NUT_Y: - // nutation correction Y - final double nutY = parseDoubleWithUnit(line, 40, 4, 47, 21); - getSinexEopEntry(currentDate).setNutY(nutY); + case "TIME_SYSTEM": + if ("UTC".equals(data)) { + dcbDescription.setTimeSystem(TimeSystem.UTC); + } else if ("TAI".equals(data)) { + dcbDescription.setTimeSystem(TimeSystem.TAI); + } else { + dcbDescription.setTimeSystem(TimeSystem.parseOneLetterCode(data)); + } + scale = dcbDescription.getTimeSystem().getTimeScale(scales); + // A time scale has been parsed, update start, end, and creation dates + // to take into account the time scale + startDate = stringEpochToAbsoluteDate(startDateString, true, scale); + endDate = stringEpochToAbsoluteDate(endDateString, false, scale); + creationDate = stringEpochToAbsoluteDate(creationDateString, false, scale); break; default: - // ignore that field break; } + } else if (inDcbSol) { + + // Parsing the data present in a DCB file solution line. + // Most fields are used in the files provided by CDDIS. + // Station is empty for satellite measurements. + // The separator between columns is composed of spaces. + + final String satellitePrn = parseString(line, 11, 3); + final String siteCode = parseString(line, 15, 9); + + // Parsing the line data. + final String obs1 = parseString(line, 25, 4); + final String obs2 = parseString(line, 30, 4); + final AbsoluteDate beginDate = stringEpochToAbsoluteDate(parseString(line, 35, 14), true, scale); + final AbsoluteDate finalDate = stringEpochToAbsoluteDate(parseString(line, 50, 14), false, scale); + final Unit unitDcb = Unit.parse(parseString(line, 65, 4)); + final double valueDcb = unitDcb.toSI(Double.parseDouble(parseString(line, 70, 21))); + + // Verifying if present + if (siteCode.equals("")) { + final String id = satellitePrn; + DcbSatellite dcbSatellite = getDcbSatellite(id); + if (dcbSatellite == null) { + dcbSatellite = new DcbSatellite(id); + dcbSatellite.setDescription(dcbDescription); + } + final Dcb dcb = dcbSatellite.getDcbData(); + // Add the data to the DCB object. + dcb.addDcbLine(obs1, obs2, beginDate, finalDate, valueDcb); + // Adding the object to the HashMap if not present. + addDcbSatellite(dcbSatellite, id); + } else { + final String id = siteCode; + DcbStation dcbStation = getDcbStation(id); + if (dcbStation == null) { + dcbStation = new DcbStation(id); + dcbStation.setDescription(dcbDescription); + } + final SatelliteSystem satSystem = SatelliteSystem.parseSatelliteSystem(satellitePrn); + // Add the data to the DCB object. + final Dcb dcb = dcbStation.getDcbData(satSystem); + if (dcb == null) { + dcbStation.addDcb(satSystem, new Dcb()); + } + dcbStation.getDcbData(satSystem).addDcbLine(obs1, obs2, beginDate, finalDate, valueDcb); + // Adding the object to the HashMap if not present. + addDcbStation(dcbStation, id); + } + + } else { + // not supported line, ignore it } - } else { - // not supported line, ignore it } - } - break; + break; + } } } @@ -552,36 +779,41 @@ private double parseDoubleWithUnit(final String line, final int startUnit, final /** * Transform a String epoch to an AbsoluteDate. * @param stringDate string epoch + * @param isStart true if epoch is a start validity epoch + * @param scale TimeScale for the computation of the dates * @return the corresponding AbsoluteDate */ - private AbsoluteDate stringEpochToAbsoluteDate(final String stringDate) { + private AbsoluteDate stringEpochToAbsoluteDate(final String stringDate, final boolean isStart, final TimeScale scale) { // Deal with 00:000:00000 epochs - if (DEFAULT_EPOCH.equals(stringDate)) { - // Data is still available, return a dummy date at infinity in the future direction - return AbsoluteDate.FUTURE_INFINITY; + if (DEFAULT_EPOCH_TWO_DIGITS.equals(stringDate) || DEFAULT_EPOCH_FOUR_DIGITS.equals(stringDate)) { + // If its a start validity epoch, the file start date shall be used. + // For end validity epoch, future infinity is acceptable. + return isStart ? startDate : AbsoluteDate.FUTURE_INFINITY; } // Date components final String[] fields = SEPARATOR.split(stringDate); // Read fields - final int twoDigitsYear = Integer.parseInt(fields[0]); - final int day = Integer.parseInt(fields[1]); - final int secInDay = Integer.parseInt(fields[2]); + final int digitsYear = Integer.parseInt(fields[0]); + final int day = Integer.parseInt(fields[1]); + final int secInDay = Integer.parseInt(fields[2]); // Data year final int year; - if (twoDigitsYear > 50) { - year = 1900 + twoDigitsYear; + if (digitsYear > 50 && digitsYear < 100) { + year = 1900 + digitsYear; + } else if (digitsYear < 100) { + year = 2000 + digitsYear; } else { - year = 2000 + twoDigitsYear; + year = digitsYear; } // Return an absolute date. // Initialize to 1st January of the given year because // sometimes day in equal to 0 in the file. - return new AbsoluteDate(new DateComponents(year, 1, 1), utc). + return new AbsoluteDate(new DateComponents(year, 1, 1), scale). shiftedBy(Constants.JULIAN_DAY * (day - 1)). shiftedBy(secInDay); @@ -592,20 +824,45 @@ private AbsoluteDate stringEpochToAbsoluteDate(final String stringDate) { * @param station station entry to add */ private void addStation(final Station station) { - // Check if station already exists + // Check if the station already exists if (stations.get(station.getSiteCode()) == null) { stations.put(station.getSiteCode(), station); } } + /** + * Add a new entry to the map of stations DCB. + * @param dcb DCB entry + * @param siteCode site code + * @since 12.0 + */ + private void addDcbStation(final DcbStation dcb, final String siteCode) { + // Check if the DCB for the current station already exists + if (dcbStations.get(siteCode) == null) { + dcbStations.put(siteCode, dcb); + } + } + + /** + * Add a new entry to the map of satellites DCB. + * @param dcb DCB entry + * @param prn satellite PRN (e.g. "G01" for GPS 01) + * @since 12.0 + */ + private void addDcbSatellite(final DcbSatellite dcb, final String prn) { + if (dcbSatellites.get(prn) == null) { + dcbSatellites.put(prn, dcb); + } + } + /** * Get the EOP entry for the given epoch. * @param date epoch * @return the EOP entry corresponding to the epoch */ private SinexEopEntry getSinexEopEntry(final AbsoluteDate date) { - map.putIfAbsent(date, new SinexEopEntry(date)); - return map.get(date); + eop.putIfAbsent(date, new SinexEopEntry(date)); + return eop.get(date); } /** @@ -616,31 +873,40 @@ private SinexEopEntry getSinexEopEntry(final AbsoluteDate date) { * read chronological data is duplicated at the end time of the data. *

          * @param converter converter to use for nutation corrections + * @param scale time scale of EOP entries * @return a list of EOP entries */ - private List getEopList(final IERSConventions.NutationCorrectionConverter converter) { + private List getEopList(final IERSConventions.NutationCorrectionConverter converter, + final TimeScale scale) { // Initialize the list - final List eop = new ArrayList<>(); + final List eopEntries = new ArrayList<>(); // Convert the map of parsed EOP data to a sorted set - final SortedSet set = mapToSortedSet(map); + final SortedSet set = mapToSortedSet(eop); // Loop on set for (final SinexEopEntry entry : set) { // Add to the list - eop.add(entry.toEopEntry(converter, itrfVersionEop, utc)); + eopEntries.add(entry.toEopEntry(converter, itrfVersionEop, scale)); } // Add first entry to the start time of the data - eop.add(copyEopEntry(startDate, set.first()).toEopEntry(converter, itrfVersionEop, utc)); + eopEntries.add(copyEopEntry(startDate, set.first()).toEopEntry(converter, itrfVersionEop, scale)); // Add the last entry to the end time of the data - eop.add(copyEopEntry(endDate, set.last()).toEopEntry(converter, itrfVersionEop, utc)); + eopEntries.add(copyEopEntry(endDate, set.last()).toEopEntry(converter, itrfVersionEop, scale)); + + if (set.size() < 2) { + // there is only one entry in the Sinex file + // in order for interpolation to work, we need to add more dummy entries + eopEntries.add(copyEopEntry(startDate.shiftedBy(+1.0), set.first()).toEopEntry(converter, itrfVersionEop, scale)); + eopEntries.add(copyEopEntry(endDate.shiftedBy(-1.0), set.last()).toEopEntry(converter, itrfVersionEop, scale)); + } // Return - eop.sort(new ChronologicalComparator()); - return eop; + eopEntries.sort(new ChronologicalComparator()); + return eopEntries; } diff --git a/src/main/java/org/orekit/files/sinex/Station.java b/src/main/java/org/orekit/files/sinex/Station.java index e428260b94..81e5993417 100644 --- a/src/main/java/org/orekit/files/sinex/Station.java +++ b/src/main/java/org/orekit/files/sinex/Station.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,8 +29,7 @@ * Station model. *

          * Since Orekit 11.1, this class handles multiple site antenna - * eccentricity. The {@link #getEccentricities()} method - * provides the last known eccentricity values. + * eccentricity. * The {@link #getEccentricities(AbsoluteDate)} method can be * used to access the site antenna eccentricity values for a * given epoch. @@ -55,12 +54,14 @@ public class Station { /** Eccentricity reference system. */ private ReferenceSystem eccRefSystem; - /** Latest site antenna eccentricities (m). */ - private Vector3D eccentricities; - /** TimeSpanMap of site antenna eccentricities. */ private TimeSpanMap eccentricitiesTimeSpanMap; + /** Antenna type. + * @since 12.0 + */ + private TimeSpanMap antennaTypesMap; + /** Station position. */ private Vector3D position; @@ -74,8 +75,8 @@ public class Station { * Constructor. */ public Station() { - this.eccentricities = Vector3D.ZERO; this.eccentricitiesTimeSpanMap = new TimeSpanMap<>(null); + this.antennaTypesMap = new TimeSpanMap<>(null); this.position = Vector3D.ZERO; this.velocity = Vector3D.ZERO; } @@ -117,9 +118,6 @@ public void setDomes(final String domes) { * @return start of validity */ public AbsoluteDate getValidFrom() { - if (validFrom == null) { - validFrom = eccentricitiesTimeSpanMap.getFirstTransition().getDate(); - } return validFrom; } @@ -136,9 +134,6 @@ public void setValidFrom(final AbsoluteDate validFrom) { * @return end of validity */ public AbsoluteDate getValidUntil() { - if (validUntil == null) { - validUntil = eccentricitiesTimeSpanMap.getLastTransition().getDate(); - } return validUntil; } @@ -166,26 +161,6 @@ public void setEccRefSystem(final ReferenceSystem eccRefSystem) { this.eccRefSystem = eccRefSystem; } - /** - * Get the last known station antenna eccentricities. - *

          - * Vector convention: X-Y-Z or UP-NORTH-EAST. - * See {@link #getEccRefSystem()} method. - *

          - * @return station antenna eccentricities (m) - */ - public Vector3D getEccentricities() { - return eccentricities; - } - - /** - * Set the last known station antenna eccentricities. - * @param eccentricities the eccenticities to set (m) - */ - public void setEccentricities(final Vector3D eccentricities) { - this.eccentricities = eccentricities; - } - /** * Get the station antenna eccentricities for the given epoch. *

          @@ -193,8 +168,7 @@ public void setEccentricities(final Vector3D eccentricities) { * See {@link #getEccRefSystem()} method. *

          * If there is no eccentricity values for the given epoch, an - * exception is thrown. It is possible to access the last known - * values using the {@link #getEccentricities()} method. + * exception is thrown. * @param date epoch * @return station antenna eccentricities (m) * @since 11.1 @@ -204,7 +178,7 @@ public Vector3D getEccentricities(final AbsoluteDate date) { // If the entry is null, there is no valid eccentricity values for the input epoch if (eccAtEpoch == null) { // Throw an exception - throw new OrekitException(OrekitMessages.NO_STATION_ECCENTRICITY_FOR_EPOCH, date, getValidFrom(), getValidUntil()); + throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date); } return eccAtEpoch; } @@ -242,6 +216,57 @@ public void addStationEccentricitiesValidAfter(final Vector3D entry, final Absol eccentricitiesTimeSpanMap.addValidAfter(entry, earliestValidityDate, false); } + /** + * Get the antenna type for the given epoch. + * If there is no antenna types for the given epoch, an + * exception is thrown. + * @param date epoch + * @return antenna type + * @since 12.0 + */ + public String getAntennaType(final AbsoluteDate date) { + final String typeAtEpoch = antennaTypesMap.get(date); + // If the entry is null, there is no valid type for the input epoch + if (typeAtEpoch == null) { + // Throw an exception + throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date); + } + return typeAtEpoch; + } + + /** + * Get the TimeSpanMap of site antenna type. + * @return the TimeSpanMap of site antenna type + * @since 12.0 + */ + public TimeSpanMap getAntennaTypeTimeSpanMap() { + return antennaTypesMap; + } + + /** Add a antenna type entry valid before a limit date.
          + * Using addAntennaTypeValidBefore(entry, t) will make entry + * valid in ]-∞, t[ (note the open bracket). + * @param entry antenna type entry + * @param latestValidityDate date before which the entry is valid + * (must be different from all dates already used for transitions) + * @since 12.0 + */ + public void addAntennaTypeValidBefore(final String entry, final AbsoluteDate latestValidityDate) { + antennaTypesMap.addValidBefore(entry, latestValidityDate, false); + } + + /** Add a antenna type entry valid after a limit date.
          + * Using addAntennaTypeValidAfter(entry, t) will make entry + * valid in [t, +∞[ (note the closed bracket). + * @param entry antenna type entry + * @param earliestValidityDate date after which the entry is valid + * (must be different from all dates already used for transitions) + * @since 12.0 + */ + public void addAntennaTypeValidAfter(final String entry, final AbsoluteDate earliestValidityDate) { + antennaTypesMap.addValidAfter(entry, earliestValidityDate, false); + } + /** * Get the station position. * @return the station position (m) diff --git a/src/main/java/org/orekit/files/sinex/package-info.java b/src/main/java/org/orekit/files/sinex/package-info.java index 98e101ba75..161fdf0c3d 100644 --- a/src/main/java/org/orekit/files/sinex/package-info.java +++ b/src/main/java/org/orekit/files/sinex/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/sp3/DataUsed.java b/src/main/java/org/orekit/files/sp3/DataUsed.java new file mode 100644 index 0000000000..3bc7dbacc1 --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/DataUsed.java @@ -0,0 +1,116 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.HashMap; +import java.util.Map; + +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; + +/** Enumerate for data used. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum DataUsed { + + /** Undifferenciated carrier phase. */ + UNDIFFERENTIATED_CARRIER_PHASE("u"), + + /** Change in undifferenciated carrier phase. */ + CHANGE_IN_UNDIFFERENTIATED_CARRIER_PHASE("du"), + + /** 2-receiver/1-satellite carrier phase. */ + TWO_RECEIVER_ONE_SATELLITE_CARRIER_PHASE("s"), + + /** Change in 2-receiver/1-satellite carrier phase. */ + CHANGE_IN_TWO_RECEIVER_ONE_SATELLITE_CARRIER_PHASE("ds"), + + /** 2-receiver/2-satellite carrier phase. */ + TWO_RECEIVER_TWO_SATELLITE_CARRIER_PHASE("d"), + + /** Change in 2-receiver/2-satellite carrier phase. */ + CHANGE_IN_TWO_RECEIVER_TWO_SATELLITE_CARRIER_PHASE("dd"), + + /** Undifferenciated code phase. */ + UNDIFFERENTIATED_CODE_PHASE("U"), + + /** Change in undifferenciated code phase. */ + CHANGE_IN_UNDIFFERENTIATED_CODE_PHASE("dU"), + + /** 2-receiver/1-satellite code phase. */ + TWO_RECEIVER_ONE_SATELLITE_CODE_PHASE("S"), + + /** Change in 2-receiver/1-satellite code phase. */ + CHANGE_IN_TWO_RECEIVER_ONE_SATELLITE_CODE_PHASE("dS"), + + /** 2-receiver/2-satellite code phase. */ + TWO_RECEIVER_TWO_SATELLITE_CODE_PHASE("D"), + + /** Change in 2-receiver/2-satellite code phase. */ + CHANGE_IN_TWO_RECEIVER_TWO_SATELLITE_CODE_PHASE("dD"), + + /** Satellite Laser Ranging. */ + SATELLITE_LASER_RANGING("SLR"), + + /** Mixed data. */ + MIXED("mixed"), + + /** Orbit data. */ + ORBIT("ORBIT"); + + /** Numbers map. */ + private static final Map MAP = new HashMap<>(); + static { + for (final DataUsed dataUsed : values()) { + MAP.put(dataUsed.getKey(), dataUsed); + } + } + + /** Key for the data used. */ + private final String key; + + /** Simple constructor. + * @param key for the data used + */ + DataUsed(final String key) { + this.key = key; + } + + /** Get the key for the data used. + * @return key for the data used + */ + public String getKey() { + return key; + } + + /** Parse the string to get the data used. + * @param s string to parse + * @param fileName file name to generate the error message + * @param version format version + * @return the data used corresponding to the string + * @exception IllegalArgumentException if the string does not correspond to a data used + */ + public static DataUsed parse(final String s, final String fileName, final char version) { + final DataUsed dataUsed = MAP.get(s); + if (dataUsed == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, "data used", s, fileName, version); + } + return dataUsed; + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3.java b/src/main/java/org/orekit/files/sp3/SP3.java index 770eec99fd..767aa99987 100644 --- a/src/main/java/org/orekit/files/sp3/SP3.java +++ b/src/main/java/org/orekit/files/sp3/SP3.java @@ -17,153 +17,44 @@ package org.orekit.files.sp3; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.function.Function; +import java.util.SortedSet; +import java.util.TreeSet; -import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; import org.orekit.files.general.EphemerisFile; import org.orekit.frames.Frame; -import org.orekit.gnss.TimeSystem; -import org.orekit.propagation.BoundedPropagator; import org.orekit.time.AbsoluteDate; -import org.orekit.utils.CartesianDerivativesFilter; -import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.time.ChronologicalComparator; /** * Represents a parsed SP3 orbit file. * @author Thomas Neidhart * @author Evan Ward */ -public class SP3 - implements EphemerisFile { - /** String representation of the center of ephemeris coordinate system. **/ - public static final String SP3_FRAME_CENTER_STRING = "EARTH"; - - /** File type indicator. */ - public enum SP3FileType { - /** GPS only file. */ - GPS, - /** Mixed file. */ - MIXED, - /** GLONASS only file. */ - GLONASS, - /** LEO only file. */ - LEO, - /** Galileo only file. */ - GALILEO, - /** SBAS only file. */ - SBAS, - /** IRNSS only file. */ - IRNSS, - /** COMPASS only file. */ - COMPASS, - /** QZSS only file. */ - QZSS, - /** undefined file format. */ - UNDEFINED - } - - /** Orbit type indicator. */ - public enum SP3OrbitType { - /** fitted. */ - FIT, - /** extrapolated or predicted. */ - EXT, - /** broadcast. */ - BCT, - /** fitted after applying a Helmert transformation. */ - HLM, - /** other type, defined by SP3 file producing agency. - * @since 9.3 - */ - OTHER; - - /** Parse a string to get the type. - * @param s string to parse - * @return the type corresponding to the string - */ - public static SP3OrbitType parseType(final String s) { - final String normalizedString = s.trim().toUpperCase(Locale.US); - if ("EST".equals(normalizedString)) { - return FIT; - } else if ("BHN".equals(normalizedString)) { - // ESOC navigation team uses BHN for files produced - // by their main parameter estimation program Bahn - return FIT; - } else if ("PRO".equals(normalizedString)) { - // ESOC navigation team uses PRO for files produced - // by their orbit propagation program Propag - return EXT; - } else { - try { - return valueOf(normalizedString); - } catch (IllegalArgumentException iae) { - return OTHER; - } - } - } - - } - - /** File type. */ - private SP3FileType type; - - /** Time system. */ - private TimeSystem timeSystem; - - /** Epoch of the file. */ - private AbsoluteDate epoch; - - /** GPS week. */ - private int gpsWeek; +public class SP3 implements EphemerisFile { - /** Seconds of the current GPS week. */ - private double secondsOfWeek; - - /** Julian day. */ - private int julianDay; - - /** Day fraction. */ - private double dayFraction; - - /** Time-interval between epochs. */ - private double epochInterval; - - /** Number of epochs. */ - private int numberOfEpochs; - - /** Coordinate system. */ - private String coordinateSystem; - - /** Data used indicator. */ - private String dataUsed; - - /** Orbit type. */ - private SP3OrbitType orbitType; - - /** Key for orbit type. - * @since 9.3 + /** Header. + * @since 12.0 */ - private String orbitTypeKey; - - /** Agency providing the file. */ - private String agency; + private final SP3Header header; - /** Indicates if data contains velocity or not. */ - private CartesianDerivativesFilter filter; - - /** Standard gravitational parameter in m^3 / s^2. */ + /** Standard gravitational parameter in m³ / s². */ private final double mu; /** Number of samples to use when interpolating. */ private final int interpolationSamples; - /** Maps {@link #coordinateSystem} to a {@link Frame}. */ - private final Function frameBuilder; + /** Reference frame. */ + private final Frame frame; /** A map containing satellite information. */ private Map satellites; @@ -171,219 +62,259 @@ public static SP3OrbitType parseType(final String s) { /** * Create a new SP3 file object. * - * @param mu is the standard gravitational parameter in m^3 / s^2. + * @param mu is the standard gravitational parameter in m³ / s². * @param interpolationSamples number of samples to use in interpolation. - * @param frameBuilder for constructing a reference frame from the identifier + * @param frame reference frame */ - public SP3(final double mu, - final int interpolationSamples, - final Function frameBuilder) { - this.mu = mu; + public SP3(final double mu, final int interpolationSamples, final Frame frame) { + this.header = new SP3Header(); + this.mu = mu; this.interpolationSamples = interpolationSamples; - this.frameBuilder = frameBuilder; - // must be linked has map to preserve order of satellites in the file. - satellites = new LinkedHashMap<>(); - } - - /** - * Set the derivatives filter. - * - * @param filter that indicates which derivatives of position are available. - */ - public void setFilter(final CartesianDerivativesFilter filter) { - this.filter = filter; - } - - /** Returns the {@link SP3FileType} associated with this SP3 file. - * @return the file type for this SP3 file - */ - public SP3FileType getType() { - return type; - } - - /** Set the file type for this SP3 file. - * @param fileType the file type to be set - */ - public void setType(final SP3FileType fileType) { - this.type = fileType; - } - - /** Returns the {@link TimeSystem} used to time-stamp position entries. - * @return the {@link TimeSystem} of the orbit file - */ - public TimeSystem getTimeSystem() { - return timeSystem; - } - - /** Set the time system used in this SP3 file. - * @param system the time system to be set - */ - public void setTimeSystem(final TimeSystem system) { - this.timeSystem = system; - } - - /** Returns the data used indicator from the SP3 file. - * @return the data used indicator (unparsed) - */ - public String getDataUsed() { - return dataUsed; - } - - /** Set the data used indicator for this SP3 file. - * @param data the data used indicator to be set - */ - public void setDataUsed(final String data) { - this.dataUsed = data; - } + this.frame = frame; + this.satellites = new LinkedHashMap<>(); // must be linked hash map to preserve order of satellites in the file + } + + /** Check file is valid. + * @param parsing if true, we are parsing an existing file, and are more lenient + * in order to accept some common errors (like between 86 and 99 satellites + * in SP3a, SP3b or SP3c files) + * @param fileName file name to generate the error message + * @exception OrekitException if file is not valid + */ + public void validate(final boolean parsing, final String fileName) throws OrekitException { + + // check available data + final SortedSet epochs = new TreeSet<>(new ChronologicalComparator()); + boolean hasAccuracy = false; + for (final Map.Entry entry : satellites.entrySet()) { + SP3Coordinate previous = null; + for (final SP3Segment segment : entry.getValue().getSegments()) { + for (final SP3Coordinate coordinate : segment.getCoordinates()) { + final AbsoluteDate previousDate = previous == null ? header.getEpoch() : previous.getDate(); + final double nbSteps = coordinate.getDate().durationFrom(previousDate) / header.getEpochInterval(); + if (FastMath.abs(nbSteps - FastMath.rint(nbSteps)) > 0.001) { + // not an integral number of steps + throw new OrekitException(OrekitMessages.INCONSISTENT_SAMPLING_DATE, + previousDate.shiftedBy(FastMath.rint(nbSteps) * header.getEpochInterval()), + coordinate.getDate()); + } + epochs.add(coordinate.getDate()); + previous = coordinate; + hasAccuracy |= !(coordinate.getPositionAccuracy() == null && + coordinate.getVelocityAccuracy() == null && + Double.isNaN(coordinate.getClockAccuracy()) && + Double.isNaN(coordinate.getClockRateAccuracy())); + } + } + } - /** Returns the start epoch of the orbit file. - * @return the start epoch - */ - public AbsoluteDate getEpoch() { - return epoch; - } + // check versions limitations + if (getSatelliteCount() > getMaxAllowedSatCount(parsing)) { + throw new OrekitException(OrekitMessages.SP3_TOO_MANY_SATELLITES_FOR_VERSION, + header.getVersion(), getMaxAllowedSatCount(parsing), getSatelliteCount(), + fileName); + } - /** Set the epoch of the SP3 file. - * @param time the epoch to be set - */ - public void setEpoch(final AbsoluteDate time) { - this.epoch = time; - } + header.validate(parsing, hasAccuracy, fileName); - /** Returns the GPS week as contained in the SP3 file. - * @return the GPS week of the SP3 file - */ - public int getGpsWeek() { - return gpsWeek; - } + // check epochs + if (epochs.size() != header.getNumberOfEpochs()) { + throw new OrekitException(OrekitMessages.SP3_NUMBER_OF_EPOCH_MISMATCH, + epochs.size(), fileName, header.getNumberOfEpochs()); + } - /** Set the GPS week of the SP3 file. - * @param week the GPS week to be set - */ - public void setGpsWeek(final int week) { - this.gpsWeek = week; } - /** Returns the seconds of the GPS week as contained in the SP3 file. - * @return the seconds of the GPS week - */ - public double getSecondsOfWeek() { - return secondsOfWeek; - } + /** Get the header. + * @return header + * @since 12.0 + */ + public SP3Header getHeader() { + return header; + } + + /** Get maximum number of satellites allowed for format version. + * @param parsing if true, we are parsing an existing file, and are more lenient + * in order to accept some common errors (like between 86 and 99 satellites + * in SP3a, SP3b or SP3c files) + * @return maximum number of satellites allowed for format version + * @since 12.0 + */ + private int getMaxAllowedSatCount(final boolean parsing) { + return header.getVersion() < 'd' ? (parsing ? 99 : 85) : 999; + } + + /** Splice several SP3 files together. + *

          + * Splicing SP3 files is intended to be used when continuous computation + * covering more than one file is needed. The files should all have the exact same + * metadata: {@link SP3Header#getType() type}, {@link SP3Header#getTimeSystem() time system}, + * {@link SP3Header#getCoordinateSystem() coordinate system}, except for satellite accuracy + * which can be different from one file to the next one, and some satellites may + * be missing in some files… Once sorted (which is done internally), if the gap between + * segments from two file is at most {@link SP3Header#getEpochInterval() epoch interval}, + * then the segments are merged as one segment, otherwise the segments are kept separated. + *

          + *

          + * The spliced file only contains the satellites that were present in all files. + * Satellites present in some files and absent from other files are silently + * dropped. + *

          + *

          + * Depending on producer, successive SP3 files either have a gap between the last + * entry of one file and the first entry of the next file (for example files with + * a 5 minutes epoch interval may end at 23:55 and the next file start at 00:00), + * or both files have one point exactly at the splicing date (i.e. 24:00 one day + * and 00:00 next day). In the later case, the last point of the early file is dropped + * and the first point of the late file takes precedence, hence only one point remains + * in the spliced file ; this design choice is made to enforce continuity and + * regular interpolation. + *

          + * @param sp3 SP3 files to merge + * @return merged SP3 + * @since 12.0 + */ + public static SP3 splice(final Collection sp3) { + + // sort the files + final ChronologicalComparator comparator = new ChronologicalComparator(); + final SortedSet sorted = new TreeSet<>((s1, s2) -> comparator.compare(s1.header.getEpoch(), s2.header.getEpoch())); + sorted.addAll(sp3); + + // prepare spliced file + final SP3 first = sorted.first(); + final SP3 spliced = new SP3(first.mu, first.interpolationSamples, first.frame); + spliced.header.setFilter(first.header.getFilter()); + spliced.header.setType(first.header.getType()); + spliced.header.setTimeSystem(first.header.getTimeSystem()); + spliced.header.setDataUsed(first.header.getDataUsed()); + spliced.header.setEpoch(first.header.getEpoch()); + spliced.header.setGpsWeek(first.header.getGpsWeek()); + spliced.header.setSecondsOfWeek(first.header.getSecondsOfWeek()); + spliced.header.setModifiedJulianDay(first.header.getModifiedJulianDay()); + spliced.header.setDayFraction(first.header.getDayFraction()); + spliced.header.setEpochInterval(first.header.getEpochInterval()); + spliced.header.setCoordinateSystem(first.header.getCoordinateSystem()); + spliced.header.setOrbitTypeKey(first.header.getOrbitTypeKey()); + spliced.header.setAgency(first.header.getAgency()); + spliced.header.setPosVelBase(first.header.getPosVelBase()); + spliced.header.setClockBase(first.header.getClockBase()); + + // identify the satellites that are present in all files + final List commonSats = new ArrayList<>(first.header.getSatIds()); + for (final SP3 current : sorted) { + for (final Iterator iter = commonSats.iterator(); iter.hasNext();) { + final String sat = iter.next(); + if (!current.containsSatellite(sat)) { + iter.remove(); + break; + } + } + } - /** Set the seconds of the GPS week for this SP3 file. - * @param seconds the seconds to be set - */ - public void setSecondsOfWeek(final double seconds) { - this.secondsOfWeek = seconds; - } + // create the spliced list + for (final String sat : commonSats) { + spliced.addSatellite(sat); + } - /** Returns the julian day for this SP3 file. - * @return the julian day - */ - public int getJulianDay() { - return julianDay; - } + // in order to be conservative, we keep the worst accuracy from all SP3 files for this satellite + for (int i = 0; i < commonSats.size(); ++i) { + final String sat = commonSats.get(i); + double accuracy = Double.POSITIVE_INFINITY; + for (final SP3 current : sorted) { + accuracy = FastMath.max(accuracy, current.header.getAccuracy(sat)); + } + spliced.header.setAccuracy(i, accuracy); + } - /** Set the julian day for this SP3 file. - * @param day the julian day to be set - */ - public void setJulianDay(final int day) { - this.julianDay = day; - } + // splice files + SP3 previous = null; + int epochCount = 0; + for (final SP3 current : sorted) { - /** Returns the day fraction for this SP3 file. - * @return the day fraction - */ - public double getDayFraction() { - return dayFraction; - } + epochCount += current.header.getNumberOfEpochs(); + if (previous != null) { - /** Set the day fraction for this SP3 file. - * @param fraction the day fraction to be set - */ - public void setDayFraction(final double fraction) { - this.dayFraction = fraction; - } + // check metadata and check if we should drop the last entry of previous file + final boolean dropLast = current.checkSplice(previous); + if (dropLast) { + --epochCount; + } - /** Returns the time interval between epochs (in seconds). - * @return the time interval between epochs - */ - public double getEpochInterval() { - return epochInterval; - } + // append the pending data from previous file + for (final Map.Entry entry : previous.satellites.entrySet()) { + if (commonSats.contains(entry.getKey())) { + final SP3Ephemeris splicedEphemeris = spliced.getEphemeris(entry.getKey()); + for (final SP3Segment segment : entry.getValue().getSegments()) { + final List coordinates = segment.getCoordinates(); + for (int i = 0; i < coordinates.size() - (dropLast ? 1 : 0); ++i) { + splicedEphemeris.addCoordinate(coordinates.get(i), spliced.header.getEpochInterval()); + } + } + } + } - /** Set the epoch interval for this SP3 file. - * @param interval the interval between orbit entries - */ - public void setEpochInterval(final double interval) { - this.epochInterval = interval; - } + } - /** Returns the number of epochs contained in this orbit file. - * @return the number of epochs - */ - public int getNumberOfEpochs() { - return numberOfEpochs; - } + previous = current; - /** Set the number of epochs as contained in the SP3 file. - * @param epochCount the number of epochs to be set - */ - public void setNumberOfEpochs(final int epochCount) { - this.numberOfEpochs = epochCount; - } + } + spliced.header.setNumberOfEpochs(epochCount); + + // append the pending data from last file + for (final Map.Entry entry : previous.satellites.entrySet()) { + if (commonSats.contains(entry.getKey())) { + final SP3Ephemeris splicedEphemeris = spliced.getEphemeris(entry.getKey()); + for (final SP3Segment segment : entry.getValue().getSegments()) { + for (final SP3Coordinate coordinate : segment.getCoordinates()) { + splicedEphemeris.addCoordinate(coordinate, spliced.header.getEpochInterval()); + } + } + } + } - /** Returns the coordinate system of the entries in this orbit file. - * @return the coordinate system - */ - public String getCoordinateSystem() { - return coordinateSystem; - } + return spliced; - /** Set the coordinate system used for the orbit entries. - * @param system the coordinate system to be set - */ - public void setCoordinateSystem(final String system) { - this.coordinateSystem = system; } - /** Returns the {@link SP3OrbitType} for this SP3 file. - * @return the orbit type + /** Check if instance can be spliced after previous one. + * @param previous SP3 file (should already be sorted to be before current instance), can be null + * @return true if last entry of previous file should be dropped as first entry of current file + * is at very close date and will take precedence + * @exception OrekitException if metadata are incompatible + * @since 12.0 */ - public SP3OrbitType getOrbitType() { - return orbitType; - } + private boolean checkSplice(final SP3 previous) throws OrekitException { - /** Returns the orbit type key for this SP3 file. - * @return the orbit type key - * @since 9.3 - */ - public String getOrbitTypeKey() { - return orbitTypeKey; - } + if (!(previous.header.getType() == header.getType() && + previous.header.getTimeSystem() == header.getTimeSystem() && + previous.header.getOrbitType() == header.getOrbitType() && + previous.header.getCoordinateSystem().equals(header.getCoordinateSystem()) && + previous.header.getDataUsed().equals(header.getDataUsed()) && + previous.header.getAgency().equals(header.getAgency()))) { + throw new OrekitException(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA); + } - /** Set the orbit type key for this SP3 file. - * @param oTypeKey the orbit type key to be set - * @since 9.3 - */ - public void setOrbitTypeKey(final String oTypeKey) { - this.orbitTypeKey = oTypeKey; - this.orbitType = SP3OrbitType.parseType(oTypeKey); - } + boolean dropLast = false; + for (final Map.Entry entry : previous.satellites.entrySet()) { + final SP3Ephemeris previousEphem = entry.getValue(); + final SP3Ephemeris currentEphem = satellites.get(entry.getKey()); + if (currentEphem != null) { + if (!(previousEphem.getAvailableDerivatives() == currentEphem.getAvailableDerivatives() && + previousEphem.getFrame() == currentEphem.getFrame() && + previousEphem.getInterpolationSamples() == currentEphem.getInterpolationSamples() && + Precision.equals(previousEphem.getMu(), currentEphem.getMu(), 2))) { + throw new OrekitException(OrekitMessages.SP3_INCOMPATIBLE_SATELLITE_MEDATADA, + entry.getKey()); + } else { + final double dt = currentEphem.getStart().durationFrom(previousEphem.getStop()); + dropLast = dt < 0.001 * header.getEpochInterval(); + } + } + } - /** Returns the agency that prepared this SP3 file. - * @return the agency - */ - public String getAgency() { - return agency; - } + return dropLast; - /** Set the agency string for this SP3 file. - * @param agencyStr the agency string to be set - */ - public void setAgency(final String agencyStr) { - this.agency = agencyStr; } /** Add a new satellite with a given identifier to the list of @@ -391,8 +322,8 @@ public void setAgency(final String agencyStr) { * @param satId the satellite identifier */ public void addSatellite(final String satId) { - // only add satellites which have not been added before - satellites.putIfAbsent(satId, new SP3Ephemeris(satId)); + header.addSatId(satId); + satellites.putIfAbsent(satId, new SP3Ephemeris(satId, mu, frame, interpolationSamples, header.getFilter())); } @Override @@ -400,45 +331,44 @@ public Map getSatellites() { return Collections.unmodifiableMap(satellites); } - /** Get the number of satellites contained in this orbit file. - * @return the number of satellites + /** Get an ephemeris. + * @param index index of the satellite + * @return satellite ephemeris + * @since 12.0 */ - public int getSatelliteCount() { - return satellites.size(); - } - - /** - * Set the formal accuracy for a satellite. - * - * @param index is the index of the satellite. - * @param accuracy of the satellite, in m. - */ - public void setAccuracy(final int index, final double accuracy) { + public SP3Ephemeris getEphemeris(final int index) { int n = index; - for (final SP3Ephemeris ephemeris : satellites.values()) { + for (final Map.Entry entry : satellites.entrySet()) { if (n == 0) { - ephemeris.setAccuracy(accuracy); - return; + return entry.getValue(); } n--; } + + // satellite not found + throw new OrekitException(OrekitMessages.INVALID_SATELLITE_ID, index); + } - /** - * Get the formal accuracy for a satellite. - * - * @param index is the index of the satellite. - * @return accuracy of the satellite, in m. + /** Get an ephemeris. + * @param satId satellite identifier + * @return satellite ephemeris, or null if not found + * @since 12.0 */ - public double getAccuracy(final int index) { - int n = index; - for (final SP3Ephemeris ephemeris : satellites.values()) { - if (n == 0) { - return ephemeris.getAccuracy(); - } - n--; + public SP3Ephemeris getEphemeris(final String satId) { + final SP3Ephemeris ephemeris = satellites.get(satId); + if (ephemeris == null) { + throw new OrekitException(OrekitMessages.INVALID_SATELLITE_ID, satId); + } else { + return ephemeris; } - return Double.NaN; + } + + /** Get the number of satellites contained in this orbit file. + * @return the number of satellites + */ + public int getSatelliteCount() { + return satellites.size(); } /** Tests whether a satellite with the given id is contained in this orbit @@ -448,175 +378,7 @@ public double getAccuracy(final int index) { * {@code false} otherwise */ public boolean containsSatellite(final String satId) { - return satellites.containsKey(satId); - } - - /** - * Adds a new P/V coordinate for a given satellite. - * - * @param satId the satellite identifier - * @param coord the P/V coordinate of the satellite - */ - public void addSatelliteCoordinate(final String satId, final SP3Coordinate coord) { - satellites.get(satId).coordinates.add(coord); - } - - /** An ephemeris for a single satellite in a SP3 file. */ - public class SP3Ephemeris - implements EphemerisFile.SatelliteEphemeris, - EphemerisFile.EphemerisSegment { - - /** Satellite ID. */ - private final String id; - /** Ephemeris Data. */ - private final List coordinates; - /** Accuracy in m. */ - private double accuracy; - - /** - * Create an ephemeris for a single satellite. - * - * @param id of the satellite. - */ - public SP3Ephemeris(final String id) { - this.id = id; - this.coordinates = new ArrayList<>(); - } - - @Override - public String getId() { - return this.id; - } - - @Override - public double getMu() { - return mu; - } - - @Override - public Frame getFrame() { - return frameBuilder.apply(SP3_FRAME_CENTER_STRING); - } - - @Override - public int getInterpolationSamples() { - return interpolationSamples; - } - - @Override - public CartesianDerivativesFilter getAvailableDerivatives() { - return filter; - } - - @Override - public List getCoordinates() { - return Collections.unmodifiableList(this.coordinates); - } - - /** Returns a list containing only {@code this}. */ - @Override - public List getSegments() { - return Collections.singletonList(this); - } - - @Override - public AbsoluteDate getStart() { - return coordinates.get(0).getDate(); - } - - @Override - public AbsoluteDate getStop() { - return coordinates.get(coordinates.size() - 1).getDate(); - } - - @Override - public BoundedPropagator getPropagator() { - return EphemerisSegment.super.getPropagator(); - } - - /** - * Set the accuracy for this satellite. - * - * @param accuracy in m. - */ - public void setAccuracy(final double accuracy) { - this.accuracy = accuracy; - } - - /** - * Get the formal accuracy for this satellite. - * - *

          The accuracy is limited by the SP3 standard to be a power of 2 in mm. - * The value returned here is in meters.

          - * - * @return magnitude of one standard deviation, in m. - */ - public double getAccuracy() { - return accuracy; - } - - } - - /** A single record of position clock and possibly derivatives in an SP3 file. */ - public static class SP3Coordinate extends TimeStampedPVCoordinates { - - /** Serializable UID. */ - private static final long serialVersionUID = 20161116L; - /** Clock correction in s. */ - private final double clock; - /** Clock rate in s / s. */ - private final double clockRate; - - /** - * Create a coordinate with only position. - * - * @param date of validity. - * @param position of the satellite. - * @param clock correction in s. - */ - public SP3Coordinate(final AbsoluteDate date, - final Vector3D position, - final double clock) { - this(date, position, Vector3D.ZERO, clock, 0); - } - - /** - * Create a coordinate with position and velocity. - * - * @param date of validity. - * @param position of the satellite. - * @param velocity of the satellite. - * @param clock correction in s. - * @param clockRate in s / s. - */ - public SP3Coordinate(final AbsoluteDate date, - final Vector3D position, - final Vector3D velocity, - final double clock, - final double clockRate) { - super(date, position, velocity, Vector3D.ZERO); - this.clock = clock; - this.clockRate = clockRate; - } - - /** - * Returns the clock correction value. - * - * @return the clock correction in s. - */ - public double getClockCorrection() { - return clock; - } - - /** - * Returns the clock rate. - * - * @return the clock rate of change in s/s. - */ - public double getClockRateChange() { - return clockRate; - } - + return header.getSatIds().contains(satId); } } diff --git a/src/main/java/org/orekit/files/sp3/SP3Coordinate.java b/src/main/java/org/orekit/files/sp3/SP3Coordinate.java new file mode 100644 index 0000000000..77d7a17eaf --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3Coordinate.java @@ -0,0 +1,181 @@ +/* Copyright 2002-2012 Space Applications Services + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** A single record of position clock and possibly derivatives in an SP3 file. + * @author Thomas Neidhart + * @author Evan Ward + * @author Luc Maisonobe + * @since 12.0 + */ +public class SP3Coordinate extends TimeStampedPVCoordinates { + + /** Dummy coordinate with all fields set to 0.0. */ + public static final SP3Coordinate DUMMY = new SP3Coordinate(AbsoluteDate.ARBITRARY_EPOCH, + Vector3D.ZERO, null, Vector3D.ZERO, null, + SP3Utils.CLOCK_UNIT.toSI(SP3Utils.DEFAULT_CLOCK_VALUE), + Double.NaN, + SP3Utils.CLOCK_RATE_UNIT.toSI(SP3Utils.DEFAULT_CLOCK_RATE_VALUE), + Double.NaN, + false, false, false, false); + + /** Serializable UID. */ + private static final long serialVersionUID = 20230903L; + + /** Clock correction in s. */ + private final double clock; + + /** Clock rate in s / s. */ + private final double clockRate; + + /** Position accuracy. */ + private final Vector3D positionAccuracy; + + /** Velocity accuracy. */ + private final Vector3D velocityAccuracy; + + /** Clock accuracy. */ + private final double clockAccuracy; + + /** Clock rate accuracy. */ + private final double clockRateAccuracy; + + /** Clock event flag. */ + private final boolean clockEvent; + + /** Clock prediction flag. */ + private final boolean clockPrediction; + + /** Orbit maneuver event flag. */ + private final boolean orbitManeuverEvent; + + /** Clock orbit prediction flag. */ + private final boolean orbitPrediction; + + /** Create a coordinate with position and velocity. + * @param date of validity. + * @param position of the satellite. + * @param positionAccuracy of the satellite (null if not known). + * @param velocity of the satellite. + * @param velocityAccuracy of the satellite (null if not known). + * @param clock correction in s. + * @param clockAccuracy correction in s ({@code Double.NaN} if not known). + * @param clockRate in s / s. + * @param clockRateAccuracy in s / s ({@code Double.NaN} if not known). + * @param clockEvent clock event flag + * @param clockPrediction clock prediction flag + * @param orbitManeuverEvent orbit maneuver event flag + * @param orbitPrediction flag + */ + public SP3Coordinate(final AbsoluteDate date, + final Vector3D position, final Vector3D positionAccuracy, + final Vector3D velocity, final Vector3D velocityAccuracy, + final double clock, final double clockAccuracy, + final double clockRate, final double clockRateAccuracy, + final boolean clockEvent, final boolean clockPrediction, + final boolean orbitManeuverEvent, final boolean orbitPrediction) { + + super(date, position, velocity, Vector3D.ZERO); + this.clock = clock; + this.clockRate = clockRate; + + this.positionAccuracy = positionAccuracy; + this.velocityAccuracy = velocityAccuracy; + this.clockAccuracy = clockAccuracy; + this.clockRateAccuracy = clockRateAccuracy; + this.clockEvent = clockEvent; + this.clockPrediction = clockPrediction; + this.orbitManeuverEvent = orbitManeuverEvent; + this.orbitPrediction = orbitPrediction; + + } + + /** Get the clock correction value. + * @return the clock correction in s. + */ + public double getClockCorrection() { + return clock; + } + + /** Get the clock rate. + * @return the clock rate of change in s/s. + */ + public double getClockRateChange() { + return clockRate; + } + + /** Get the position accuracy. + * @return position accuracy in m (null if not known). + */ + public Vector3D getPositionAccuracy() { + return positionAccuracy; + } + + /** Get the velocity accuracy. + * @return velocity accuracy in m/s (null if not known). + */ + public Vector3D getVelocityAccuracy() { + return velocityAccuracy; + } + + /** Get the clock accuracy. + * @return clock accuracy in s ({@code Double.NaN} if not known). + */ + public double getClockAccuracy() { + return clockAccuracy; + } + + /** Get the clock rate accuracy. + * @return clock rate accuracy in s/s ({@code Double.NaN} if not known). + */ + public double getClockRateAccuracy() { + return clockRateAccuracy; + } + + /** Get clock event flag. + * @return true if clock event flag is set + */ + public boolean hasClockEvent() { + return clockEvent; + } + + /** Get clock prediction flag. + * @return true if clock prediction flag is set + */ + public boolean hasClockPrediction() { + return clockPrediction; + } + + /** Get orbit maneuver event flag. + * @return true if orbit maneuver event flag is set + */ + public boolean hasOrbitManeuverEvent() { + return orbitManeuverEvent; + } + + /** Get orbit prediction flag. + * @return true if orbit prediction flag is set + */ + public boolean hasOrbitPrediction() { + return orbitPrediction; + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java b/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java new file mode 100644 index 0000000000..ca6c627854 --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3CoordinateHermiteInterpolator.java @@ -0,0 +1,129 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.stream.Stream; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; + +/** Interpolator for {@link SP3Coordinate SP3 coordinates}. + *

          + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @author Luc Maisonobe + * @see HermiteInterpolator + * @see SP3Coordinate + * @since 12.0 + */ +public class SP3CoordinateHermiteInterpolator extends AbstractTimeInterpolator { + + /** Flag for using velocity and clock rate. */ + private final boolean useRates; + + /** + * Constructor. + *

          + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

          + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param useRates if true, use velocity and clock rates for interpolation + */ + public SP3CoordinateHermiteInterpolator(final int interpolationPoints, + final double extrapolationThreshold, + final boolean useRates) { + super(interpolationPoints, extrapolationThreshold); + this.useRates = useRates; + } + + /** + * {@inheritDoc} + *

          + * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact + * derivative of position. + *

          + * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always + * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be + * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and + * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the + * positions. + */ + @Override + protected SP3Coordinate interpolate(final InterpolationData interpolationData) { + + // Get date + final AbsoluteDate date = interpolationData.getInterpolationDate(); + + // Convert sample to stream + final Stream sample = interpolationData.getNeighborList().stream(); + + // Set up an interpolator taking derivatives into account + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Add sample points + if (useRates) { + // populate sample with position, clock, velocity and clock rate data + sample.forEach(c -> { + interpolator.addSamplePoint(c.getDate().durationFrom(date), + new double[] { + c.getPosition().getX(), + c.getPosition().getY(), + c.getPosition().getZ(), + c.getClockCorrection(), + }, + new double[] { + c.getVelocity().getX(), + c.getVelocity().getY(), + c.getVelocity().getZ(), + c.getClockRateChange(), + }); + }); + } else { + // populate sample with position and clock data, ignoring velocity and clock rate + sample.forEach(c -> { + interpolator.addSamplePoint(c.getDate().durationFrom(date), + new double[] { + c.getPosition().getX(), + c.getPosition().getY(), + c.getPosition().getZ(), + c.getClockCorrection(), + }); + }); + } + + // Interpolate + final double[][] interpolated = interpolator.derivatives(0.0, 1); + + // Build a new interpolated instance + return new SP3Coordinate(date, + new Vector3D(interpolated[0][0], interpolated[0][1], interpolated[0][2]), null, + new Vector3D(interpolated[1][0], interpolated[1][1], interpolated[1][2]), null, + interpolated[0][3], Double.NaN, + interpolated[1][3], Double.NaN, + false, false, false, false); + + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java b/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java new file mode 100644 index 0000000000..d648a2b30e --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java @@ -0,0 +1,138 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.orekit.files.general.EphemerisFile; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.CartesianDerivativesFilter; + +/** Single satellite ephemeris from an {@link SP3 SP3} file. + * @author Luc Maisonobe + * @since 12.0 + */ +public class SP3Ephemeris implements EphemerisFile.SatelliteEphemeris { + + /** Satellite ID. */ + private final String id; + + /** Standard gravitational parameter in m³ / s². */ + private final double mu; + + /** Reference frame. */ + private final Frame frame; + + /** Number of points to use for interpolation. */ + private final int interpolationSamples; + + /** Available derivatives. */ + private final CartesianDerivativesFilter filter; + + /** Segments. */ + private final List segments; + + /** Create an ephemeris for a single satellite. + * @param id of the satellite. + * @param mu standard gravitational parameter to use for creating + * {@link org.orekit.orbits.Orbit Orbits} from the ephemeris data. + * @param frame reference frame + * @param interpolationSamples number of points to use for interpolation + * @param filter available derivatives + */ + public SP3Ephemeris(final String id, final double mu, final Frame frame, + final int interpolationSamples, final CartesianDerivativesFilter filter) { + this.id = id; + this.mu = mu; + this.frame = frame; + this.interpolationSamples = interpolationSamples; + this.filter = filter; + this.segments = new ArrayList<>(); + } + + /** {@inheritDoc} */ + @Override + public String getId() { + return this.id; + } + + /** {@inheritDoc} */ + @Override + public double getMu() { + return mu; + } + + /** {@inheritDoc} */ + @Override + public List getSegments() { + return Collections.unmodifiableList(segments); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStart() { + return segments.isEmpty() ? null : segments.get(0).getStart(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStop() { + return segments.isEmpty() ? null : segments.get(segments.size() - 1).getStop(); + } + + /** Get the reference frame. + * @return reference frame + */ + public Frame getFrame() { + return frame; + } + + /** Get the number of points to use for interpolation. + * @return number of points to use for interpolation + */ + public int getInterpolationSamples() { + return interpolationSamples; + } + + /** Get the available derivatives. + * @return available derivatives + */ + public CartesianDerivativesFilter getAvailableDerivatives() { + return filter; + } + + /** Adds a new P/V coordinate. + * @param coord the P/V coordinate of the satellite + * @param maxGap maximum gap between segments + */ + public void addCoordinate(final SP3Coordinate coord, final double maxGap) { + final AbsoluteDate lastDate = getStop(); + final SP3Segment segment; + if (lastDate == null || coord.getDate().durationFrom(lastDate) > maxGap) { + // we need to create a new segment + segment = new SP3Segment(mu, frame, interpolationSamples, filter); + segments.add(segment); + } else { + segment = segments.get(segments.size() - 1); + } + segment.addCoordinate(coord); + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3FileType.java b/src/main/java/org/orekit/files/sp3/SP3FileType.java new file mode 100644 index 0000000000..700ab3cb45 --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3FileType.java @@ -0,0 +1,94 @@ +/* Copyright 2002-2012 Space Applications Services + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.HashMap; +import java.util.Map; + +/** File type indicator. + * @author Thomas Neidhart + * @author Evan Ward + * @author Luc Maisonobe + * @since 12.0 + */ +public enum SP3FileType { + + /** GPS only file. */ + GPS("G"), + + /** Mixed file. */ + MIXED("M"), + + /** GLONASS only file. */ + GLONASS("R"), + + /** LEO only file. */ + LEO("L"), + + /** Galileo only file. */ + GALILEO("E"), + + /** SBAS only file. */ + SBAS("S"), + + /** IRNSS only file. */ + IRNSS("I"), + + /** COMPASS only file. */ + COMPASS("C"), + + /** QZSS only file. */ + QZSS("J"), + + /** undefined file format. */ + UNDEFINED("?"); + + /** Numbers map. */ + private static final Map MAP = new HashMap<>(); + static { + for (final SP3FileType type : values()) { + MAP.put(type.getKey(), type); + } + } + + /** Key for the file type. */ + private final String key; + + /** Simple constructor. + * @param key for the file type + */ + SP3FileType(final String key) { + this.key = key; + } + + /** Get the key for the file type. + * @return key for the file type + */ + public String getKey() { + return key; + } + + /** Parse the string to get the data used. + * @param s string to parse + * @return the file type corresponding to the string + */ + public static SP3FileType parse(final String s) { + final SP3FileType type = MAP.get(s.toUpperCase()); + return (type == null) ? UNDEFINED : type; + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3Header.java b/src/main/java/org/orekit/files/sp3/SP3Header.java new file mode 100644 index 0000000000..7095b7b452 --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3Header.java @@ -0,0 +1,482 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.gnss.TimeSystem; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.CartesianDerivativesFilter; + +/** Header for SP3 files. + * @author Luc Maisonobe + * @since 12.0 + */ +public class SP3Header { + + /** String representation of the center of ephemeris coordinate system. **/ + public static final String SP3_FRAME_CENTER_STRING = "EARTH"; + + /** Name for pos/vel accuracy base header entry. */ + private static final String POS_VEL_ACCURACY_BASE = "pos/vel accuracy base"; + + /** Name for clock accuracy base header entry. */ + private static final String CLOCK_ACCURACY_BASE = "clock accuracy base"; + + /** Name for comments header entry. */ + private static final String COMMENTS = "comments"; + + /** File version. */ + private char version; + + /** File type. */ + private SP3FileType type; + + /** Time system. */ + private TimeSystem timeSystem; + + /** Epoch of the file. */ + private AbsoluteDate epoch; + + /** GPS week. */ + private int gpsWeek; + + /** Seconds of the current GPS week. */ + private double secondsOfWeek; + + /** Julian day. */ + private int modifiedJulianDay; + + /** Day fraction. */ + private double dayFraction; + + /** Time-interval between epochs. */ + private double epochInterval; + + /** Number of epochs. */ + private int numberOfEpochs; + + /** Coordinate system. */ + private String coordinateSystem; + + /** Data used indicator. */ + private List dataUsed; + + /** Orbit type. */ + private SP3OrbitType orbitType; + + /** Key for orbit type. */ + private String orbitTypeKey; + + /** Agency providing the file. */ + private String agency; + + /** Indicates if data contains velocity or not. */ + private CartesianDerivativesFilter filter; + + /** Base for position/velocity accuracy. */ + private double posVelBase; + + /** Base for clock/clock-rate accuracy. */ + private double clockBase; + + /** Satellite identifiers. */ + private List satIds; + + /** Satellite accuracies. */ + private double[] accuracies; + + /** Comments. */ + private final List comments; + + /** Create a new SP3 header. + */ + public SP3Header() { + this.version = '?'; + this.satIds = new ArrayList<>(); + this.accuracies = null; + this.comments = new ArrayList<>(); + } + + /** Check header is valid. + * @param parsing if true, we are parsing an existing file, and are more lenient + * in order to accept some common errors (like between 86 and 99 satellites + * in SP3a, SP3b or SP3c files) + * @param hasAccuracy if true, there are accuracy data in the file + * @param fileName file name to generate the error message + * @exception OrekitException if file is not valid + */ + void validate(final boolean parsing, final boolean hasAccuracy, final String fileName) throws OrekitException { + + // check version + if ("abcd".indexOf(getVersion()) < 0) { + throw new OrekitException(OrekitMessages.SP3_UNSUPPORTED_VERSION, getVersion()); + } + + if (getVersion() == 'a') { + // in SP3 version a, the base accuracy must be set to 0 + if (getPosVelBase() != 0.0) { + throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, + POS_VEL_ACCURACY_BASE, getPosVelBase(), fileName, getVersion()); + } + if (getClockBase() != 0.0) { + throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, + CLOCK_ACCURACY_BASE, getClockBase(), fileName, getVersion()); + } + } else if (hasAccuracy) { + // in SP3 versions after version a, the base accuracy must be set if entries specify accuracy + if (getPosVelBase() <= 0.0) { + throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, + POS_VEL_ACCURACY_BASE, getPosVelBase(), fileName, getVersion()); + } + if (getClockBase() <= 0.0) { + throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, + CLOCK_ACCURACY_BASE, getClockBase(), fileName, getVersion()); + } + } + if (getVersion() < 'd') { + // in SP3 versions a, b, and c, there are exactly 4 comments with max length 57 + // (60 minus first three characters) + if (comments.size() != 4 || + comments.get(0).length() > 57 || + comments.get(1).length() > 57 || + comments.get(2).length() > 57 || + comments.get(3).length() > 57) { + throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, + COMMENTS, "/* …", fileName, getVersion()); + } + } else { + // starting with SP3 version d, there is an unspecified number of comments with max length 77 + // (80 minus first three characters) + for (final String c : comments) { + if (c.length() > 77) { + throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY, + COMMENTS, c, fileName, getVersion()); + } + } + } + + } + + /** Set the file version. + * @param version file version + */ + public void setVersion(final char version) { + this.version = version; + } + + /** Get the file version. + * @return file version + */ + public char getVersion() { + return version; + } + + /** Set the derivatives filter. + * @param filter that indicates which derivatives of position are available. + */ + public void setFilter(final CartesianDerivativesFilter filter) { + this.filter = filter; + } + + /** Get the derivatives filter. + * @return filter with available derivatives + */ + public CartesianDerivativesFilter getFilter() { + return filter; + } + + /** Returns the {@link SP3FileType} associated with this SP3 file. + * @return the file type for this SP3 file + */ + public SP3FileType getType() { + return type; + } + + /** Set the file type for this SP3 file. + * @param fileType the file type to be set + */ + public void setType(final SP3FileType fileType) { + this.type = fileType; + } + + /** Returns the {@link TimeSystem} used to time-stamp position entries. + * @return the {@link TimeSystem} of the orbit file + */ + public TimeSystem getTimeSystem() { + return timeSystem; + } + + /** Set the time system used in this SP3 file. + * @param system the time system to be set + */ + public void setTimeSystem(final TimeSystem system) { + this.timeSystem = system; + } + + /** Returns the data used indicator from the SP3 file. + * @return the data used indicator + */ + public List getDataUsed() { + return dataUsed; + } + + /** Set the data used indicator for this SP3 file. + * @param dataUsed the data used indicator to be set + */ + public void setDataUsed(final List dataUsed) { + this.dataUsed = dataUsed; + } + + /** Returns the start epoch of the orbit file. + * @return the start epoch + */ + public AbsoluteDate getEpoch() { + return epoch; + } + + /** Set the epoch of the SP3 file. + * @param time the epoch to be set + */ + public void setEpoch(final AbsoluteDate time) { + this.epoch = time; + } + + /** Returns the GPS week as contained in the SP3 file. + * @return the GPS week of the SP3 file + */ + public int getGpsWeek() { + return gpsWeek; + } + + /** Set the GPS week of the SP3 file. + * @param week the GPS week to be set + */ + public void setGpsWeek(final int week) { + this.gpsWeek = week; + } + + /** Returns the seconds of the GPS week as contained in the SP3 file. + * @return the seconds of the GPS week + */ + public double getSecondsOfWeek() { + return secondsOfWeek; + } + + /** Set the seconds of the GPS week for this SP3 file. + * @param seconds the seconds to be set + */ + public void setSecondsOfWeek(final double seconds) { + this.secondsOfWeek = seconds; + } + + /** Returns the modified julian day for this SP3 file. + * @return the modified julian day + */ + public int getModifiedJulianDay() { + return modifiedJulianDay; + } + + /** Set the modified julian day for this SP3 file. + * @param day the modified julian day to be set + */ + public void setModifiedJulianDay(final int day) { + this.modifiedJulianDay = day; + } + + /** Returns the day fraction for this SP3 file. + * @return the day fraction + */ + public double getDayFraction() { + return dayFraction; + } + + /** Set the day fraction for this SP3 file. + * @param fraction the day fraction to be set + */ + public void setDayFraction(final double fraction) { + this.dayFraction = fraction; + } + + /** Returns the time interval between epochs (in seconds). + * @return the time interval between epochs + */ + public double getEpochInterval() { + return epochInterval; + } + + /** Set the epoch interval for this SP3 file. + * @param interval the interval between orbit entries + */ + public void setEpochInterval(final double interval) { + this.epochInterval = interval; + } + + /** Returns the number of epochs contained in this orbit file. + * @return the number of epochs + */ + public int getNumberOfEpochs() { + return numberOfEpochs; + } + + /** Set the number of epochs as contained in the SP3 file. + * @param epochCount the number of epochs to be set + */ + public void setNumberOfEpochs(final int epochCount) { + this.numberOfEpochs = epochCount; + } + + /** Returns the coordinate system of the entries in this orbit file. + * @return the coordinate system + */ + public String getCoordinateSystem() { + return coordinateSystem; + } + + /** Set the coordinate system used for the orbit entries. + * @param system the coordinate system to be set + */ + public void setCoordinateSystem(final String system) { + this.coordinateSystem = system; + } + + /** Returns the {@link SP3OrbitType} for this SP3 file. + * @return the orbit type + */ + public SP3OrbitType getOrbitType() { + return orbitType; + } + + /** Returns the orbit type key for this SP3 file. + * @return the orbit type key + */ + public String getOrbitTypeKey() { + return orbitTypeKey; + } + + /** Set the orbit type key for this SP3 file. + * @param oTypeKey the orbit type key to be set + */ + public void setOrbitTypeKey(final String oTypeKey) { + this.orbitTypeKey = oTypeKey; + this.orbitType = SP3OrbitType.parseType(oTypeKey); + } + + /** Returns the agency that prepared this SP3 file. + * @return the agency + */ + public String getAgency() { + return agency; + } + + /** Set the agency string for this SP3 file. + * @param agencyStr the agency string to be set + */ + public void setAgency(final String agencyStr) { + this.agency = agencyStr; + } + + /** Set the base for position/velocity accuracy. + * @param posVelBase base for position/velocity accuracy + */ + public void setPosVelBase(final double posVelBase) { + this.posVelBase = posVelBase; + } + + /** Get the base for position/velocity accuracy. + * @return base for position/velocity accuracy + */ + public double getPosVelBase() { + return posVelBase; + } + + /** Set the base for clock/clock-rate accuracy. + * @param clockBase base for clock/clock-rate accuracy + */ + public void setClockBase(final double clockBase) { + this.clockBase = clockBase; + } + + /** Get the base for clock/clock-rate accuracy. + * @return base for clock/clock-rate accuracy + */ + public double getClockBase() { + return clockBase; + } + + /** Add a satellite identifier. + * @param satId satellite identifier + */ + public void addSatId(final String satId) { + satIds.add(satId); + } + + /** Get the satellite identifiers. + * @return satellites identifiers + */ + public List getSatIds() { + return Collections.unmodifiableList(satIds); + } + + /** Set the accuracy. + * @param index satellite index in {@link #getSatIds()} + * @param accuracy in m + */ + public void setAccuracy(final int index, final double accuracy) { + if (accuracies == null) { + // lazy allocation of the array + accuracies = new double[satIds.size()]; + } + accuracies[index] = accuracy; + } + + /** Get the formal accuracy. + *

          + * The accuracy is limited by the SP3 standard to be a power of 2 in mm. + * The value returned here is in meters. + *

          + * @param satId satellite identifier + * @return magnitude of one standard deviation, in m. + */ + public double getAccuracy(final String satId) { + for (int i = 0; i < satIds.size(); ++i) { + if (satIds.get(i).equals(satId)) { + return accuracies[i]; + } + } + return Double.NaN; + } + + /** Get the comments. + * @return an unmodifiable view of comments + */ + public List getComments() { + return Collections.unmodifiableList(comments); + } + + /** Add a comment. + * @param comment comment to add + */ + public void addComment(final String comment) { + comments.add(comment); + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3OrbitType.java b/src/main/java/org/orekit/files/sp3/SP3OrbitType.java new file mode 100644 index 0000000000..4902a74ded --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3OrbitType.java @@ -0,0 +1,70 @@ +/* Copyright 2002-2012 Space Applications Services + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.Locale; + +/** Orbit type indicator. + * @author Thomas Neidhart + * @author Evan Ward + * @author Luc Maisonobe + */ +public enum SP3OrbitType { + + /** fitted. */ + FIT, + + /** extrapolated or predicted. */ + EXT, + + /** broadcast. */ + BCT, + + /** fitted after applying a Helmert transformation. */ + HLM, + + /** other type, defined by SP3 file producing agency. + * @since 9.3 + */ + OTHER; + + /** Parse a string to get the type. + * @param s string to parse + * @return the type corresponding to the string + */ + public static SP3OrbitType parseType(final String s) { + final String normalizedString = s.trim().toUpperCase(Locale.US); + if ("EST".equals(normalizedString)) { + return FIT; + } else if ("BHN".equals(normalizedString)) { + // ESOC navigation team uses BHN for files produced + // by their main parameter estimation program Bahn + return FIT; + } else if ("PRO".equals(normalizedString)) { + // ESOC navigation team uses PRO for files produced + // by their orbit propagation program Propag + return EXT; + } else { + try { + return valueOf(normalizedString); + } catch (IllegalArgumentException iae) { + return OTHER; + } + } + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3Parser.java b/src/main/java/org/orekit/files/sp3/SP3Parser.java index 3b75b62a66..b5ef3106af 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Parser.java +++ b/src/main/java/org/orekit/files/sp3/SP3Parser.java @@ -20,13 +20,13 @@ import java.io.IOException; import java.io.Reader; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.Optional; import java.util.Scanner; import java.util.function.Function; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -38,8 +38,6 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.files.general.EphemerisFileParser; -import org.orekit.files.sp3.SP3.SP3Coordinate; -import org.orekit.files.sp3.SP3.SP3FileType; import org.orekit.frames.Frame; import org.orekit.gnss.TimeSystem; import org.orekit.time.AbsoluteDate; @@ -58,29 +56,29 @@ * Note: this parser is thread-safe, so calling {@link #parse} from * different threads is allowed. *

          - * @see SP3-a file format - * @see SP3-c file format - * @see SP3-d file format + * @see SP3-a file format + * @see SP3-c file format + * @see SP3-d file format * @author Thomas Neidhart * @author Luc Maisonobe */ public class SP3Parser implements EphemerisFileParser { - /** Bad or absent clock values are to be set to 999999.999999. */ - public static final double DEFAULT_CLOCK_VALUE = 999999.999999; + /** String representation of the center of ephemeris coordinate system. **/ + public static final String SP3_FRAME_CENTER_STRING = "EARTH"; /** Spaces delimiters. */ private static final String SPACES = "\\s+"; - /** One millimeter, in meters. */ - private static final double MILLIMETER = 1.0e-3; - - /** Standard gravitational parameter in m^3 / s^2. */ + /** Standard gravitational parameter in m³/s². */ private final double mu; + /** Number of data points to use in interpolation. */ private final int interpolationSamples; + /** Mapping from frame identifier in the file to a {@link Frame}. */ private final Function frameBuilder; + /** Set of time scales. */ private final TimeScales timeScales; @@ -136,10 +134,10 @@ public SP3Parser(final double mu, final int interpolationSamples, final Function frameBuilder, final TimeScales timeScales) { - this.mu = mu; + this.mu = mu; this.interpolationSamples = interpolationSamples; - this.frameBuilder = frameBuilder; - this.timeScales = timeScales; + this.frameBuilder = frameBuilder; + this.timeScales = timeScales; } /** @@ -168,45 +166,38 @@ public SP3 parse(final DataSource source) { } // initialize internal data structures - final ParseInfo pi = new ParseInfo(); + final ParseInfo pi = new ParseInfo(source.getName()); int lineNumber = 0; - Stream candidateParsers = Stream.of(LineParser.HEADER_VERSION); - for (String line = br.readLine(); line != null; line = br.readLine()) { - ++lineNumber; - final String l = line; - final Optional selected = candidateParsers.filter(p -> p.canHandle(l)).findFirst(); - if (selected.isPresent()) { - try { - selected.get().parse(line, pi); - } catch (StringIndexOutOfBoundsException | NumberFormatException e) { - throw new OrekitException(e, - OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, source.getName(), line); + Iterable candidateParsers = Collections.singleton(LineParser.HEADER_VERSION); + nextLine: + for (String line = br.readLine(); line != null; line = br.readLine()) { + ++lineNumber; + for (final LineParser candidate : candidateParsers) { + if (candidate.canHandle(line)) { + try { + candidate.parse(line, pi); + if (pi.done) { + break nextLine; + } + candidateParsers = candidate.allowedNext(); + continue nextLine; + } catch (StringIndexOutOfBoundsException | NumberFormatException e) { + throw new OrekitException(e, + OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, pi.fileName, line); + } + } } - candidateParsers = selected.get().allowedNext(); - } else { + + // no parsers found for this line throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, source.getName(), line); - } - if (pi.done) { - if (pi.nbEpochs != pi.file.getNumberOfEpochs()) { - throw new OrekitException(OrekitMessages.SP3_NUMBER_OF_EPOCH_MISMATCH, - pi.nbEpochs, source.getName(), pi.file.getNumberOfEpochs()); - } - return pi.file; - } - } + lineNumber, pi.fileName, line); - // Sometimes, the "EOF" key is not available in the file - // If the expected number of entries has been read - // we can suppose that the file has been read properly - if (pi.nbEpochs == pi.file.getNumberOfEpochs()) { - return pi.file; - } + } - // we never reached the EOF marker or number of epochs doesn't correspond to the expected number - throw new OrekitException(OrekitMessages.SP3_UNEXPECTED_END_OF_FILE, lineNumber); + pi.file.validate(true, pi.fileName); + return pi.file; } catch (IOException ioe) { throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage()); @@ -214,34 +205,6 @@ public SP3 parse(final DataSource source) { } - /** Returns the {@link SP3FileType} that corresponds to a given string in a SP3 file. - * @param fileType file type as string - * @return file type as enum - */ - private static SP3FileType getFileType(final String fileType) { - SP3FileType type = SP3FileType.UNDEFINED; - if ("G".equalsIgnoreCase(fileType)) { - type = SP3FileType.GPS; - } else if ("M".equalsIgnoreCase(fileType)) { - type = SP3FileType.MIXED; - } else if ("R".equalsIgnoreCase(fileType)) { - type = SP3FileType.GLONASS; - } else if ("L".equalsIgnoreCase(fileType)) { - type = SP3FileType.LEO; - } else if ("S".equalsIgnoreCase(fileType)) { - type = SP3FileType.SBAS; - } else if ("I".equalsIgnoreCase(fileType)) { - type = SP3FileType.IRNSS; - } else if ("E".equalsIgnoreCase(fileType)) { - type = SP3FileType.GALILEO; - } else if ("C".equalsIgnoreCase(fileType)) { - type = SP3FileType.COMPASS; - } else if ("J".equalsIgnoreCase(fileType)) { - type = SP3FileType.QZSS; - } - return type; - } - /** Transient data used for parsing a sp3 file. The data is kept in a * separate data structure to make the parser thread-safe. *

          Note: The class intentionally does not provide accessor @@ -249,6 +212,11 @@ private static SP3FileType getFileType(final String fileType) { */ private class ParseInfo { + /** File name. + * @since 12.0 + */ + private final String fileName; + /** Set of time scales for parsing dates. */ private final TimeScales timeScales; @@ -261,9 +229,39 @@ private class ParseInfo { /** The latest position as read from the SP3 file. */ private Vector3D latestPosition; + /** The latest position accuracy as read from the SP3 file. + * @since 12.0 + */ + private Vector3D latestPositionAccuracy; + /** The latest clock value as read from the SP3 file. */ private double latestClock; + /** The latest clock value as read from the SP3 file. + * @since 12.0 + */ + private double latestClockAccuracy; + + /** The latest clock event flag as read from the SP3 file. + * @since 12.0 + */ + private boolean latestClockEvent; + + /** The latest clock prediction flag as read from the SP3 file. + * @since 12.0 + */ + private boolean latestClockPrediction; + + /** The latest orbit maneuver event flag as read from the SP3 file. + * @since 12.0 + */ + private boolean latestOrbitManeuverEvent; + + /** The latest orbit prediction flag as read from the SP3 file. + * @since 12.0 + */ + private boolean latestOrbitPrediction; + /** Indicates if the SP3 file has velocity entries. */ private boolean hasVelocityEntries; @@ -279,22 +277,16 @@ private class ParseInfo { /** The number of satellites accuracies already seen. */ private int nbAccuracies; - /** The number of epochs already seen. */ - private int nbEpochs; - /** End Of File reached indicator. */ private boolean done; - /** The base for pos/vel. */ - //private double posVelBase; - - /** The base for clock/rate. */ - //private double clockBase; - - /** Create a new {@link ParseInfo} object. */ - protected ParseInfo() { - this.timeScales = SP3Parser.this.timeScales; - file = new SP3(mu, interpolationSamples, frameBuilder); + /** Create a new {@link ParseInfo} object. + * @param fileName file name + */ + protected ParseInfo(final String fileName) { + this.fileName = fileName; + this.timeScales = SP3Parser.this.timeScales; + file = new SP3(mu, interpolationSamples, frameBuilder.apply(SP3_FRAME_CENTER_STRING)); latestEpoch = null; latestPosition = null; latestClock = 0.0; @@ -303,10 +295,7 @@ protected ParseInfo() { timeScale = timeScales.getGPS(); maxSatellites = 0; nbAccuracies = 0; - nbEpochs = 0; done = false; - //posVelBase = 2d; - //clockBase = 2d; } } @@ -325,15 +314,12 @@ public void parse(final String line, final ParseInfo pi) { scanner.skip("#"); final String v = scanner.next(); - final char version = v.substring(0, 1).toLowerCase().charAt(0); - if (version != 'a' && version != 'b' && version != 'c' && version != 'd') { - throw new OrekitException(OrekitMessages.SP3_UNSUPPORTED_VERSION, version); - } + pi.file.getHeader().setVersion(v.substring(0, 1).toLowerCase().charAt(0)); pi.hasVelocityEntries = "V".equals(v.substring(1, 2)); - pi.file.setFilter(pi.hasVelocityEntries ? - CartesianDerivativesFilter.USE_PV : - CartesianDerivativesFilter.USE_P); + pi.file.getHeader().setFilter(pi.hasVelocityEntries ? + CartesianDerivativesFilter.USE_PV : + CartesianDerivativesFilter.USE_P); final int year = Integer.parseInt(v.substring(2)); final int month = scanner.nextInt(); @@ -346,21 +332,26 @@ public void parse(final String line, final ParseInfo pi) { hour, minute, second); final int numEpochs = scanner.nextInt(); - pi.file.setNumberOfEpochs(numEpochs); + pi.file.getHeader().setNumberOfEpochs(numEpochs); // data used indicator - pi.file.setDataUsed(scanner.next()); + final String fullSpec = scanner.next(); + final List dataUsed = new ArrayList<>(); + for (final String specifier : fullSpec.split("\\+")) { + dataUsed.add(DataUsed.parse(specifier, pi.fileName, pi.file.getHeader().getVersion())); + } + pi.file.getHeader().setDataUsed(dataUsed); - pi.file.setCoordinateSystem(scanner.next()); - pi.file.setOrbitTypeKey(scanner.next()); - pi.file.setAgency(scanner.next()); + pi.file.getHeader().setCoordinateSystem(scanner.next()); + pi.file.getHeader().setOrbitTypeKey(scanner.next()); + pi.file.getHeader().setAgency(scanner.next()); } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_DATE_TIME_REFERENCE); + public Iterable allowedNext() { + return Collections.singleton(HEADER_DATE_TIME_REFERENCE); } }, @@ -377,22 +368,22 @@ public void parse(final String line, final ParseInfo pi) { scanner.skip("##"); // gps week - pi.file.setGpsWeek(scanner.nextInt()); + pi.file.getHeader().setGpsWeek(scanner.nextInt()); // seconds of week - pi.file.setSecondsOfWeek(scanner.nextDouble()); + pi.file.getHeader().setSecondsOfWeek(scanner.nextDouble()); // epoch interval - pi.file.setEpochInterval(scanner.nextDouble()); - // julian day - pi.file.setJulianDay(scanner.nextInt()); + pi.file.getHeader().setEpochInterval(scanner.nextDouble()); + // modified julian day + pi.file.getHeader().setModifiedJulianDay(scanner.nextInt()); // day fraction - pi.file.setDayFraction(scanner.nextDouble()); + pi.file.getHeader().setDayFraction(scanner.nextDouble()); } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_SAT_IDS); + public Iterable allowedNext() { + return Collections.singleton(HEADER_SAT_IDS); } }, @@ -423,8 +414,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_SAT_IDS, HEADER_ACCURACY); + public Iterable allowedNext() { + return Arrays.asList(HEADER_SAT_IDS, HEADER_ACCURACY); } }, @@ -442,7 +433,10 @@ public void parse(final String line, final ParseInfo pi) { if (sub.length() > 0) { final int exponent = Integer.parseInt(sub); // the accuracy is calculated as 2**exp (in mm) - pi.file.setAccuracy(pi.nbAccuracies++, (2 << exponent) * MILLIMETER); + pi.file.getHeader().setAccuracy(pi.nbAccuracies++, + SP3Utils.siAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, + SP3Utils.POS_VEL_BASE_ACCURACY, + exponent)); } startIdx += 3; } @@ -450,8 +444,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_ACCURACY, HEADER_TIME_SYSTEM); + public Iterable allowedNext() { + return Arrays.asList(HEADER_ACCURACY, HEADER_TIME_SYSTEM); } }, @@ -463,9 +457,9 @@ public Stream allowedNext() { @Override public void parse(final String line, final ParseInfo pi) { - if (pi.file.getType() == null) { + if (pi.file.getHeader().getType() == null) { // this the first custom fields line, the only one really used - pi.file.setType(getFileType(line.substring(3, 5).trim())); + pi.file.getHeader().setType(SP3FileType.parse(line.substring(3, 5).trim())); // now identify the time system in use final String tsStr = line.substring(9, 12).trim(); @@ -475,19 +469,19 @@ public void parse(final String line, final ParseInfo pi) { } else { ts = TimeSystem.valueOf(tsStr); } - pi.file.setTimeSystem(ts); + pi.file.getHeader().setTimeSystem(ts); pi.timeScale = ts.getTimeScale(pi.timeScales); // now we know the time scale used, we can set the file epoch - pi.file.setEpoch(new AbsoluteDate(pi.epoch, pi.timeScale)); + pi.file.getHeader().setEpoch(new AbsoluteDate(pi.epoch, pi.timeScale)); } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_TIME_SYSTEM, HEADER_STANDARD_DEVIATIONS); + public Iterable allowedNext() { + return Arrays.asList(HEADER_TIME_SYSTEM, HEADER_STANDARD_DEVIATIONS); } }, @@ -498,23 +492,23 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // String base = line.substring(3, 13).trim(); - // if (!base.equals("0.0000000")) { - // // (mm or 10**-4 mm/sec) - // pi.posVelBase = Double.valueOf(base); - // } - - // base = line.substring(14, 26).trim(); - // if (!base.equals("0.000000000")) { - // // (psec or 10**-4 psec/sec) - // pi.clockBase = Double.valueOf(base); - // } + final double posVelBase = Double.parseDouble(line.substring(3, 13).trim()); + if (posVelBase != 0.0) { + // (mm or 10⁻⁴ mm/s) + pi.file.getHeader().setPosVelBase(posVelBase); + } + + final double clockBase = Double.parseDouble(line.substring(14, 26).trim()); + if (clockBase != 0.0) { + // (ps or 10⁻⁴ ps/s) + pi.file.getHeader().setClockBase(clockBase); + } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_STANDARD_DEVIATIONS, HEADER_CUSTOM_PARAMETERS); + public Iterable allowedNext() { + return Arrays.asList(HEADER_STANDARD_DEVIATIONS, HEADER_CUSTOM_PARAMETERS); } }, @@ -530,8 +524,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_CUSTOM_PARAMETERS, HEADER_COMMENTS); + public Iterable allowedNext() { + return Arrays.asList(HEADER_CUSTOM_PARAMETERS, HEADER_COMMENTS); } }, @@ -542,13 +536,13 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // ignore comments + pi.file.getHeader().addComment(line.substring(line.indexOf('*') + 1).trim()); } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(HEADER_COMMENTS, DATA_EPOCH); + public Iterable allowedNext() { + return Arrays.asList(HEADER_COMMENTS, DATA_EPOCH); } }, @@ -559,14 +553,25 @@ public Stream allowedNext() { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - final int year = Integer.parseInt(line.substring(3, 7).trim()); - final int month = Integer.parseInt(line.substring(8, 10).trim()); - final int day = Integer.parseInt(line.substring(11, 13).trim()); - final int hour = Integer.parseInt(line.substring(14, 16).trim()); - final int minute = Integer.parseInt(line.substring(17, 19).trim()); - final double second = Double.parseDouble(line.substring(20).trim()); - - // some SP3 files have weird epochs as in the following two examples, where + final int year; + final int month; + final int day; + final int hour; + final int minute; + final double second; + try (Scanner s1 = new Scanner(line); + Scanner s2 = s1.useDelimiter(SPACES); + Scanner scanner = s2.useLocale(Locale.US)) { + scanner.skip("\\*"); + year = scanner.nextInt(); + month = scanner.nextInt(); + day = scanner.nextInt(); + hour = scanner.nextInt(); + minute = scanner.nextInt(); + second = scanner.nextDouble(); + } + + // some SP3 files have weird epochs as in the following three examples, where // the middle dates are wrong // // * 2016 7 6 16 58 0.00000000 @@ -589,6 +594,15 @@ public void parse(final String line, final ParseInfo pi) { // PL51 2744.983592 -9000.639164 7931.904779 // VL51 -21072.925764 -40899.633288 -38801.567078 // + // * 2021 12 31 0 0 0.00000000 + // PL51 6578.459330 5572.231927 -8703.502054 + // VL51 -5356.007694 -48869.881161 -35036.676469 + // * 2022 1 0 0 2 0.00000000 + // PL51 6499.035610 4978.263048 -9110.135595 + // VL51 -7881.633197 -50092.564035 -32717.740919 + // * 2022 1 0 0 4 0.00000000 + // PL51 6389.313975 4370.794537 -9488.314264 + // VL51 -10403.797055 -51119.231402 -30295.421935 // In the first case, the date should really be 2016 7 6 17 0 0.00000000, // i.e as the minutes field overflows, the hours field should be incremented // In the second case, the date should really be 2016 7 7 0 0 0.00000000, @@ -596,7 +610,11 @@ public void parse(final String line, final ParseInfo pi) { // we cannot be sure how carry was managed when these bogus files were written // so we try different options, incrementing or not previous field, and selecting // the closest one to expected date - DateComponents dc = new DateComponents(year, month, day); + // In the third case, there are two different errors: the date is globally + // shifted to the left by one character, and the day is 0 instead of 1 + DateComponents dc = day == 0 ? + new DateComponents(new DateComponents(year, month, 1), -1) : + new DateComponents(year, month, day); final List candidates = new ArrayList<>(); int h = hour; int m = minute; @@ -618,11 +636,11 @@ public void parse(final String line, final ParseInfo pi) { } addCandidate(candidates, dc, h, m, s, pi.timeScale); final AbsoluteDate expected = pi.latestEpoch == null ? - pi.file.getEpoch() : - pi.latestEpoch.shiftedBy(pi.file.getEpochInterval()); + pi.file.getHeader().getEpoch() : + pi.latestEpoch.shiftedBy(pi.file.getHeader().getEpochInterval()); pi.latestEpoch = null; for (final AbsoluteDate candidate : candidates) { - if (FastMath.abs(candidate.durationFrom(expected)) < 0.01 * pi.file.getEpochInterval()) { + if (FastMath.abs(candidate.durationFrom(expected)) < 0.01 * pi.file.getHeader().getEpochInterval()) { pi.latestEpoch = candidate; } } @@ -631,7 +649,7 @@ public void parse(final String line, final ParseInfo pi) { // in order to generate again an exception pi.latestEpoch = new AbsoluteDate(year, month, day, hour, minute, second, pi.timeScale); } - pi.nbEpochs++; + } /** Add an epoch candidate to a list. @@ -655,8 +673,8 @@ private void addCandidate(final List candidates, final DateCompone /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(DATA_POSITION); + public Iterable allowedNext() { + return Collections.singleton(DATA_POSITION); } }, @@ -670,62 +688,69 @@ public void parse(final String line, final ParseInfo pi) { final String satelliteId = line.substring(1, 4).trim(); if (!pi.file.containsSatellite(satelliteId)) { - pi.latestPosition = null; + pi.latestPosition = Vector3D.ZERO; } else { - final double x = Double.parseDouble(line.substring(4, 18).trim()); - final double y = Double.parseDouble(line.substring(18, 32).trim()); - final double z = Double.parseDouble(line.substring(32, 46).trim()); + + final SP3Header header = pi.file.getHeader(); // the position values are in km and have to be converted to m - pi.latestPosition = new Vector3D(x * 1000, y * 1000, z * 1000); + pi.latestPosition = new Vector3D(SP3Utils.POSITION_UNIT.toSI(Double.parseDouble(line.substring(4, 18).trim())), + SP3Utils.POSITION_UNIT.toSI(Double.parseDouble(line.substring(18, 32).trim())), + SP3Utils.POSITION_UNIT.toSI(Double.parseDouble(line.substring(32, 46).trim()))); // clock (microsec) - pi.latestClock = line.trim().length() <= 46 ? - DEFAULT_CLOCK_VALUE : - Double.parseDouble(line.substring(46, 60).trim()) * 1e-6; - - // the additional items are optional and not read yet - - // if (line.length() >= 73) { - // // x-sdev (b**n mm) - // int xStdDevExp = Integer.valueOf(line.substring(61, - // 63).trim()); - // // y-sdev (b**n mm) - // int yStdDevExp = Integer.valueOf(line.substring(64, - // 66).trim()); - // // z-sdev (b**n mm) - // int zStdDevExp = Integer.valueOf(line.substring(67, - // 69).trim()); - // // c-sdev (b**n psec) - // int cStdDevExp = Integer.valueOf(line.substring(70, - // 73).trim()); - // - // pi.posStdDevRecord = - // new PositionStdDevRecord(FastMath.pow(pi.posVelBase, xStdDevExp), - // FastMath.pow(pi.posVelBase, - // yStdDevExp), FastMath.pow(pi.posVelBase, zStdDevExp), - // FastMath.pow(pi.clockBase, cStdDevExp)); - // - // String clockEventFlag = line.substring(74, 75); - // String clockPredFlag = line.substring(75, 76); - // String maneuverFlag = line.substring(78, 79); - // String orbitPredFlag = line.substring(79, 80); - // } - - if (!pi.hasVelocityEntries) { - final SP3Coordinate coord = - new SP3Coordinate(pi.latestEpoch, - pi.latestPosition, - pi.latestClock); - pi.file.addSatelliteCoordinate(satelliteId, coord); + pi.latestClock = SP3Utils.CLOCK_UNIT.toSI(line.trim().length() <= 46 ? + SP3Utils.DEFAULT_CLOCK_VALUE : + Double.parseDouble(line.substring(46, 60).trim())); + + if (pi.latestPosition.getNorm() > 0) { + + if (line.length() < 69 || line.substring(61, 69).trim().length() == 0) { + pi.latestPositionAccuracy = null; + } else { + pi.latestPositionAccuracy = new Vector3D(SP3Utils.siAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, + header.getPosVelBase(), + Integer.parseInt(line.substring(61, 63).trim())), + SP3Utils.siAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, + header.getPosVelBase(), + Integer.parseInt(line.substring(64, 66).trim())), + SP3Utils.siAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, + header.getPosVelBase(), + Integer.parseInt(line.substring(67, 69).trim()))); + } + + if (line.length() < 73 || line.substring(70, 73).trim().length() == 0) { + pi.latestClockAccuracy = Double.NaN; + } else { + pi.latestClockAccuracy = SP3Utils.siAccuracy(SP3Utils.CLOCK_ACCURACY_UNIT, + header.getClockBase(), + Integer.parseInt(line.substring(70, 73).trim())); + } + + pi.latestClockEvent = line.length() < 75 ? false : line.substring(74, 75).equals("E"); + pi.latestClockPrediction = line.length() < 76 ? false : line.substring(75, 76).equals("P"); + pi.latestOrbitManeuverEvent = line.length() < 79 ? false : line.substring(78, 79).equals("M"); + pi.latestOrbitPrediction = line.length() < 80 ? false : line.substring(79, 80).equals("P"); + + if (!pi.hasVelocityEntries) { + final SP3Coordinate coord = + new SP3Coordinate(pi.latestEpoch, + pi.latestPosition, pi.latestPositionAccuracy, + Vector3D.ZERO, null, + pi.latestClock, pi.latestClockAccuracy, + 0.0, Double.NaN, + pi.latestClockEvent, pi.latestClockPrediction, + pi.latestOrbitManeuverEvent, pi.latestOrbitPrediction); + pi.file.getEphemeris(satelliteId).addCoordinate(coord, header.getEpochInterval()); + } } } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(DATA_EPOCH, DATA_POSITION, DATA_POSITION_CORRELATION, DATA_VELOCITY, EOF); + public Iterable allowedNext() { + return Arrays.asList(DATA_EPOCH, DATA_POSITION, DATA_POSITION_CORRELATION, DATA_VELOCITY, EOF); } }, @@ -741,8 +766,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(DATA_EPOCH, DATA_POSITION, DATA_VELOCITY, EOF); + public Iterable allowedNext() { + return Arrays.asList(DATA_EPOCH, DATA_POSITION, DATA_VELOCITY, EOF); } }, @@ -755,50 +780,60 @@ public Stream allowedNext() { public void parse(final String line, final ParseInfo pi) { final String satelliteId = line.substring(1, 4).trim(); - if (pi.file.containsSatellite(satelliteId)) { - final double xv = Double.parseDouble(line.substring(4, 18).trim()); - final double yv = Double.parseDouble(line.substring(18, 32).trim()); - final double zv = Double.parseDouble(line.substring(32, 46).trim()); + if (pi.file.containsSatellite(satelliteId) && pi.latestPosition.getNorm() > 0) { + + final SP3Header header = pi.file.getHeader(); // the velocity values are in dm/s and have to be converted to m/s - final Vector3D velocity = new Vector3D(xv / 10d, yv / 10d, zv / 10d); + final Vector3D velocity = new Vector3D(SP3Utils.VELOCITY_UNIT.toSI(Double.parseDouble(line.substring(4, 18).trim())), + SP3Utils.VELOCITY_UNIT.toSI(Double.parseDouble(line.substring(18, 32).trim())), + SP3Utils.VELOCITY_UNIT.toSI(Double.parseDouble(line.substring(32, 46).trim()))); // clock rate in file is 1e-4 us / s - final double clockRateChange = line.trim().length() <= 46 ? - DEFAULT_CLOCK_VALUE : - Double.parseDouble(line.substring(46, 60).trim()) * 1e-4; - - // the additional items are optional and not read yet - - // if (line.length() >= 73) { - // // xvel-sdev (b**n 10**-4 mm/sec) - // int xVstdDevExp = Integer.valueOf(line.substring(61, - // 63).trim()); - // // yvel-sdev (b**n 10**-4 mm/sec) - // int yVstdDevExp = Integer.valueOf(line.substring(64, - // 66).trim()); - // // zvel-sdev (b**n 10**-4 mm/sec) - // int zVstdDevExp = Integer.valueOf(line.substring(67, - // 69).trim()); - // // clkrate-sdev (b**n 10**-4 psec/sec) - // int clkStdDevExp = Integer.valueOf(line.substring(70, - // 73).trim()); - // } + final double clockRateChange = SP3Utils.CLOCK_RATE_UNIT.toSI(line.trim().length() <= 46 ? + SP3Utils.DEFAULT_CLOCK_RATE_VALUE : + Double.parseDouble(line.substring(46, 60).trim())); + + final Vector3D velocityAccuracy; + if (line.length() < 69 || line.substring(61, 69).trim().length() == 0) { + velocityAccuracy = null; + } else { + velocityAccuracy = new Vector3D(SP3Utils.siAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, + header.getPosVelBase(), + Integer.parseInt(line.substring(61, 63).trim())), + SP3Utils.siAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, + header.getPosVelBase(), + Integer.parseInt(line.substring(64, 66).trim())), + SP3Utils.siAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, + header.getPosVelBase(), + Integer.parseInt(line.substring(67, 69).trim()))); + } + + final double clockRateAccuracy; + if (line.length() < 73 || line.substring(70, 73).trim().length() == 0) { + clockRateAccuracy = Double.NaN; + } else { + clockRateAccuracy = SP3Utils.siAccuracy(SP3Utils.CLOCK_RATE_ACCURACY_UNIT, + header.getClockBase(), + Integer.parseInt(line.substring(70, 73).trim())); + } final SP3Coordinate coord = new SP3Coordinate(pi.latestEpoch, - pi.latestPosition, - velocity, - pi.latestClock, - clockRateChange); - pi.file.addSatelliteCoordinate(satelliteId, coord); + pi.latestPosition, pi.latestPositionAccuracy, + velocity, velocityAccuracy, + pi.latestClock, pi.latestClockAccuracy, + clockRateChange, clockRateAccuracy, + pi.latestClockEvent, pi.latestClockPrediction, + pi.latestOrbitManeuverEvent, pi.latestOrbitPrediction); + pi.file.getEphemeris(satelliteId).addCoordinate(coord, header.getEpochInterval()); } } /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(DATA_EPOCH, DATA_POSITION, DATA_VELOCITY_CORRELATION, EOF); + public Iterable allowedNext() { + return Arrays.asList(DATA_EPOCH, DATA_POSITION, DATA_VELOCITY_CORRELATION, EOF); } }, @@ -814,8 +849,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(DATA_EPOCH, DATA_POSITION, EOF); + public Iterable allowedNext() { + return Arrays.asList(DATA_EPOCH, DATA_POSITION, EOF); } }, @@ -831,8 +866,8 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override - public Stream allowedNext() { - return Stream.of(EOF); + public Iterable allowedNext() { + return Collections.singleton(EOF); } }; @@ -856,7 +891,7 @@ public Stream allowedNext() { /** Get the allowed parsers for next line. * @return allowed parsers for next line */ - public abstract Stream allowedNext(); + public abstract Iterable allowedNext(); /** Check if parser can handle line. * @param line line to parse diff --git a/src/main/java/org/orekit/files/sp3/SP3Segment.java b/src/main/java/org/orekit/files/sp3/SP3Segment.java new file mode 100644 index 0000000000..cfd73070e8 --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3Segment.java @@ -0,0 +1,130 @@ +/* Copyright Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.files.general.EphemerisFile; +import org.orekit.frames.Frame; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.CartesianDerivativesFilter; + +/** One segment of an {@link SP3Ephemeris}. + * @author Thomas Neidhart + * @author Evan Ward + * @author Luc Maisonobe + * @since 12.0 + */ +public class SP3Segment implements EphemerisFile.EphemerisSegment { + + /** Standard gravitational parameter in m³ / s². */ + private final double mu; + + /** Reference frame. */ + private final Frame frame; + + /** Number of points to use for interpolation. */ + private final int interpolationSamples; + + /** Available derivatives. */ + private final CartesianDerivativesFilter filter; + + /** Ephemeris Data. */ + private final List coordinates; + + /** Simple constructor. + * @param mu standard gravitational parameter to use for creating + * {@link org.orekit.orbits.Orbit Orbits} from the ephemeris data. + * @param frame reference frame + * @param interpolationSamples number of points to use for interpolation + * @param filter available derivatives + */ + public SP3Segment(final double mu, final Frame frame, + final int interpolationSamples, final CartesianDerivativesFilter filter) { + this.mu = mu; + this.frame = frame; + this.interpolationSamples = interpolationSamples; + this.filter = filter; + this.coordinates = new ArrayList<>(); + } + + /** {@inheritDoc} */ + @Override + public double getMu() { + return mu; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStart() { + return coordinates.get(0).getDate(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getStop() { + return coordinates.get(coordinates.size() - 1).getDate(); + } + + /** {@inheritDoc} */ + @Override + public Frame getFrame() { + return frame; + } + + /** {@inheritDoc} */ + @Override + public int getInterpolationSamples() { + return interpolationSamples; + } + + /** {@inheritDoc} */ + @Override + public CartesianDerivativesFilter getAvailableDerivatives() { + return filter; + } + + /** {@inheritDoc} */ + @Override + public List getCoordinates() { + return Collections.unmodifiableList(this.coordinates); + } + + /** Adds a new P/V coordinate. + * @param coord the P/V coordinate of the satellite + */ + public void addCoordinate(final SP3Coordinate coord) { + coordinates.add(coord); + } + + /** {@inheritDoc} */ + @Override + public BoundedPropagator getPropagator() { + return EphemerisFile.EphemerisSegment.super.getPropagator(); + } + + /** {@inheritDoc} */ + @Override + public BoundedPropagator getPropagator(final AttitudeProvider attitudeProvider) { + return EphemerisFile.EphemerisSegment.super.getPropagator(attitudeProvider); + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3Utils.java b/src/main/java/org/orekit/files/sp3/SP3Utils.java new file mode 100644 index 0000000000..c6dbfd05de --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3Utils.java @@ -0,0 +1,87 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import org.hipparchus.util.FastMath; +import org.orekit.utils.units.Unit; + +/** Constants for SP3 files. + * @since 12.0 + * @author Luc Maisonobe + */ +public class SP3Utils { + + /** Bad or absent clock values are to be set to 999999.999999. */ + public static final double DEFAULT_CLOCK_VALUE = 999999.999999; + + /** Bad or absent clock rate values are to be set to 999999.999999. */ + public static final double DEFAULT_CLOCK_RATE_VALUE = 999999.999999; + + /** Base for general position/velocity accuracy. */ + public static final double POS_VEL_BASE_ACCURACY = 2.0; + + /** Position unit. */ + public static final Unit POSITION_UNIT = Unit.parse("km"); + + /** Position accuracy unit. */ + public static final Unit POSITION_ACCURACY_UNIT = Unit.parse("mm"); + + /** Velocity unit. */ + public static final Unit VELOCITY_UNIT = Unit.parse("dm/s"); + + /** Velocity accuracy unit. */ + public static final Unit VELOCITY_ACCURACY_UNIT = Unit.parse("mm/s").scale("10⁻⁴mm/s", 1.0e-4); + + /** Clock unit. */ + public static final Unit CLOCK_UNIT = Unit.parse("µs"); + + /** Clock accuracy unit. */ + public static final Unit CLOCK_ACCURACY_UNIT = Unit.parse("ps"); + + /** Clock rate unit. */ + public static final Unit CLOCK_RATE_UNIT = Unit.parse("µs/s").scale("10⁻⁴µs/s", 1.0e-4); + + /** Clock rate accuracy unit. */ + public static final Unit CLOCK_RATE_ACCURACY_UNIT = Unit.parse("ps/s").scale("10⁻⁴ps/s", 1.0e-4); + + /** Private constructor for utility class. + */ + private SP3Utils() { + // nothing to do + } + + /** Convert an accuracy to SI units. + * @param unit accuracy unit + * @param base base + * @param accuracyIndex index of accuracy + * @return accuracy in SI units + */ + public static double siAccuracy(final Unit unit, final double base, final int accuracyIndex) { + return unit.toSI(FastMath.pow(base, accuracyIndex)); + } + + /** Convert an accuracy from SI units. + * @param unit accuracy unit + * @param base base + * @param accuracy in SI units + * @return accuracyIndex index of accuracy + */ + public static int indexAccuracy(final Unit unit, final double base, final double accuracy) { + return (int) FastMath.ceil(FastMath.log(unit.fromSI(accuracy)) / FastMath.log(base)); + } + +} diff --git a/src/main/java/org/orekit/files/sp3/SP3Writer.java b/src/main/java/org/orekit/files/sp3/SP3Writer.java new file mode 100644 index 0000000000..335777da88 --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/SP3Writer.java @@ -0,0 +1,477 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.sp3; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.hipparchus.util.FastMath; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScales; +import org.orekit.utils.CartesianDerivativesFilter; + +/** Writer for SP3 file. + * @author Luc Maisonobe + * @since 12.0 + */ +public class SP3Writer { + + /** End Of Line. */ + private static final String EOL = System.lineSeparator(); + + /** Prefix for accuracy lines. */ + private static final String ACCURACY_LINE_PREFIX = "++ "; + + /** Prefix for comment lines. */ + private static final String COMMENT_LINE_PREFIX = "/* "; + + /** Format for accuracy base lines. */ + private static final String ACCURACY_BASE_FORMAT = "%%f %10.7f %12.9f %14.11f %18.15f%n"; + + /** Constant additional parameters lines. */ + private static final String ADDITIONAL_PARAMETERS_LINE = "%i 0 0 0 0 0 0 0 0 0"; + + /** Format for one 2 digits integer field. */ + private static final String TWO_DIGITS_INTEGER = "%2d"; + + /** Format for one 3 digits integer field. */ + private static final String THREE_DIGITS_INTEGER = "%3d"; + + /** Format for one 14.6 digits float field. */ + private static final String FOURTEEN_SIX_DIGITS_FLOAT = "%14.6f"; + + /** Format for three blanks field. */ + private static final String THREE_BLANKS = " "; + + /** Time system default line. */ + private static final String TIME_SYSTEM_DEFAULT = "%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc"; + + /** Destination of generated output. */ + private final Appendable output; + + /** Output name for error messages. */ + private final String outputName; + + /** Set of time scales used for parsing dates. */ + private final TimeScales timeScales; + + /** Simple constructor. + * @param output destination of generated output + * @param outputName output name for error messages + * @param timeScales set of time scales used for parsing dates + */ + public SP3Writer(final Appendable output, final String outputName, final TimeScales timeScales) { + this.output = output; + this.outputName = outputName; + this.timeScales = timeScales; + } + + /** Write a SP3 file. + * @param sp3 SP3 file to write + * @exception IOException if an I/O error occurs. + */ + public void write(final SP3 sp3) + throws IOException { + sp3.validate(false, outputName); + writeHeader(sp3.getHeader()); + + // set up iterators for all satellites + final CoordinatesIterator[] iterators = new CoordinatesIterator[sp3.getSatelliteCount()]; + int k = 0; + for (final Map.Entry entry : sp3.getSatellites().entrySet()) { + iterators[k++] = new CoordinatesIterator(entry.getValue()); + } + + final TimeScale timeScale = sp3.getHeader().getTimeSystem().getTimeScale(timeScales); + for (AbsoluteDate date = earliest(iterators); !date.equals(AbsoluteDate.FUTURE_INFINITY); date = earliest(iterators)) { + + // epoch + final DateTimeComponents dtc = date.getComponents(timeScale); + output.append(String.format(Locale.US, "* %4d %2d %2d %2d %2d %11.8f%n", + dtc.getDate().getYear(), + dtc.getDate().getMonth(), + dtc.getDate().getDay(), + dtc.getTime().getHour(), + dtc.getTime().getMinute(), + dtc.getTime().getSecond())); + + for (final CoordinatesIterator iter : iterators) { + + final SP3Coordinate coordinate; + if (iter.pending != null && + FastMath.abs(iter.pending.getDate().durationFrom(date)) <= 0.001 * sp3.getHeader().getEpochInterval()) { + // the pending coordinate date matches current epoch + coordinate = iter.pending; + iter.advance(); + } else { + // the pending coordinate does not match current epoch + coordinate = SP3Coordinate.DUMMY; + } + + // position + writePosition(sp3.getHeader(), iter.id, coordinate); + + if (sp3.getHeader().getFilter() != CartesianDerivativesFilter.USE_P) { + // velocity + writeVelocity(sp3.getHeader(), iter.id, coordinate); + } + + } + + } + + output.append("EOF"). + append(EOL); + + } + + /** Find earliest date in ephemerides. + * @param iterators ephemerides iterators + * @return earliest date in iterators + */ + private AbsoluteDate earliest(final CoordinatesIterator[] iterators) { + AbsoluteDate date = AbsoluteDate.FUTURE_INFINITY; + for (final CoordinatesIterator iter : iterators) { + if (iter.pending != null && iter.pending.getDate().isBefore(date)) { + date = iter.pending.getDate(); + } + } + return date; + } + + /** Write position. + * @param header file header + * @param satId satellite id + * @param coordinate coordinate + * @exception IOException if an I/O error occurs. + */ + private void writePosition(final SP3Header header, final String satId, final SP3Coordinate coordinate) + throws IOException { + + final StringBuilder lineBuilder = new StringBuilder(); + + // position + lineBuilder.append(String.format(Locale.US, "P%3s%14.6f%14.6f%14.6f", + satId, + SP3Utils.POSITION_UNIT.fromSI(coordinate.getPosition().getX()), + SP3Utils.POSITION_UNIT.fromSI(coordinate.getPosition().getY()), + SP3Utils.POSITION_UNIT.fromSI(coordinate.getPosition().getZ()))); + + // clock + lineBuilder.append(String.format(Locale.US, FOURTEEN_SIX_DIGITS_FLOAT, + SP3Utils.CLOCK_UNIT.fromSI(coordinate.getClockCorrection()))); + + // position accuracy + if (coordinate.getPositionAccuracy() == null) { + lineBuilder.append(THREE_BLANKS). + append(THREE_BLANKS). + append(THREE_BLANKS); + } else { + lineBuilder.append(' '); + lineBuilder.append(String.format(Locale.US, TWO_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, header.getPosVelBase(), + coordinate.getPositionAccuracy().getX()))); + lineBuilder.append(' '); + lineBuilder.append(String.format(Locale.US, TWO_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, header.getPosVelBase(), + coordinate.getPositionAccuracy().getY()))); + lineBuilder.append(' '); + lineBuilder.append(String.format(Locale.US, TWO_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, header.getPosVelBase(), + coordinate.getPositionAccuracy().getZ()))); + } + + // clock accuracy + lineBuilder.append(' '); + if (Double.isNaN(coordinate.getClockAccuracy())) { + lineBuilder.append(THREE_BLANKS); + } else { + lineBuilder.append(String.format(Locale.US, THREE_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.CLOCK_ACCURACY_UNIT, header.getClockBase(), + coordinate.getClockAccuracy()))); + } + + // events + lineBuilder.append(' '); + lineBuilder.append(coordinate.hasClockEvent() ? 'E' : ' '); + lineBuilder.append(coordinate.hasClockPrediction() ? 'P' : ' '); + lineBuilder.append(' '); + lineBuilder.append(' '); + lineBuilder.append(coordinate.hasOrbitManeuverEvent() ? 'M' : ' '); + lineBuilder.append(coordinate.hasOrbitPrediction() ? 'P' : ' '); + + output.append(lineBuilder.toString().trim()).append(EOL); + + } + + /** Write velocity. + * @param header file header + * @param satId satellite id + * @param coordinate coordinate + * @exception IOException if an I/O error occurs. + */ + private void writeVelocity(final SP3Header header, final String satId, final SP3Coordinate coordinate) + throws IOException { + + final StringBuilder lineBuilder = new StringBuilder(); + // velocity + lineBuilder.append(String.format(Locale.US, "V%3s%14.6f%14.6f%14.6f", + satId, + SP3Utils.VELOCITY_UNIT.fromSI(coordinate.getVelocity().getX()), + SP3Utils.VELOCITY_UNIT.fromSI(coordinate.getVelocity().getY()), + SP3Utils.VELOCITY_UNIT.fromSI(coordinate.getVelocity().getZ()))); + + // clock rate + lineBuilder.append(String.format(Locale.US, FOURTEEN_SIX_DIGITS_FLOAT, + SP3Utils.CLOCK_RATE_UNIT.fromSI(coordinate.getClockRateChange()))); + + // velocity accuracy + if (coordinate.getVelocityAccuracy() == null) { + lineBuilder.append(THREE_BLANKS). + append(THREE_BLANKS). + append(THREE_BLANKS); + } else { + lineBuilder.append(' '); + lineBuilder.append(String.format(Locale.US, TWO_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, header.getPosVelBase(), + coordinate.getVelocityAccuracy().getX()))); + lineBuilder.append(' '); + lineBuilder.append(String.format(Locale.US, TWO_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, header.getPosVelBase(), + coordinate.getVelocityAccuracy().getY()))); + lineBuilder.append(' '); + lineBuilder.append(String.format(Locale.US, TWO_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, header.getPosVelBase(), + coordinate.getVelocityAccuracy().getZ()))); + } + + // clock rate accuracy + lineBuilder.append(' '); + if (Double.isNaN(coordinate.getClockRateAccuracy())) { + lineBuilder.append(THREE_BLANKS); + } else { + lineBuilder.append(String.format(Locale.US, THREE_DIGITS_INTEGER, + SP3Utils.indexAccuracy(SP3Utils.CLOCK_RATE_ACCURACY_UNIT, header.getClockBase(), + coordinate.getClockRateAccuracy()))); + } + + output.append(lineBuilder.toString().trim()).append(EOL); + + } + + /** Write header. + * @param header SP3 header to write + * @exception IOException if an I/O error occurs. + */ + private void writeHeader(final SP3Header header) + throws IOException { + final TimeScale timeScale = header.getTimeSystem().getTimeScale(timeScales); + final DateTimeComponents dtc = header.getEpoch().getComponents(timeScale); + final StringBuilder dataUsedBuilder = new StringBuilder(); + for (final DataUsed du : header.getDataUsed()) { + if (dataUsedBuilder.length() > 0) { + dataUsedBuilder.append('+'); + } + dataUsedBuilder.append(du.getKey()); + } + final String dataUsed = dataUsedBuilder.length() <= 5 ? + dataUsedBuilder.toString() : + DataUsed.MIXED.getKey(); + + // header first line: version, epoch... + output.append(String.format(Locale.US, "#%c%c%4d %2d %2d %2d %2d %11.8f %7d %5s %5s %3s %4s%n", + header.getVersion(), + header.getFilter() == CartesianDerivativesFilter.USE_P ? 'P' : 'V', + dtc.getDate().getYear(), + dtc.getDate().getMonth(), + dtc.getDate().getDay(), + dtc.getTime().getHour(), + dtc.getTime().getMinute(), + dtc.getTime().getSecond(), + header.getNumberOfEpochs(), + dataUsed, + header.getCoordinateSystem(), + header.getOrbitTypeKey(), + header.getAgency())); + + // header second line : dates + output.append(String.format(Locale.US, "## %4d %15.8f %14.8f %5d %15.13f%n", + header.getGpsWeek(), + header.getSecondsOfWeek(), + header.getEpochInterval(), + header.getModifiedJulianDay(), + header.getDayFraction())); + + // list of satellites + final List satellites = header.getSatIds(); + output.append(String.format(Locale.US, "+ %3d ", satellites.size())); + int lines = 0; + int column = 9; + int remaining = satellites.size(); + for (final String satId : satellites) { + output.append(String.format(Locale.US, "%3s", satId)); + --remaining; + column += 3; + if (column >= 60) { + // finish line + output.append(EOL); + ++lines; + if (remaining > 0) { + // start new line + output.append("+ "); + column = 9; + } + } + } + while (column < 60) { + output.append(' '). + append(' '). + append('0'); + column += 3; + } + output.append(EOL); + ++lines; + while (lines++ < 5) { + // write extra lines to have at least 85 satellites + output.append("+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"). + append(EOL); + } + + // general accuracy + output.append(ACCURACY_LINE_PREFIX); + lines = 0; + column = 9; + remaining = satellites.size(); + for (final String satId : satellites) { + final double accuracy = header.getAccuracy(satId); + final int accuracyExp = SP3Utils.indexAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, SP3Utils.POS_VEL_BASE_ACCURACY, accuracy); + output.append(String.format(Locale.US, THREE_DIGITS_INTEGER, accuracyExp)); + --remaining; + column += 3; + if (column >= 60) { + // finish line + output.append(EOL); + ++lines; + if (remaining > 0) { + // start new line + output.append(ACCURACY_LINE_PREFIX); + column = 9; + } + } + } + while (column < 60) { + output.append(' '). + append(' '). + append('0'); + column += 3; + } + output.append(EOL); + ++lines; + while (lines++ < 5) { + // write extra lines to have at least 85 satellites + output.append("++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"). + append(EOL); + } + + // type + if (header.getVersion() == 'a') { + output.append(TIME_SYSTEM_DEFAULT).append(EOL); + } else { + output.append(String.format(Locale.US, "%%c %1s cc %3s ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc%n", + header.getType().getKey(), + header.getTimeSystem().getKey())); + } + output.append(TIME_SYSTEM_DEFAULT).append(EOL); + + // entries accuracy + output.append(String.format(Locale.US, ACCURACY_BASE_FORMAT, + header.getPosVelBase(), header.getClockBase(), 0.0, 0.0)); + output.append(String.format(Locale.US, ACCURACY_BASE_FORMAT, + 0.0, 0.0, 0.0, 0.0)); + + // additional parameters + output.append(ADDITIONAL_PARAMETERS_LINE).append(EOL); + output.append(ADDITIONAL_PARAMETERS_LINE).append(EOL); + + // comments + int count = 0; + for (final String comment : header.getComments()) { + ++count; + output.append(COMMENT_LINE_PREFIX).append(comment).append(EOL); + } + while (count < 4) { + // add dummy comments to get at least the four comments specified for versions a, b and c + ++count; + output.append(COMMENT_LINE_PREFIX).append(EOL); + } + + } + + /** Iterator for coordinates. */ + private static class CoordinatesIterator { + + /** Satellite ID. */ + private final String id; + + /** Iterator over segments. */ + private Iterator segmentsIterator; + + /** Iterator over coordinates. */ + private Iterator coordinatesIterator; + + /** Pending coordinate. */ + private SP3Coordinate pending; + + /** Simple constructor. + * @param ephemeris underlying ephemeris + */ + CoordinatesIterator(final SP3Ephemeris ephemeris) { + this.id = ephemeris.getId(); + this.segmentsIterator = ephemeris.getSegments().iterator(); + this.coordinatesIterator = null; + advance(); + } + + /** Advance to next coordinates. + */ + private void advance() { + + while (coordinatesIterator == null || !coordinatesIterator.hasNext()) { + // we have exhausted previous segment + if (segmentsIterator != null && segmentsIterator.hasNext()) { + coordinatesIterator = segmentsIterator.next().getCoordinates().iterator(); + } else { + // we have exhausted the ephemeris + segmentsIterator = null; + pending = null; + return; + } + } + + // retrieve the next entry + pending = coordinatesIterator.next(); + + } + + } + +} diff --git a/src/main/java/org/orekit/files/sp3/package-info.java b/src/main/java/org/orekit/files/sp3/package-info.java index 2d62909f07..d781a1c544 100644 --- a/src/main/java/org/orekit/files/sp3/package-info.java +++ b/src/main/java/org/orekit/files/sp3/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/files/stk/STKEphemerisFile.java b/src/main/java/org/orekit/files/stk/STKEphemerisFile.java new file mode 100644 index 0000000000..27ea0dac6f --- /dev/null +++ b/src/main/java/org/orekit/files/stk/STKEphemerisFile.java @@ -0,0 +1,269 @@ +/* Copyright 2002-2023 Andrew Goetz + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.stk; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.general.EphemerisFile; +import org.orekit.files.stk.STKEphemerisFile.STKEphemerisSegment; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * STK ephemeris file. + * + * @author Andrew Goetz + * @since 12.0 + */ +public class STKEphemerisFile implements EphemerisFile { + + /** STK version. */ + private final String stkVersion; + + /** Unmodifiable mapping with a single key-value pair from satellite id to ephemeris. */ + private final Map satellites; + + /** + * Constructs a {@link STKEphemerisFile} instance. + * @param stkVersion STK version string (example: "stk.v.11.0") + * @param satelliteId satellite id + * @param ephemeris ephemeris + */ + public STKEphemerisFile(final String stkVersion, final String satelliteId, final STKEphemeris ephemeris) { + this.stkVersion = Objects.requireNonNull(stkVersion); + final Map tempMap = new HashMap<>(); + tempMap.put(satelliteId, ephemeris); + this.satellites = Collections.unmodifiableMap(tempMap); + } + + /** + * Returns the STK version string. + * @return STK version string + */ + public String getSTKVersion() { + return stkVersion; + } + + /** + * {@inheritDoc} + *

          + * STK ephemeris files define ephemeris for a single satellite, so the returned + * map will have a single entry. + *

          + */ + @Override + public Map getSatellites() { + return satellites; + } + + /** + * Ephemeris segment from an STK ephemeris file. + */ + public static class STKEphemerisSegment implements EphemerisFile.EphemerisSegment { + + /** Gravitational parameter (m^3/s^2). */ + private final double mu; + + /** Reference frame. */ + private final Frame frame; + + /** Number of samples to use in interpolation. */ + private final int interpolationSamples; + + /** Cartesian derivatives filter. */ + private final CartesianDerivativesFilter cartesianDerivativesFilter; + + /** Time-sorted time/position/velocity data. */ + private final List timeStampedPVCoordinates; + + /** + * Constructs a {@link STKEphemerisSegment} instance. + * @param mu gravitational parameter (m^3/s^2) + * @param frame frame + * @param interpolationSamples number of samples to use in interpolation + * @param cartesianDerivativesFilter Cartesian derivatives filter + * @param timeStampedPVCoordinates time-sorted time/position/velocity data + */ + public STKEphemerisSegment(final double mu, final Frame frame, final int interpolationSamples, + final CartesianDerivativesFilter cartesianDerivativesFilter, + final List timeStampedPVCoordinates) { + this.mu = mu; + this.frame = Objects.requireNonNull(frame); + this.interpolationSamples = interpolationSamples; + this.cartesianDerivativesFilter = Objects.requireNonNull(cartesianDerivativesFilter); + this.timeStampedPVCoordinates = Collections.unmodifiableList(new ArrayList<>(timeStampedPVCoordinates)); + } + + @Override + public double getMu() { + return mu; + } + + @Override + public Frame getFrame() { + return frame; + } + + @Override + public int getInterpolationSamples() { + return interpolationSamples; + } + + @Override + public CartesianDerivativesFilter getAvailableDerivatives() { + return cartesianDerivativesFilter; + } + + @Override + public List getCoordinates() { + return timeStampedPVCoordinates; + } + + @Override + public AbsoluteDate getStart() { + return timeStampedPVCoordinates.get(0).getDate(); + } + + @Override + public AbsoluteDate getStop() { + return timeStampedPVCoordinates.get(timeStampedPVCoordinates.size() - 1).getDate(); + } + + } + + /** + * Ephemeris from an STK ephemeris file. + */ + public static class STKEphemeris implements SatelliteEphemeris { + + /** Satellite id.*/ + private final String satelliteId; + + /** Gravitational parameter (m^3/s^2). */ + private final double mu; + + /** Unmodifiable list of ephemeris segments. */ + private final List segments; + + /** + * Constructs a {@link STKEphemeris} instance. This constructor shallowly copies the list of segments provided. + * @param satelliteId satellite id + * @param mu gravitational parameter (m^3/s^2) + * @param segments ephemeris segments + */ + public STKEphemeris(final String satelliteId, final double mu, final List segments) { + this.satelliteId = Objects.requireNonNull(satelliteId); + this.mu = mu; + this.segments = Collections.unmodifiableList(new ArrayList<>(segments)); + } + + @Override + public String getId() { + return satelliteId; + } + + @Override + public double getMu() { + return mu; + } + + @Override + public List getSegments() { + return segments; + } + + @Override + public AbsoluteDate getStart() { + return segments.get(0).getStart(); + } + + @Override + public AbsoluteDate getStop() { + return segments.get(segments.size() - 1).getStop(); + } + + } + + /** + * STK coordinate system. + *

          + * Currently, only Earth-centered coordinate systems are supported. + *

          + */ + public enum STKCoordinateSystem { + + /** International Celestial Reference Frame. */ + ICRF, + + /** Mean equator and mean equinox of the J2000 epoch. */ + J2000, + + /** Central-body-dependent inertial frame, equivalent to ICRF for Earth. */ + INERTIAL, + + /** Fixed frame. */ + FIXED, + + /** True equator and true equinox of date. */ + TRUE_OF_DATE, + + /** Mean equator and mean equinox of date. */ + MEAN_OF_DATE, + + /** True equator and mean equinox of date. */ + TEME_OF_DATE; + + /** + * Parses a coordinate system from a string. + * @param s string + * @return coordinate system + */ + public static STKCoordinateSystem parse(final String s) { + final String sUpper = s.toUpperCase(Locale.US); + switch (sUpper) { + case "ICRF": + return ICRF; + case "J2000": + return J2000; + case "INERTIAL": + return INERTIAL; + case "FIXED": + return FIXED; + case "TRUEOFDATE": + return TRUE_OF_DATE; + case "MEANOFDATE": + return MEAN_OF_DATE; + case "TEMEOFDATE": + return TEME_OF_DATE; + default: + throw new OrekitException(OrekitMessages.STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM, s); + } + } + + } + +} diff --git a/src/main/java/org/orekit/files/stk/STKEphemerisFileParser.java b/src/main/java/org/orekit/files/stk/STKEphemerisFileParser.java new file mode 100644 index 0000000000..a6696074ff --- /dev/null +++ b/src/main/java/org/orekit/files/stk/STKEphemerisFileParser.java @@ -0,0 +1,770 @@ +/* Copyright 2002-2023 Andrew Goetz + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.files.stk; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.general.EphemerisFileParser; +import org.orekit.files.stk.STKEphemerisFile.STKCoordinateSystem; +import org.orekit.files.stk.STKEphemerisFile.STKEphemeris; +import org.orekit.files.stk.STKEphemerisFile.STKEphemerisSegment; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.Month; +import org.orekit.time.UTCScale; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * Parser of {@link STKEphemerisFile}s. + * + *

          The STK ephemeris file format specification is quite extensive and this implementation does not + * attempt (nor is it possible, given the lack of an STK scenario to provide context) to support all + * possible variations of the format. The following keywords are recognized (case-insensitive): + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
          Recognized Keywords
          KeywordSupportedComment
          stk.v.*.*YesSTK version number
          BEGIN/END EphemerisYes
          ScenarioEpochYesGregorian UTC time format (dd mmm yyyy hh:mm:ss.s) assumed; + * the TimeFormat keyword is not recognized.
          CentralBodyNoClass constructors require gravitational parameter.
          CoordinateSystemYesImplementation uses a frame mapping to map {@link STKCoordinateSystem}s to {@link Frame}s.
          DistanceUnitYesOnly Meters and Kilometers are supported.
          InterpolationMethodNoThe Orekit EphemerisSegmentPropagator class uses + * {@link org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator#interpolate(AbsoluteDate, Stream)} + * to do Hermite interpolation, so the value of InterpolationMethod, if present, is + * ignored.
          InterpolationSamplesM1YesNote that the InterpolationMethod keyword is ignored, but the value of + * InterpolationSamplesM1 will be used to determine the number of sample points in the + * Hermite interpolator used by Orekit.
          NumberOfEphemerisPointsYes
          BEGIN/END SegmentBoundaryTimesYes
          + * + *

          Any keyword in the format specification which is not explicitly named in the above table is not recognized and + * will cause a parse exception. Those keywords that are listed above as recognized but not supported are simply + * ignored. + * + *

          The following ephemeris formats are recognized and supported: + *

            + *
          • EphemerisTimePos
          • + *
          • EphemerisTimePosVel
          • + *
          • EphemerisTimePosVelAcc
          • + *
          + * Any ephemeris format in the format specification which is not explicitly named in the above list is not recognized + * and will cause an exception. + * + * @author Andrew Goetz + * @since 12.0 + */ +public class STKEphemerisFileParser implements EphemerisFileParser { + + /** Pattern for delimiting regular expressions. */ + private static final Pattern SEPARATOR = Pattern.compile("\\s+"); + + /** Pattern for ignorable lines. Comments are preceded by '#'. */ + private static final Pattern IGNORABLE_LINE = Pattern.compile("^\\s*(#.*)?"); + + /** Regular expression that matches anything. */ + private static final String MATCH_ANY_REGEX = ".*"; + + /** Recognized keywords. */ + private static final List KEYWORDS = Arrays.asList( + LineParser.NUMBER_OF_EPHEMERIS_POINTS, + LineParser.SCENARIO_EPOCH, + LineParser.INTERPOLATION_METHOD, + LineParser.INTERPOLATION_SAMPLESM1, + LineParser.CENTRAL_BODY, + LineParser.COORDINATE_SYSTEM, + LineParser.BEGIN_SEGMENT_BOUNDARY_TIMES, + LineParser.EPHEMERIS_TIME_POS, + LineParser.EPHEMERIS_TIME_POS_VEL, + LineParser.EPHEMERIS_TIME_POS_VEL_ACC + ); + + /** Satellite id. */ + private final String satelliteId; + + /** Gravitational parameter (m^3/s^2). */ + private final double mu; + + /** UTC time scale. */ + private final UTCScale utc; + + /** Mapping of STK coordinate system to Orekit reference frame. */ + private final Map frameMapping; + + /** + * Constructs a {@link STKEphemerisFileParser} instance. + * @param satelliteId satellite id for satellites parsed by the parser + * @param mu gravitational parameter (m^3/s^2) + * @param utc UTC scale for parsed dates + * @param frameMapping mapping from STK coordinate system to Orekit frame + */ + public STKEphemerisFileParser(final String satelliteId, final double mu, final UTCScale utc, + final Map frameMapping) { + this.satelliteId = Objects.requireNonNull(satelliteId); + this.mu = mu; + this.utc = Objects.requireNonNull(utc); + this.frameMapping = Collections.unmodifiableMap(new EnumMap<>(frameMapping)); + } + + @Override + public STKEphemerisFile parse(final DataSource source) { + + try (Reader reader = source.getOpener().openReaderOnce(); + BufferedReader br = (reader == null) ? null : new BufferedReader(reader)) { + + if (br == null) { + throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, source.getName()); + } + + // initialize internal data structures + final ParseInfo pi = new ParseInfo(); + + int lineNumber = 0; + Iterable parsers = Collections.singleton(LineParser.VERSION); + nextLine: + for (String line = br.readLine(); line != null; line = br.readLine()) { + ++lineNumber; + if (pi.file != null) { + break; + } else if (IGNORABLE_LINE.matcher(line).matches()) { + continue; + } + for (final LineParser candidate : parsers) { + if (candidate.canHandle(line)) { + try { + candidate.parse(line, pi); + parsers = candidate.allowedNext(); + continue nextLine; + } catch (StringIndexOutOfBoundsException | IllegalArgumentException e) { + throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, + source.getName(), line); + } + } + } + + // no parsers found for this line + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, source.getName(), + line); + + } + + if (pi.file != null) { + return pi.file; + } else { + throw new OrekitException(OrekitMessages.STK_UNEXPECTED_END_OF_FILE, lineNumber); + } + + } catch (IOException ioe) { + throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage()); + } + } + + /** + * Transient data used for parsing an STK ephemeris file. The data is kept in a + * separate data structure to make the parser thread-safe. + *

          + * Note: The class intentionally does not provide accessor methods, as it + * is only used internally for parsing an STK ephemeris file. + *

          + */ + private final class ParseInfo { + + /** STK version. */ + private String stkVersion; + + /** Scenario epoch. */ + private AbsoluteDate scenarioEpoch; // technically optional but required here b/c no STK scenario for context + + /** Number of ephemeris points. */ + private Integer numberOfEphemerisPoints; + + /** One less than the number of points used in the interpolation. */ + private int interpolationSamplesM1; + + /** Cartesian derivatives filter for interpolation. */ + private CartesianDerivativesFilter cartesianDerivativesFilter; + + /** Coordinate system. */ + private STKCoordinateSystem coordinateSystem; + + /** Distance unit. */ + private STKDistanceUnit distanceUnit; + + /** Number of ephemeris points read. */ + private int numberOfEphemerisPointsRead; + + /** Segment boundary times. */ + private SortedSet segmentBoundaryTimes; + + /** Ephemeris segments. */ + private List ephemerisSegments; + + /** Last-saved ephemeris. */ + private TimeStampedPVCoordinates lastSavedEphemeris; + + /** Ephemeris for current segment. */ + private List segmentEphemeris; + + /** Completely parsed ephemeris file. */ + private STKEphemerisFile file; + + /** + * Constructs a {@link ParseInfo} instance. + */ + private ParseInfo() { + // Set defaults. + this.distanceUnit = STKDistanceUnit.METERS; + this.interpolationSamplesM1 = 5; + this.coordinateSystem = STKCoordinateSystem.FIXED; + + // Other initialization. + this.ephemerisSegments = new ArrayList<>(); + this.segmentBoundaryTimes = new TreeSet<>(); + this.segmentEphemeris = new ArrayList<>(); + } + + /** + * Returns the UTC scale. + * @return UTC scale + */ + private UTCScale getUTCScale() { + return utc; + } + + /** + * Adds an ephemeris point. + * @param time time + * @param pvCoordinates position/velocity coordinates + */ + private void addEphemeris(final double time, final PVCoordinates pvCoordinates) { + if (numberOfEphemerisPoints != null && numberOfEphemerisPointsRead == numberOfEphemerisPoints) { + return; + } + final AbsoluteDate date = scenarioEpoch.shiftedBy(time); + final TimeStampedPVCoordinates timeStampedPVCoordinates = new TimeStampedPVCoordinates(date, pvCoordinates); + if (segmentBoundaryTimes.contains(time) && numberOfEphemerisPointsRead > 0) { + if (segmentEphemeris.isEmpty()) { // begin new segment + if (!date.equals(lastSavedEphemeris.getDate())) { + segmentEphemeris.add(lastSavedEphemeris); // no gaps allowed + } + segmentEphemeris.add(timeStampedPVCoordinates); + } else { // end segment + segmentEphemeris.add(timeStampedPVCoordinates); + ephemerisSegments.add(new STKEphemerisSegment(mu, getFrame(), 1 + interpolationSamplesM1, + cartesianDerivativesFilter, segmentEphemeris)); + segmentEphemeris = new ArrayList<>(); + } + } else { + segmentEphemeris.add(timeStampedPVCoordinates); + } + lastSavedEphemeris = timeStampedPVCoordinates; + ++numberOfEphemerisPointsRead; + } + + /** + * Returns the frame. + * @return frame + */ + private Frame getFrame() { + final STKCoordinateSystem stkCoordinateSystem = coordinateSystem == null ? STKCoordinateSystem.FIXED : + coordinateSystem; + final Frame frame = frameMapping.get(stkCoordinateSystem); + if (frame == null) { + throw new OrekitException(OrekitMessages.STK_UNMAPPED_COORDINATE_SYSTEM, stkCoordinateSystem); + } + return frame; + } + + /** + * Completes parsing. + */ + private void complete() { + if (!segmentEphemeris.isEmpty()) { + ephemerisSegments.add(new STKEphemerisSegment(mu, getFrame(), 1 + interpolationSamplesM1, + cartesianDerivativesFilter, segmentEphemeris)); + } + final STKEphemeris ephemeris = new STKEphemeris(satelliteId, mu, ephemerisSegments); + file = new STKEphemerisFile(stkVersion, satelliteId, ephemeris); + } + + } + + /** Parser for specific line. */ + private enum LineParser { + + /** STK version. */ + VERSION("^stk\\.v\\.\\d+\\.\\d+$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.stkVersion = line; + } + + @Override + public Iterable allowedNext() { + return Collections.singleton(BEGIN_EPHEMERIS); + } + + }, + + /** BEGIN Ephemeris keyword. */ + BEGIN_EPHEMERIS("^\\s*BEGIN Ephemeris\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + // nothing to do + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** NumberOfEphemerisPoints keyword. */ + NUMBER_OF_EPHEMERIS_POINTS("^\\s*NumberOfEphemerisPoints\\s*\\d+\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.numberOfEphemerisPoints = Integer.parseInt(SEPARATOR.split(line.trim())[1]); + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** ScenarioEpoch keyword. */ + SCENARIO_EPOCH("^\\s*ScenarioEpoch\\s* \\d{2} [a-zA-Z]{3} \\d{4} \\d{2}:\\d{2}:\\d{2}(\\.\\d*)?\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + final String[] tokens = SEPARATOR.split(line.trim()); + final int dayOfMonth = Integer.parseInt(tokens[1]); + final Month month = Month.parseMonth(tokens[2]); + final int year = Integer.parseInt(tokens[3]); + final int hour = Integer.parseInt(tokens[4].substring(0, 2)); + final int minute = Integer.parseInt(tokens[4].substring(3, 5)); + final double seconds = Double.parseDouble(tokens[4].substring(6)); + final DateTimeComponents dateTimeComponents = new DateTimeComponents(year, month, dayOfMonth, hour, minute, seconds); + pi.scenarioEpoch = new AbsoluteDate(dateTimeComponents, pi.getUTCScale()); + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** InterpolationMethod keyword. */ + INTERPOLATION_METHOD("^\\s*InterpolationMethod\\s+[a-zA-Z]+\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + // do nothing; this keyword is recognized, but ignored and unsupported + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** InterpolationSamplesM1 keyword. */ + INTERPOLATION_SAMPLESM1("^\\s*InterpolationSamplesM1\\s+\\d+\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.interpolationSamplesM1 = Integer.parseInt(SEPARATOR.split(line.trim())[1]); + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** CentralBody keyword. */ + CENTRAL_BODY("^\\s*CentralBody\\s+[a-zA-Z]+\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + // do nothing; this keyword is recognized, but ignored and unsupported; Earth + // assumed + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** CoordinateSystem keyword. */ + COORDINATE_SYSTEM("^\\s*CoordinateSystem\\s+[a-zA-Z0-9]+\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.coordinateSystem = STKCoordinateSystem.parse(SEPARATOR.split(line.trim())[1]); + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** DistanceUnit keyword. */ + DISTANCE_UNIT("^\\s*DistanceUnit\\s+[a-zA-Z0-9]+\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.distanceUnit = STKDistanceUnit.valueOf(SEPARATOR.split(line.trim())[1].toUpperCase()); + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** BEGIN SegmentBoundaryTimes keyword. */ + BEGIN_SEGMENT_BOUNDARY_TIMES("^\\s*BEGIN SegmentBoundaryTimes\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + // nothing to be done + } + + @Override + public Iterable allowedNext() { + return Collections.singleton(SEGMENT_BOUNDARY_TIME); + } + + }, + + /** Segment boundary time. */ + SEGMENT_BOUNDARY_TIME(MATCH_ANY_REGEX) { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.segmentBoundaryTimes.add(Double.parseDouble(SEPARATOR.split(line.trim())[0])); + } + + @Override + public Iterable allowedNext() { + return Arrays.asList(END_SEGMENT_BOUNDARY_TIMES, SEGMENT_BOUNDARY_TIME); + } + + }, + + /** END SegmentBoundaryTimes keyword. */ + END_SEGMENT_BOUNDARY_TIMES("^\\s*END SegmentBoundaryTimes\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + // nothing to be done + } + + @Override + public Iterable allowedNext() { + return KEYWORDS; + } + + }, + + /** EphemerisTimePos keyword. */ + EPHEMERIS_TIME_POS("^\\s*EphemerisTimePos\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.cartesianDerivativesFilter = CartesianDerivativesFilter.USE_P; + } + + @Override + public Iterable allowedNext() { + return Collections.singleton(EPHEMERIS_TIME_POS_DATUM); + } + + }, + + /** EphemerisTimePos datum. */ + EPHEMERIS_TIME_POS_DATUM(MATCH_ANY_REGEX) { + + @Override + public void parse(final String line, final ParseInfo pi) { + final String[] tokens = SEPARATOR.split(line.trim()); + final double time = Double.parseDouble(tokens[0]); + final double px = Double.parseDouble(tokens[1]) * pi.distanceUnit.conversionToMetersFactor; + final double py = Double.parseDouble(tokens[2]) * pi.distanceUnit.conversionToMetersFactor; + final double pz = Double.parseDouble(tokens[3]) * pi.distanceUnit.conversionToMetersFactor; + + final Vector3D position = new Vector3D(px, py, pz); + final Vector3D velocity = Vector3D.ZERO; + + pi.addEphemeris(time, new PVCoordinates(position, velocity)); + } + + @Override + public Iterable allowedNext() { + return Arrays.asList(END_EPHEMERIS, EPHEMERIS_TIME_POS_DATUM); + } + + }, + + /** EphemerisTimePosVel keyword. */ + EPHEMERIS_TIME_POS_VEL("^\\s*EphemerisTimePosVel\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.cartesianDerivativesFilter = CartesianDerivativesFilter.USE_PV; + } + + @Override + public Iterable allowedNext() { + return Collections.singleton(EPHEMERIS_TIME_POS_VEL_DATUM); + } + + }, + + /** EphemerisTimePosVel datum. */ + EPHEMERIS_TIME_POS_VEL_DATUM(MATCH_ANY_REGEX) { + + @Override + public void parse(final String line, final ParseInfo pi) { + final String[] tokens = SEPARATOR.split(line.trim()); + final double time = Double.parseDouble(tokens[0]); + final double px = Double.parseDouble(tokens[1]) * pi.distanceUnit.conversionToMetersFactor; + final double py = Double.parseDouble(tokens[2]) * pi.distanceUnit.conversionToMetersFactor; + final double pz = Double.parseDouble(tokens[3]) * pi.distanceUnit.conversionToMetersFactor; + final double vx = Double.parseDouble(tokens[4]) * pi.distanceUnit.conversionToMetersFactor; + final double vy = Double.parseDouble(tokens[5]) * pi.distanceUnit.conversionToMetersFactor; + final double vz = Double.parseDouble(tokens[6]) * pi.distanceUnit.conversionToMetersFactor; + + final Vector3D position = new Vector3D(px, py, pz); + final Vector3D velocity = new Vector3D(vx, vy, vz); + + pi.addEphemeris(time, new PVCoordinates(position, velocity)); + } + + @Override + public Iterable allowedNext() { + return Arrays.asList(END_EPHEMERIS, EPHEMERIS_TIME_POS_VEL_DATUM); + } + + }, + + /** EphemerisTimePosVelAcc keyword. */ + EPHEMERIS_TIME_POS_VEL_ACC("^\\s*EphemerisTimePosVelAcc\\s*(#.*)?$") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.cartesianDerivativesFilter = CartesianDerivativesFilter.USE_PVA; + } + + @Override + public Iterable allowedNext() { + return Collections.singleton(EPHEMERIS_TIME_POS_VEL_ACC_DATUM); + } + + }, + + /** EphemerisTimePosVelAcc datum. */ + EPHEMERIS_TIME_POS_VEL_ACC_DATUM(MATCH_ANY_REGEX) { + + @Override + public void parse(final String line, final ParseInfo pi) { + final String[] tokens = SEPARATOR.split(line.trim()); + final double time = Double.parseDouble(tokens[0]); + final double px = Double.parseDouble(tokens[1]) * pi.distanceUnit.conversionToMetersFactor; + final double py = Double.parseDouble(tokens[2]) * pi.distanceUnit.conversionToMetersFactor; + final double pz = Double.parseDouble(tokens[3]) * pi.distanceUnit.conversionToMetersFactor; + final double vx = Double.parseDouble(tokens[4]) * pi.distanceUnit.conversionToMetersFactor; + final double vy = Double.parseDouble(tokens[5]) * pi.distanceUnit.conversionToMetersFactor; + final double vz = Double.parseDouble(tokens[6]) * pi.distanceUnit.conversionToMetersFactor; + final double ax = Double.parseDouble(tokens[7]) * pi.distanceUnit.conversionToMetersFactor; + final double ay = Double.parseDouble(tokens[8]) * pi.distanceUnit.conversionToMetersFactor; + final double az = Double.parseDouble(tokens[9]) * pi.distanceUnit.conversionToMetersFactor; + + final Vector3D position = new Vector3D(px, py, pz); + final Vector3D velocity = new Vector3D(vx, vy, vz); + final Vector3D acceleration = new Vector3D(ax, ay, az); + + pi.addEphemeris(time, new PVCoordinates(position, velocity, acceleration)); + } + + @Override + public Iterable allowedNext() { + return Arrays.asList(END_EPHEMERIS, EPHEMERIS_TIME_POS_VEL_ACC_DATUM); + } + + }, + + /** END Ephemeris keyword. */ + END_EPHEMERIS("\\s*END Ephemeris\\s*(#.*)?") { + + @Override + public void parse(final String line, final ParseInfo pi) { + pi.complete(); + } + + @Override + public Iterable allowedNext() { + return Collections.emptyList(); + } + + }; + + /** Pattern for identifying line. */ + private final Pattern pattern; + + /** + * Constructs a {@link LineParser} instance. + * @param regex regular expression for identifying line + */ + LineParser(final String regex) { + pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + } + + /** + * Parses a line. + * @param line line to parse + * @param pi holder for transient data + */ + public abstract void parse(String line, ParseInfo pi); + + /** + * Returns the allowed parsers for the next line. + * @return returns the allowed parsers for the next line + */ + public abstract Iterable allowedNext(); + + /** + * Checks if a parser can handle line. + * @param line line to parse + * @return true if parser can handle the specified line + */ + public boolean canHandle(final String line) { + return pattern.matcher(line).matches(); + } + + } + + /** STK distance unit. */ + private enum STKDistanceUnit { + + /** Kilometers. */ + KILOMETERS(1000.0), + + /** Meters. */ + METERS(1.0); + + /** Factor by which to multiply to convert the distance unit to meters. */ + private final double conversionToMetersFactor; + + /** + * Constructs a {@link STKDistanceUnit} instance. + * @param conversionToMetersFactor factor by which to multiply to + * convert the distance unit to meters + */ + STKDistanceUnit(final double conversionToMetersFactor) { + this.conversionToMetersFactor = conversionToMetersFactor; + } + + } + +} diff --git a/src/main/java/org/orekit/files/stk/package-info.java b/src/main/java/org/orekit/files/stk/package-info.java new file mode 100644 index 0000000000..0d30dca4fd --- /dev/null +++ b/src/main/java/org/orekit/files/stk/package-info.java @@ -0,0 +1,23 @@ +/* Copyright 2002-2023 Andrew Goetz + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides a parser for STK ephemeris files. + * + * @author Andrew Goetz + * @since 12.0 + */ +package org.orekit.files.stk; diff --git a/src/main/java/org/orekit/forces/AbstractForceModel.java b/src/main/java/org/orekit/forces/AbstractForceModel.java deleted file mode 100644 index 9b477130ca..0000000000 --- a/src/main/java/org/orekit/forces/AbstractForceModel.java +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.forces; - - -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.utils.ParameterDriver; - -/** Base class for force models. - * @author Luc Maisonobe - * @since 8.0 - */ -public abstract class AbstractForceModel implements ForceModel { - - /** {@inheritDoc} */ - public ParameterDriver getParameterDriver(final String name) { - - for (final ParameterDriver driver : getParametersDrivers()) { - if (name.equals(driver.getName())) { - // we have found a parameter with that name - return driver; - } - } - - throw notSupportedException(name); - - } - - /** {@inheritDoc} */ - @Override - public boolean isSupported(final String name) { - for (final ParameterDriver driver : getParametersDrivers()) { - if (name.equals(driver.getName())) { - // we have found a parameter with that name - return true; - } - } - // the parameter is not supported - return false; - } - - /** Complain if a parameter is not supported. - * @param name name of the parameter - */ - protected void complainIfNotSupported(final String name) { - if (!isSupported(name)) { - throw notSupportedException(name); - } - } - - /** Generate an exception for unsupported parameter. - * @param name unsupported parameter name - * @return exception with appropriate message - */ - private OrekitException notSupportedException(final String name) { - - final StringBuilder builder = new StringBuilder(); - for (final ParameterDriver driver : getParametersDrivers()) { - if (builder.length() > 0) { - builder.append(", "); - } - builder.append(driver.getName()); - } - if (builder.length() == 0) { - builder.append(""); - } - - return new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, - name, builder.toString()); - - } - -} diff --git a/src/main/java/org/orekit/forces/BoxAndSolarArraySpacecraft.java b/src/main/java/org/orekit/forces/BoxAndSolarArraySpacecraft.java index a7bcd5c9a2..ce62d7a73a 100644 --- a/src/main/java/org/orekit/forces/BoxAndSolarArraySpacecraft.java +++ b/src/main/java/org/orekit/forces/BoxAndSolarArraySpacecraft.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,41 +17,32 @@ package org.orekit.forces; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; -import org.hipparchus.util.SinCos; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; import org.orekit.forces.drag.DragSensitive; import org.orekit.forces.radiation.RadiationSensitive; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.ExtendedPVCoordinatesProvider; import org.orekit.utils.ParameterDriver; -/** Class representing the features of a classical satellite - * with a convex body shape and rotating flat solar arrays. +/** Class representing the features of a classical satellite with a convex body shape. *

          * The body can be either a simple parallelepipedic box aligned with - * spacecraft axes or a set of facets defined by their area and normal vector. - * This should handle accurately most spacecraft shapes. - *

          - *

          - * The solar array rotation with respect to satellite body can be either - * the best lighting orientation (i.e. Sun exactly in solar array meridian - * plane defined by solar array rotation axis and solar array normal vector) - * or a rotation evolving linearly according to a start position and an - * angular rate (which can be set to 0 for non-rotating panels, which may - * occur in special modes or during contingencies). + * spacecraft axes or a set of panels defined by their area and normal vector. + * Some panels may be moving to model solar arrays (or antennas that could + * point anywhere). This should handle accurately most spacecraft shapes. This model + * does not take cast shadows into account. *

          *

          * The lift component of the drag force can be optionally considered. It should @@ -62,14 +53,22 @@ * Without lift (i.e. when the lift ratio is set to 0), drag force is along * atmosphere relative velocity. With lift (i.e. when the lift ratio is set to any * value between 0 and 1), the drag force depends on both relative velocity direction - * and facets normal orientation. For a single panel, if the relative velocity is + * and panels normal orientation. For a single panel, if the relative velocity is * head-on (i.e. aligned with the panel normal), the force will be in the same * direction with and without lift, but the magnitude with lift ratio set to 1.0 will * be twice the magnitude with lift ratio set to 0.0 (because atmosphere molecules * bounces backward at same velocity in case of specular reflection). *

          *

          - * This model does not take cast shadow between body and solar array into account. + * Each {@link Panel panel} has its own set of radiation and drag coefficients. In + * orbit determination context, it would not be possible to estimate each panel + * individually, therefore {@link #getDragParametersDrivers()} returns a single + * {@link ParameterDriver parameter driver} representing a {@link DragSensitive#GLOBAL_DRAG_FACTOR + * global drag multiplicative factor} that applies to all panels drag coefficients + * and the {@link #getRadiationParametersDrivers()} returns a single + * {@link ParameterDriver parameter driver} representing a + * {@link RadiationSensitive#GLOBAL_RADIATION_FACTOR global radiation multiplicative factor} + * that applies to all panels radiation coefficients. *

          * * @author Luc Maisonobe @@ -85,287 +84,49 @@ public class BoxAndSolarArraySpacecraft implements RadiationSensitive, DragSensi */ private final double SCALE = FastMath.scalb(1.0, -3); - /** Driver for drag coefficient parameter. */ - private final ParameterDriver dragParameterDriver; - - /** Driver for lift ratio parameter (may be null is lift is ignored). */ - private final ParameterDriver liftParameterDriver; - - /** Driver for radiation pressure absorption coefficient parameter. */ - private final ParameterDriver absorptionParameterDriver; - - /** Driver for radiation pressure reflection coefficient parameter. */ - private final ParameterDriver reflectionParameterDriver; - - /** Surface vectors for body facets. */ - private final List facets; - - /** Solar array area (m²). */ - private final double solarArrayArea; - - /** Reference date for linear rotation angle (may be null). */ - private final AbsoluteDate referenceDate; - - /** Rotation rate for linear rotation angle. */ - private final double rotationRate; - - /** Solar array reference axis in spacecraft frame (may be null). */ - private final Vector3D saX; + /** Driver for drag multiplicative factor parameter. */ + private final ParameterDriver dragFactorParameterDriver; - /** Solar array third axis in spacecraft frame (may be null). */ - private final Vector3D saY; + /** Driver for radiation pressure multiplicative factor parameter. */ + private final ParameterDriver radiationFactorParameterDriver; - /** Solar array rotation axis in spacecraft frame. */ - private final Vector3D saZ; + /** Panels composing the spacecraft. */ + private final List panels; - /** Sun model. */ - private final PVCoordinatesProvider sun; - - /** Build a spacecraft model with best lighting of solar array. - *

          - * This constructor builds an instance that completely ignores lift - * in atmospheric drag (the value of lift coefficient is set to zero, - * and there are no {@link ParameterDriver drivers} to change it). - *

          - *

          - * Solar arrays orientation will be such that at each time the Sun direction - * will always be in the solar array meridian plane defined by solar array - * rotation axis and solar array normal vector. - *

          - * @param xLength length of the body along its X axis (m) - * @param yLength length of the body along its Y axis (m) - * @param zLength length of the body along its Z axis (m) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param dragCoeff drag coefficient (used only for drag) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) + /** Build a spacecraft model. + * @param panels panels composing the body, solar arrays and antennas + * (only the panels with strictly positive area will be stored) + * @since 12.0 */ - public BoxAndSolarArraySpacecraft(final double xLength, final double yLength, - final double zLength, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final double dragCoeff, - final double absorptionCoeff, - final double reflectionCoeff) { - this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis, - dragCoeff, 0.0, false, - absorptionCoeff, reflectionCoeff); - } + public BoxAndSolarArraySpacecraft(final List panels) { - /** Build a spacecraft model with best lighting of solar array. - *

          - * Solar arrays orientation will be such that at each time the Sun direction - * will always be in the solar array meridian plane defined by solar array - * rotation axis and solar array normal vector. - *

          - * @param xLength length of the body along its X axis (m) - * @param yLength length of the body along its Y axis (m) - * @param zLength length of the body along its Z axis (m) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param dragCoeff drag coefficient (used only for drag) - * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules - * that will experience specular reflection when hitting spacecraft instead - * of experiencing diffuse reflection, hence producing lift) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @since 9.0 - */ - public BoxAndSolarArraySpacecraft(final double xLength, final double yLength, - final double zLength, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final double dragCoeff, final double liftRatio, - final double absorptionCoeff, - final double reflectionCoeff) { - this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis, - dragCoeff, liftRatio, - absorptionCoeff, reflectionCoeff); - } + try { + dragFactorParameterDriver = new ParameterDriver(DragSensitive.GLOBAL_DRAG_FACTOR, + 1.0, SCALE, 0.0, Double.POSITIVE_INFINITY); + radiationFactorParameterDriver = new ParameterDriver(RadiationSensitive.GLOBAL_RADIATION_FACTOR, + 1.0, SCALE, 0.0, Double.POSITIVE_INFINITY); + } catch (OrekitException oe) { + // this should never happen + throw new OrekitInternalError(oe); + } - /** Build a spacecraft model with best lighting of solar array. - *

          - * The spacecraft body is described by an array of surface vectors. Each facet of - * the body is described by a vector normal to the facet (pointing outward of the spacecraft) - * and whose norm is the surface area in m². - *

          - *

          - * Solar arrays orientation will be such that at each time the Sun direction - * will always be in the solar array meridian plane defined by solar array - * rotation axis and solar array normal vector. - *

          - * @param facets body facets (only the facets with strictly positive area will be stored) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param dragCoeff drag coefficient (used only for drag) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - */ - public BoxAndSolarArraySpacecraft(final Facet[] facets, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final double dragCoeff, - final double absorptionCoeff, - final double reflectionCoeff) { - this(facets, sun, solarArrayArea, solarArrayAxis, - dragCoeff, 0.0, false, - absorptionCoeff, reflectionCoeff); - } + // remove spurious panels + this.panels = panels.stream().filter(p -> p.getArea() > 0).collect(Collectors.toList()); - /** Build a spacecraft model with best lighting of solar array. - *

          - * The spacecraft body is described by an array of surface vectors. Each facet of - * the body is described by a vector normal to the facet (pointing outward of the spacecraft) - * and whose norm is the surface area in m². - *

          - *

          - * Solar arrays orientation will be such that at each time the Sun direction - * will always be in the solar array meridian plane defined by solar array - * rotation axis and solar array normal vector. - *

          - * @param facets body facets (only the facets with strictly positive area will be stored) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param dragCoeff drag coefficient (used only for drag) - * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules - * that will experience specular reflection when hitting spacecraft instead - * of experiencing diffuse reflection, hence producing lift) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @since 9.0 - */ - public BoxAndSolarArraySpacecraft(final Facet[] facets, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final double dragCoeff, final double liftRatio, - final double absorptionCoeff, - final double reflectionCoeff) { - this(facets, sun, solarArrayArea, solarArrayAxis, - dragCoeff, liftRatio, true, - absorptionCoeff, reflectionCoeff); } /** Build a spacecraft model with best lighting of solar array. - *

          - * The spacecraft body is described by an array of surface vectors. Each facet of - * the body is described by a vector normal to the facet (pointing outward of the spacecraft) - * and whose norm is the surface area in m². - *

          *

          * Solar arrays orientation will be such that at each time the Sun direction * will always be in the solar array meridian plane defined by solar array * rotation axis and solar array normal vector. *

          - * @param facets body facets (only the facets with strictly positive area will be stored) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param dragCoeff drag coefficient (used only for drag) - * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules - * that will experience specular reflection when hitting spacecraft instead - * of experiencing diffuse reflection, hence producing lift) - * @param useLift if true, lift should be used, otherwise it is completely ignored - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @since 9.0 - */ - private BoxAndSolarArraySpacecraft(final Facet[] facets, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final double dragCoeff, final double liftRatio, final boolean useLift, - final double absorptionCoeff, - final double reflectionCoeff) { - - // drag - dragParameterDriver = buildDragParameterDriver(dragCoeff); - liftParameterDriver = useLift ? buildLiftParameterDriver(liftRatio) : null; - - // radiation pressure - absorptionParameterDriver = buildAbsorptionParameterDriver(absorptionCoeff); - reflectionParameterDriver = buildReflectionParameterDriver(reflectionCoeff); - - this.facets = filter(facets); - - this.sun = sun; - this.solarArrayArea = solarArrayArea; - this.referenceDate = null; - this.rotationRate = 0; - - this.saZ = solarArrayAxis.normalize(); - this.saY = null; - this.saX = null; - - } - - /** Build a spacecraft model with linear rotation of solar array. - *

          - * Solar arrays orientation will be a regular rotation from the - * reference orientation at reference date and using a constant - * rotation rate. - *

          - * @param xLength length of the body along its X axis (m) - * @param yLength length of the body along its Y axis (m) - * @param zLength length of the body along its Z axis (m) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param referenceDate reference date for the solar array rotation - * @param referenceNormal direction of the solar array normal at reference date - * in spacecraft frame - * @param rotationRate rotation rate of the solar array, may be 0 (rad/s) - * @param dragCoeff drag coefficient (used only for drag) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - */ - public BoxAndSolarArraySpacecraft(final double xLength, final double yLength, - final double zLength, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final AbsoluteDate referenceDate, - final Vector3D referenceNormal, - final double rotationRate, - final double dragCoeff, - final double absorptionCoeff, - final double reflectionCoeff) { - this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis, - referenceDate, referenceNormal, rotationRate, - dragCoeff, 0.0, false, - absorptionCoeff, reflectionCoeff); - } - - /** Build a spacecraft model with linear rotation of solar array. - *

          - * Solar arrays orientation will be a regular rotation from the - * reference orientation at reference date and using a constant - * rotation rate. - *

          * @param xLength length of the body along its X axis (m) * @param yLength length of the body along its Y axis (m) * @param zLength length of the body along its Z axis (m) * @param sun sun model * @param solarArrayArea area of the solar array (m²) * @param solarArrayAxis solar array rotation axis in satellite frame - * @param referenceDate reference date for the solar array rotation - * @param referenceNormal direction of the solar array normal at reference date - * in spacecraft frame - * @param rotationRate rotation rate of the solar array, may be 0 (rad/s) * @param dragCoeff drag coefficient (used only for drag) * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules * that will experience specular reflection when hitting spacecraft instead @@ -374,613 +135,284 @@ public BoxAndSolarArraySpacecraft(final double xLength, final double yLength, * (used only for radiation pressure) * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 * (used only for radiation pressure) - * @since 9.0 + * @since 12.0 */ - public BoxAndSolarArraySpacecraft(final double xLength, final double yLength, - final double zLength, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final AbsoluteDate referenceDate, - final Vector3D referenceNormal, - final double rotationRate, + public BoxAndSolarArraySpacecraft(final double xLength, final double yLength, final double zLength, + final ExtendedPVCoordinatesProvider sun, + final double solarArrayArea, final Vector3D solarArrayAxis, final double dragCoeff, final double liftRatio, - final double absorptionCoeff, - final double reflectionCoeff) { - this(simpleBoxFacets(xLength, yLength, zLength), sun, solarArrayArea, solarArrayAxis, - referenceDate, referenceNormal, rotationRate, - dragCoeff, liftRatio, true, - absorptionCoeff, reflectionCoeff); + final double absorptionCoeff, final double reflectionCoeff) { + this(buildPanels(xLength, yLength, zLength, + sun, solarArrayArea, solarArrayAxis, + dragCoeff, liftRatio, absorptionCoeff, reflectionCoeff)); } - /** Build a spacecraft model with linear rotation of solar array. - *

          - * The spacecraft body is described by an array of surface vectors. Each facet of - * the body is described by a vector normal to the facet (pointing outward of the spacecraft) - * and whose norm is the surface area in m². - *

          - *

          - * Solar arrays orientation will be a regular rotation from the - * reference orientation at reference date and using a constant - * rotation rate. - *

          - * @param facets body facets (only the facets with strictly positive area will be stored) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param referenceDate reference date for the solar array rotation - * @param referenceNormal direction of the solar array normal at reference date - * in spacecraft frame - * @param rotationRate rotation rate of the solar array, may be 0 (rad/s) - * @param dragCoeff drag coefficient (used only for drag) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) + /** Get the panels composing the body. + * @return unmodifiable view of the panels composing the body + * @since 12.0 */ - public BoxAndSolarArraySpacecraft(final Facet[] facets, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final AbsoluteDate referenceDate, - final Vector3D referenceNormal, - final double rotationRate, - final double dragCoeff, - final double absorptionCoeff, - final double reflectionCoeff) { - this(facets, sun, solarArrayArea, solarArrayAxis, referenceDate, referenceNormal, rotationRate, - dragCoeff, 0.0, false, - absorptionCoeff, reflectionCoeff); - } - - /** Build a spacecraft model with linear rotation of solar array. - *

          - * The spacecraft body is described by an array of surface vectors. Each facet of - * the body is described by a vector normal to the facet (pointing outward of the spacecraft) - * and whose norm is the surface area in m². - *

          - *

          - * Solar arrays orientation will be a regular rotation from the - * reference orientation at reference date and using a constant - * rotation rate. - *

          - * @param facets body facets (only the facets with strictly positive area will be stored) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param referenceDate reference date for the solar array rotation - * @param referenceNormal direction of the solar array normal at reference date - * in spacecraft frame - * @param rotationRate rotation rate of the solar array, may be 0 (rad/s) - * @param dragCoeff drag coefficient (used only for drag) - * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules - * that will experience specular reflection when hitting spacecraft instead - * of experiencing diffuse reflection, hence producing lift) - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @since 9.0 - */ - public BoxAndSolarArraySpacecraft(final Facet[] facets, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final AbsoluteDate referenceDate, - final Vector3D referenceNormal, - final double rotationRate, - final double dragCoeff, final double liftRatio, - final double absorptionCoeff, - final double reflectionCoeff) { - this(facets, sun, solarArrayArea, solarArrayAxis, referenceDate, referenceNormal, rotationRate, - dragCoeff, liftRatio, true, - absorptionCoeff, reflectionCoeff); - } - - /** Build a spacecraft model with linear rotation of solar array. - *

          - * The spacecraft body is described by an array of surface vectors. Each facet of - * the body is described by a vector normal to the facet (pointing outward of the spacecraft) - * and whose norm is the surface area in m². - *

          - *

          - * Solar arrays orientation will be a regular rotation from the - * reference orientation at reference date and using a constant - * rotation rate. - *

          - * @param facets body facets (only the facets with strictly positive area will be stored) - * @param sun sun model - * @param solarArrayArea area of the solar array (m²) - * @param solarArrayAxis solar array rotation axis in satellite frame - * @param referenceDate reference date for the solar array rotation - * @param referenceNormal direction of the solar array normal at reference date - * in spacecraft frame - * @param rotationRate rotation rate of the solar array, may be 0 (rad/s) - * @param dragCoeff drag coefficient (used only for drag) - * @param liftRatio lift ratio (proportion between 0 and 1 of atmosphere modecules - * that will experience specular reflection when hitting spacecraft instead - * of experiencing diffuse reflection, hence producing lift) - * @param useLift if true, lift should be used, otherwise it is completely ignored - * @param absorptionCoeff absorption coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @param reflectionCoeff specular reflection coefficient between 0.0 an 1.0 - * (used only for radiation pressure) - * @since 9.0 - */ - private BoxAndSolarArraySpacecraft(final Facet[] facets, - final PVCoordinatesProvider sun, final double solarArrayArea, - final Vector3D solarArrayAxis, - final AbsoluteDate referenceDate, - final Vector3D referenceNormal, - final double rotationRate, - final double dragCoeff, final double liftRatio, final boolean useLift, - final double absorptionCoeff, - final double reflectionCoeff) { - - // drag - dragParameterDriver = buildDragParameterDriver(dragCoeff); - liftParameterDriver = useLift ? buildLiftParameterDriver(liftRatio) : null; - - // radiation pressure - absorptionParameterDriver = buildAbsorptionParameterDriver(absorptionCoeff); - reflectionParameterDriver = buildReflectionParameterDriver(reflectionCoeff); - - this.facets = filter(facets.clone()); - - this.sun = sun; - this.solarArrayArea = solarArrayArea; - this.referenceDate = referenceDate; - this.rotationRate = rotationRate; - - this.saZ = solarArrayAxis.normalize(); - this.saY = Vector3D.crossProduct(saZ, referenceNormal).normalize(); - this.saX = Vector3D.crossProduct(saY, saZ); - - } - - /** Build the parameter driver for drag coefficient. - * @param coeff drag coefficient - * @return parameter driver for drag coefficient - * @since 9.0 - */ - private ParameterDriver buildDragParameterDriver(final double coeff) { - try { - return new ParameterDriver(DragSensitive.DRAG_COEFFICIENT, - coeff, SCALE, 0.0, Double.POSITIVE_INFINITY); - } catch (OrekitException oe) { - // this should never happen - throw new OrekitInternalError(oe); - } - } - - /** Build the parameter driver for lift coefficient. - * @param coeff lift coefficient - * @return parameter driver for lift coefficient - * @since 9.0 - */ - private ParameterDriver buildLiftParameterDriver(final double coeff) { - try { - return new ParameterDriver(DragSensitive.LIFT_RATIO, coeff, SCALE, 0.0, 1.0); - } catch (OrekitException oe) { - // this should never happen - throw new OrekitInternalError(oe); - } - } - - /** Build the parameter driver for absorption coefficient. - * @param coeff absorption coefficient - * @return parameter driver for absorption coefficient - * @since 9.0 - */ - private ParameterDriver buildAbsorptionParameterDriver(final double coeff) { - try { - return new ParameterDriver(RadiationSensitive.ABSORPTION_COEFFICIENT, coeff, SCALE, 0.0, 1.0); - } catch (OrekitException oe) { - // this should never happen - throw new OrekitInternalError(oe); - } - } - - /** Build the parameter driver for reflection coefficient. - * @param coeff absorption coefficient - * @return parameter driver for reflection coefficient - * @since 9.0 - */ - private ParameterDriver buildReflectionParameterDriver(final double coeff) { - try { - return new ParameterDriver(RadiationSensitive.REFLECTION_COEFFICIENT, coeff, SCALE, 0.0, 1.0); - } catch (OrekitException oe) { - // this should never happen - throw new OrekitInternalError(oe); - } + public List getPanels() { + return Collections.unmodifiableList(panels); } /** {@inheritDoc} */ @Override public List getDragParametersDrivers() { - // Initialize list of drag parameter drivers - final List drivers = new ArrayList<>(); - drivers.add(dragParameterDriver); - // Verify if the driver for lift ratio parameter is defined - if (liftParameterDriver != null) { - drivers.add(liftParameterDriver); - } - return drivers; + return Collections.singletonList(dragFactorParameterDriver); } /** {@inheritDoc} */ @Override public List getRadiationParametersDrivers() { - // Initialize list of drag parameter drivers - final List drivers = new ArrayList<>(); - drivers.add(absorptionParameterDriver); - drivers.add(reflectionParameterDriver); - return drivers; - } - - /** Get solar array normal in spacecraft frame. - * @param date current date - * @param frame inertial reference frame for state (both orbit and attitude) - * @param position position of spacecraft in reference frame - * @param rotation orientation (attitude) of the spacecraft with respect to reference frame - * @return solar array normal in spacecraft frame - */ - public synchronized Vector3D getNormal(final AbsoluteDate date, final Frame frame, - final Vector3D position, final Rotation rotation) { - - if (referenceDate != null) { - // use a simple rotation at fixed rate - final double alpha = rotationRate * date.durationFrom(referenceDate); - final SinCos scAlpha = FastMath.sinCos(alpha); - return new Vector3D(scAlpha.cos(), saX, scAlpha.sin(), saY); - } - - // compute orientation for best lighting - final Vector3D sunInert = sun.getPVCoordinates(date, frame).getPosition().subtract(position).normalize(); - final Vector3D sunSpacecraft = rotation.applyTo(sunInert); - final double d = Vector3D.dotProduct(sunSpacecraft, saZ); - final double f = 1 - d * d; - if (f < Precision.EPSILON) { - // extremely rare case: the sun is along solar array rotation axis - // (there will not be much output power ...) - // we set up an arbitrary normal - return saZ.orthogonal(); - } - - final double s = 1.0 / FastMath.sqrt(f); - return new Vector3D(s, sunSpacecraft, -s * d, saZ); - - } - - /** Get solar array normal in spacecraft frame. - * @param date current date - * @param frame inertial reference frame for state (both orbit and attitude) - * @param position position of spacecraft in reference frame - * @param rotation orientation (attitude) of the spacecraft with respect to reference frame - * @return solar array normal in spacecraft frame - * @param type of the field elements - */ - public synchronized > FieldVector3D getNormal(final FieldAbsoluteDate date, - final Frame frame, - final FieldVector3D position, - final FieldRotation rotation) { - - if (referenceDate != null) { - // use a simple rotation at fixed rate - final T alpha = date.durationFrom(referenceDate).multiply(rotationRate); - return new FieldVector3D<>(alpha.cos(), saX, alpha.sin(), saY); - } - - // compute orientation for best lighting - final FieldVector3D sunInert = position.subtract(sun.getPVCoordinates(date.toAbsoluteDate(), frame).getPosition()).negate().normalize(); - final FieldVector3D sunSpacecraft = rotation.applyTo(sunInert); - final T d = FieldVector3D.dotProduct(sunSpacecraft, saZ); - final T f = d.multiply(d).subtract(1).negate(); - if (f.getReal() < Precision.EPSILON) { - // extremely rare case: the sun is along solar array rotation axis - // (there will not be much output power ...) - // we set up an arbitrary normal - return new FieldVector3D<>(f.getField(), saZ.orthogonal()); - } - - final T s = f.sqrt().reciprocal(); - return new FieldVector3D<>(s, sunSpacecraft, - s.multiply(d).negate(), new FieldVector3D<>(date.getField(), saZ)); - + return Collections.singletonList(radiationFactorParameterDriver); } /** {@inheritDoc} */ @Override - public Vector3D dragAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position, - final Rotation rotation, final double mass, + public Vector3D dragAcceleration(final SpacecraftState state, final double density, final Vector3D relativeVelocity, final double[] parameters) { - final double dragCoeff = parameters[0]; - final double liftRatio = liftParameterDriver == null ? 0.0 : parameters[1]; + final double dragFactor = parameters[0]; // relative velocity in spacecraft frame final double vNorm2 = relativeVelocity.getNormSq(); final double vNorm = FastMath.sqrt(vNorm2); - final Vector3D vDir = rotation.applyTo(relativeVelocity.scalarMultiply(1.0 / vNorm)); - final double coeff = density * dragCoeff * vNorm2 / (2.0 * mass); - final double oMr = 1 - liftRatio; - - // solar array contribution - final Vector3D frontNormal = getNormal(date, frame, position, rotation); - final double s = coeff * solarArrayArea * Vector3D.dotProduct(frontNormal, vDir); - Vector3D acceleration = new Vector3D(oMr * FastMath.abs(s), vDir, - liftRatio * s * 2, frontNormal); - - // body facets contribution - for (final Facet facet : facets) { - final double dot = Vector3D.dotProduct(facet.getNormal(), vDir); - if (dot < 0) { - // the facet intercepts the incoming flux - final double f = coeff * facet.getArea() * dot; - acceleration = new Vector3D(1, acceleration, - oMr * FastMath.abs(f), vDir, - liftRatio * f * 2, facet.getNormal()); + final Vector3D vDir = state.getAttitude().getRotation().applyTo(relativeVelocity.scalarMultiply(1.0 / vNorm)); + final double coeff = density * dragFactor * vNorm2 / (2.0 * state.getMass()); + + // panels contribution + Vector3D acceleration = Vector3D.ZERO; + for (final Panel panel : panels) { + Vector3D normal = panel.getNormal(state); + double dot = Vector3D.dotProduct(normal, vDir); + if (panel.isDoubleSided() && dot > 0) { + // the flux comes from the back side + normal = normal.negate(); + dot = -dot; } - } - - // convert back to inertial frame - return rotation.applyInverseTo(acceleration); - - } - - /** {@inheritDoc} */ - @Override - public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position, - final Rotation rotation, final double mass, final Vector3D flux, - final double[] parameters) { - - if (flux.getNormSq() < Precision.SAFE_MIN) { - // null illumination (we are probably in umbra) - return Vector3D.ZERO; - } - - // radiation flux in spacecraft frame - final Vector3D fluxSat = rotation.applyTo(flux); - - // solar array contribution - Vector3D normal = getNormal(date, frame, position, rotation); - double dot = Vector3D.dotProduct(normal, fluxSat); - if (dot > 0) { - // the solar array is illuminated backward, - // fix signs to compute contribution correctly - dot = -dot; - normal = normal.negate(); - } - Vector3D force = facetRadiationAcceleration(normal, solarArrayArea, fluxSat, dot, parameters); - - // body facets contribution - for (final Facet bodyFacet : facets) { - normal = bodyFacet.getNormal(); - dot = Vector3D.dotProduct(normal, fluxSat); if (dot < 0) { - // the facet intercepts the incoming flux - force = force.add(facetRadiationAcceleration(normal, bodyFacet.getArea(), fluxSat, dot, parameters)); + // the panel intercepts the incoming flux + final double f = coeff * panel.getDrag() * panel.getArea() * dot; + final double liftRatio = panel.getLiftRatio(); + acceleration = new Vector3D(1, acceleration, + (1 - liftRatio) * FastMath.abs(f), vDir, + liftRatio * f * 2, normal); } } - // convert to inertial frame - return rotation.applyInverseTo(new Vector3D(1.0 / mass, force)); + // convert back to inertial frame + return state.getAttitude().getRotation().applyInverseTo(acceleration); } /** {@inheritDoc} */ @Override public > FieldVector3D - dragAcceleration(final FieldAbsoluteDate date, final Frame frame, - final FieldVector3D position, final FieldRotation rotation, - final T mass, final T density, final FieldVector3D relativeVelocity, + dragAcceleration(final FieldSpacecraftState state, + final T density, final FieldVector3D relativeVelocity, final T[] parameters) { - final T dragCoeff = parameters[0]; - final T liftRatio = liftParameterDriver == null ? dragCoeff.getField().getZero() : parameters[1]; + final Field field = state.getDate().getField(); + final T dragFactor = parameters[0]; // relative velocity in spacecraft frame final T vNorm2 = relativeVelocity.getNormSq(); - final T vNorm = vNorm2.sqrt(); - final FieldVector3D vDir = rotation.applyTo(relativeVelocity.scalarMultiply(vNorm.reciprocal())); - final T coeff = density.multiply(0.5).multiply(dragCoeff).multiply(vNorm2).divide(mass); - final T oMr = liftRatio.negate().add(1); - - // solar array facet contribution - final FieldVector3D frontNormal = getNormal(date, frame, position, rotation); - final T s = coeff. - multiply(solarArrayArea). - multiply(FieldVector3D.dotProduct(frontNormal, vDir)); - FieldVector3D acceleration = new FieldVector3D<>(s.abs().multiply(oMr), vDir, - s.multiply(liftRatio).multiply(2), frontNormal); - - // body facets contribution - final Field field = coeff.getField(); - for (final Facet facet : facets) { - final T dot = FieldVector3D.dotProduct(facet.getNormal(), vDir); - if (dot.getReal() < 0) { - // the facet intercepts the incoming flux - final T f = coeff.multiply(facet.getArea()).multiply(dot); - acceleration = new FieldVector3D<>(field.getOne(), acceleration, - f.abs().multiply(oMr), vDir, - f.multiply(liftRatio).multiply(2), new FieldVector3D<>(field, facet.getNormal())); + final T vNorm = FastMath.sqrt(vNorm2); + final FieldVector3D vDir = state.getAttitude().getRotation().applyTo(relativeVelocity.scalarMultiply(vNorm.reciprocal())); + final T coeff = density.multiply(dragFactor).multiply(vNorm2).divide(state.getMass().multiply(2.0)); + + // panels contribution + FieldVector3D acceleration = FieldVector3D.getZero(field); + for (final Panel panel : panels) { + FieldVector3D normal = panel.getNormal(state); + T dot = FieldVector3D.dotProduct(normal, vDir); + if (panel.isDoubleSided() && dot.getReal() > 0) { + // the flux comes from the back side + normal = normal.negate(); + dot = dot.negate(); + } + if (panel.isDoubleSided() || dot.getReal() < 0) { + // the panel intercepts the incoming flux + final T f = coeff.multiply(panel.getDrag() * panel.getArea()).multiply(dot); + final double liftRatio = panel.getLiftRatio(); + acceleration = new FieldVector3D<>(field.getOne(), acceleration, + FastMath.abs(f).multiply(1 - liftRatio), vDir, + f.multiply(2 * liftRatio), normal); } } // convert back to inertial frame - return rotation.applyInverseTo(acceleration); + return state.getAttitude().getRotation().applyInverseTo(acceleration); } /** {@inheritDoc} */ @Override - public > FieldVector3D - radiationPressureAcceleration(final FieldAbsoluteDate date, final Frame frame, - final FieldVector3D position, - final FieldRotation rotation, final T mass, - final FieldVector3D flux, - final T[] parameters) { + public Vector3D radiationPressureAcceleration(final SpacecraftState state, + final Vector3D flux, + final double[] parameters) { - if (flux.getNormSq().getReal() < Precision.SAFE_MIN) { + if (flux.getNormSq() < Precision.SAFE_MIN) { // null illumination (we are probably in umbra) - return FieldVector3D.getZero(date.getField()); + return Vector3D.ZERO; } // radiation flux in spacecraft frame - final FieldVector3D fluxSat = rotation.applyTo(flux); - - // solar array contribution - FieldVector3D normal = getNormal(date, frame, position, rotation); - T dot = FieldVector3D.dotProduct(normal, fluxSat); - if (dot.getReal() > 0) { - // the solar array is illuminated backward, - // fix signs to compute contribution correctly - dot = dot.negate(); - normal = normal.negate(); - } - FieldVector3D force = facetRadiationAcceleration(normal, solarArrayArea, fluxSat, dot, parameters); - - // body facets contribution - for (final Facet bodyFacet : facets) { - normal = new FieldVector3D<>(date.getField(), bodyFacet.getNormal()); - dot = FieldVector3D.dotProduct(fluxSat, normal); - if (dot.getReal() < 0) { - // the facet intercepts the incoming flux - force = force.add(facetRadiationAcceleration(normal, bodyFacet.getArea(), fluxSat, dot, parameters)); + final double radiationFactor = parameters[0]; + final Vector3D fluxSat = state.getAttitude().getRotation().applyTo(flux). + scalarMultiply(radiationFactor); + + // panels contribution + Vector3D force = Vector3D.ZERO; + for (final Panel panel : panels) { + Vector3D normal = panel.getNormal(state); + double dot = Vector3D.dotProduct(normal, fluxSat); + if (panel.isDoubleSided() && dot > 0) { + // the flux comes from the back side + normal = normal.negate(); + dot = -dot; } - } - - // convert to inertial frame - return rotation.applyInverseTo(new FieldVector3D<>(mass.reciprocal(), force)); - - } + if (dot < 0) { + // the panel intercepts the incoming flux - /** Compute contribution of one facet to force. - *

          This method implements equation 8-44 from David A. Vallado's - * Fundamentals of Astrodynamics and Applications, third edition, - * 2007, Microcosm Press.

          - * @param normal facet normal - * @param area facet area - * @param fluxSat radiation pressure flux in spacecraft frame - * @param dot dot product of facet and fluxSat (must be negative) - * @param parameters values of the force model parameters - * @return contribution of the facet to force in spacecraft frame - */ - private Vector3D facetRadiationAcceleration(final Vector3D normal, final double area, final Vector3D fluxSat, - final double dot, final double[] parameters) { + final double absorptionCoeff = panel.getAbsorption(); + final double specularReflectionCoeff = panel.getReflection(); + final double diffuseReflectionCoeff = 1 - (absorptionCoeff + specularReflectionCoeff); + final double psr = fluxSat.getNorm(); - final double absorptionCoeff = parameters[0]; - final double specularReflectionCoeff = parameters[1]; - final double diffuseReflectionCoeff = 1 - (absorptionCoeff + specularReflectionCoeff); + // Vallado's equation 8-44 uses different parameters which are related to our parameters as: + // cos (phi) = -dot / (psr * area) + // n = panel / area + // s = -fluxSat / psr + final double cN = 2 * panel.getArea() * dot * (diffuseReflectionCoeff / 3 - specularReflectionCoeff * dot / psr); + final double cS = (panel.getArea() * dot / psr) * (specularReflectionCoeff - 1); + force = new Vector3D(1, force, cN, normal, cS, fluxSat); - final double psr = fluxSat.getNorm(); + } + } - // Vallado's equation 8-44 uses different parameters which are related to our parameters as: - // cos (phi) = -dot / (psr * area) - // n = facet / area - // s = -fluxSat / psr - final double cN = 2 * area * dot * (diffuseReflectionCoeff / 3 - specularReflectionCoeff * dot / psr); - final double cS = (area * dot / psr) * (specularReflectionCoeff - 1); - return new Vector3D(cN, normal, cS, fluxSat); + // convert to inertial frame + return state.getAttitude().getRotation().applyInverseTo(new Vector3D(1.0 / state.getMass(), force)); } - /** Compute contribution of one facet to force. + /** {@inheritDoc} *

          This method implements equation 8-44 from David A. Vallado's * Fundamentals of Astrodynamics and Applications, third edition, * 2007, Microcosm Press.

          - * @param normal facet normal - * @param area facet area - * @param fluxSat radiation pressure flux in spacecraft frame - * @param dot dot product of facet and fluxSat (must be negative) - * @param parameters values of the force model parameters - * @param type of the field elements - * @return contribution of the facet to force in spacecraft frame */ - private > FieldVector3D - facetRadiationAcceleration(final FieldVector3D normal, final double area, final FieldVector3D fluxSat, - final T dot, final T[] parameters) { + @Override + public > FieldVector3D + radiationPressureAcceleration(final FieldSpacecraftState state, + final FieldVector3D flux, + final T[] parameters) { - final T absorptionCoeff = parameters[0]; - final T specularReflectionCoeff = parameters[1]; - final T diffuseReflectionCoeff = absorptionCoeff.add(specularReflectionCoeff).negate().add(1); + final Field field = state.getDate().getField(); + if (flux.getNormSq().getReal() < Precision.SAFE_MIN) { + // null illumination (we are probably in umbra) + return FieldVector3D.getZero(field); + } - final T psr = fluxSat.getNorm(); + // radiation flux in spacecraft frame + final T radiationFactor = parameters[0]; + final FieldVector3D fluxSat = state.getAttitude().getRotation().applyTo(flux). + scalarMultiply(radiationFactor); + + // panels contribution + FieldVector3D force = FieldVector3D.getZero(field); + for (final Panel panel : panels) { + FieldVector3D normal = panel.getNormal(state); + T dot = FieldVector3D.dotProduct(normal, fluxSat); + if (panel.isDoubleSided() && dot.getReal() > 0) { + // the flux comes from the back side + normal = normal.negate(); + dot = dot.negate(); + } + if (dot.getReal() < 0) { + // the panel intercepts the incoming flux + + final double absorptionCoeff = panel.getAbsorption(); + final double specularReflectionCoeff = panel.getReflection(); + final double diffuseReflectionCoeff = 1 - (absorptionCoeff + specularReflectionCoeff); + final T psr = fluxSat.getNorm(); + + // Vallado's equation 8-44 uses different parameters which are related to our parameters as: + // cos (phi) = -dot / (psr * area) + // n = panel / area + // s = -fluxSat / psr + final T cN = dot.multiply(-2 * panel.getArea()).multiply(dot.multiply(specularReflectionCoeff).divide(psr).subtract(diffuseReflectionCoeff / 3)); + final T cS = dot.multiply(panel.getArea()).multiply(specularReflectionCoeff - 1).divide(psr); + force = new FieldVector3D<>(field.getOne(), force, cN, normal, cS, fluxSat); + } + } - // Vallado's equation 8-44 uses different parameters which are related to our parameters as: - // cos (phi) = -dot / (psr * area) - // n = facet / area - // s = -fluxSat / psr - final T cN = dot.multiply(-2 * area).multiply(dot.multiply(specularReflectionCoeff).divide(psr).subtract(diffuseReflectionCoeff.divide(3))); - final T cS = dot.multiply(area).multiply(specularReflectionCoeff.subtract(1)).divide(psr); - return new FieldVector3D<>(cN, normal, cS, fluxSat); + // convert to inertial frame + return state.getAttitude().getRotation().applyInverseTo(new FieldVector3D<>(state.getMass().reciprocal(), force)); } - /** Class representing a single facet of a convex spacecraft body. - *

          Instance of this class are guaranteed to be immutable.

          - * @author Luc Maisonobe + /** Build the panels of a simple parallelepipedic box. + * @param xLength length of the body along its X axis (m) + * @param yLength length of the body along its Y axis (m) + * @param zLength length of the body along its Z axis (m) + * @param drag drag coefficient + * @param liftRatio drag lift ratio (proportion between 0 and 1 of atmosphere modecules + * that will experience specular reflection when hitting spacecraft instead + * of experiencing diffuse reflection, hence producing lift) + * @param absorption radiation pressure absorption coefficient (between 0 and 1) + * @param reflection radiation pressure specular reflection coefficient (between 0 and 1) + * @return surface vectors array + * @since 12.0 */ - public static class Facet { - - /** Unit Normal vector, pointing outward. */ - private final Vector3D normal; + public static List buildBox(final double xLength, final double yLength, final double zLength, + final double drag, final double liftRatio, + final double absorption, final double reflection) { - /** Area in m². */ - private final double area; - - /** Simple constructor. - * @param normal vector normal to the facet, pointing outward (will be normalized) - * @param area facet area in m² - */ - public Facet(final Vector3D normal, final double area) { - this.normal = normal.normalize(); - this.area = area; - } + final List panels = new ArrayList<>(6); - /** Get unit normal vector. - * @return unit normal vector - */ - public Vector3D getNormal() { - return normal; - } + // spacecraft body, composed of single-sided panels + panels.add(new FixedPanel(Vector3D.MINUS_I, yLength * zLength, false, drag, liftRatio, absorption, reflection)); + panels.add(new FixedPanel(Vector3D.PLUS_I, yLength * zLength, false, drag, liftRatio, absorption, reflection)); + panels.add(new FixedPanel(Vector3D.MINUS_J, xLength * zLength, false, drag, liftRatio, absorption, reflection)); + panels.add(new FixedPanel(Vector3D.PLUS_J, xLength * zLength, false, drag, liftRatio, absorption, reflection)); + panels.add(new FixedPanel(Vector3D.MINUS_K, xLength * yLength, false, drag, liftRatio, absorption, reflection)); + panels.add(new FixedPanel(Vector3D.PLUS_K, xLength * yLength, false, drag, liftRatio, absorption, reflection)); - /** Get facet area. - * @return facet area - */ - public double getArea() { - return area; - } + return panels; } - /** Build the surface vectors for body facets of a simple parallelepipedic box. + /** Build the panels of a simple parallelepiped box plus one solar array panel. * @param xLength length of the body along its X axis (m) * @param yLength length of the body along its Y axis (m) * @param zLength length of the body along its Z axis (m) + * @param sun sun model + * @param solarArrayArea area of the solar array (m²) + * @param solarArrayAxis solar array rotation axis in satellite frame + * @param drag drag coefficient + * @param liftRatio drag lift ratio (proportion between 0 and 1 of atmosphere modecules + * that will experience specular reflection when hitting spacecraft instead + * of experiencing diffuse reflection, hence producing lift) + * @param absorption radiation pressure absorption coefficient (between 0 and 1) + * @param reflection radiation pressure specular reflection coefficient (between 0 and 1) * @return surface vectors array + * @since 12.0 */ - private static Facet[] simpleBoxFacets(final double xLength, final double yLength, final double zLength) { - return new Facet[] { - new Facet(Vector3D.MINUS_I, yLength * zLength), - new Facet(Vector3D.PLUS_I, yLength * zLength), - new Facet(Vector3D.MINUS_J, xLength * zLength), - new Facet(Vector3D.PLUS_J, xLength * zLength), - new Facet(Vector3D.MINUS_K, xLength * yLength), - new Facet(Vector3D.PLUS_K, xLength * yLength) - }; - } + public static List buildPanels(final double xLength, final double yLength, final double zLength, + final ExtendedPVCoordinatesProvider sun, + final double solarArrayArea, final Vector3D solarArrayAxis, + final double drag, final double liftRatio, + final double absorption, final double reflection) { + + // spacecraft body + final List panels = buildBox(xLength, yLength, zLength, drag, liftRatio, absorption, reflection); + + // solar array + panels.add(new PointingPanel(solarArrayAxis, sun, solarArrayArea, drag, liftRatio, absorption, reflection)); + + return panels; - /** Filter out zero area facets. - * @param facets original facets (may include zero area facets) - * @return filtered array - */ - private static List filter(final Facet[] facets) { - final List filtered = new ArrayList(facets.length); - for (Facet facet : facets) { - if (facet.getArea() > 0) { - filtered.add(facet); - } - } - return filtered; } } diff --git a/src/main/java/org/orekit/forces/FixedPanel.java b/src/main/java/org/orekit/forces/FixedPanel.java new file mode 100644 index 0000000000..12810206ee --- /dev/null +++ b/src/main/java/org/orekit/forces/FixedPanel.java @@ -0,0 +1,75 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.forces; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; + +/** Class representing one panel of a satellite, fixed with respect to satellite body. + *

          + * It is mainly used to represent one facet of the body of the satellite. + *

          + * + * @author Luc Maisonobe + * @since 3.0 + */ +public class FixedPanel extends Panel { + + /** Unit Normal vector, pointing outward. */ + private final Vector3D normal; + + /** Simple constructor. + *

          + * As the sum of absorption coefficient, specular reflection coefficient and + * diffuse reflection coefficient is exactly 1, only the first two coefficients + * are needed here, the third one is deduced from the other ones. + *

          + * @param normal vector normal to the panel in spacecraft frame, pointing outward (will be normalized) + * @param area panel area in m² + * @param doubleSided if true, the panel is double-sided (typically solar arrays), + * otherwise it is the side of a box and only relevant for flux coming from its + * positive normal + * @param drag drag coefficient + * @param liftRatio drag lift ratio (proportion between 0 and 1 of atmosphere modecules + * that will experience specular reflection when hitting spacecraft instead + * of experiencing diffuse reflection, hence producing lift) + * @param absorption radiation pressure absorption coefficient (between 0 and 1) + * @param reflection radiation pressure specular reflection coefficient (between 0 and 1) + */ + public FixedPanel(final Vector3D normal, final double area, final boolean doubleSided, + final double drag, final double liftRatio, + final double absorption, final double reflection) { + super(area, doubleSided, drag, liftRatio, absorption, reflection); + this.normal = normal.normalize(); + } + + /** {@inheritDoc} */ + @Override + public Vector3D getNormal(final SpacecraftState state) { + return normal; + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getNormal(final FieldSpacecraftState state) { + return new FieldVector3D<>(state.getDate().getField(), normal); + } + +} diff --git a/src/main/java/org/orekit/forces/ForceModel.java b/src/main/java/org/orekit/forces/ForceModel.java index 98e928f796..f35792515b 100644 --- a/src/main/java/org/orekit/forces/ForceModel.java +++ b/src/main/java/org/orekit/forces/ForceModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,24 +16,22 @@ */ package org.orekit.forces; -import java.util.List; import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.MathArrays; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.EventDetectorsProvider; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.numerical.FieldTimeDerivativesEquations; import org.orekit.propagation.numerical.TimeDerivativesEquations; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; /** This interface represents a force modifying spacecraft motion. * @@ -55,7 +53,7 @@ * Force models which create discontinuous acceleration patterns (typically for maneuvers * start/stop or solar eclipses entry/exit) must provide one or more {@link * org.orekit.propagation.events.EventDetector events detectors} to the - * propagator thanks to their {@link #getEventsDetectors()} method. This method + * propagator thanks to their {@link #getEventDetectors()} method. This method * is called once just before propagation starts. The events states will be checked by * the propagator to ensure accurate propagation and proper events handling. *

          @@ -63,8 +61,9 @@ * @author Mathieu Roméro * @author Luc Maisonobe * @author Véronique Pommier-Maurussane + * @author Melina Vanel */ -public interface ForceModel extends ParametersDriversProvider { +public interface ForceModel extends ParameterDriversProvider, EventDetectorsProvider { /** * Initialize the force model at the start of propagation. This method will be called @@ -96,6 +95,18 @@ default > void init(FieldSpacecraftState in init(initialState.toSpacecraftState(), target.toAbsoluteDate()); } + /** {@inheritDoc}.*/ + @Override + default Stream getEventDetectors() { + return getEventDetectors(getParametersDrivers()); + } + + /** {@inheritDoc}.*/ + @Override + default > Stream> getFieldEventDetectors(Field field) { + return getFieldEventDetectors(field, getParametersDrivers()); + } + /** Compute the contribution of the force model to the perturbing * acceleration. *

          @@ -106,7 +117,7 @@ default > void init(FieldSpacecraftState in * @param adder object where the contribution should be added */ default void addContribution(SpacecraftState s, TimeDerivativesEquations adder) { - adder.addNonKeplerianAcceleration(acceleration(s, getParameters())); + adder.addNonKeplerianAcceleration(acceleration(s, getParameters(s.getDate()))); } /** Compute the contribution of the force model to the perturbing @@ -116,35 +127,7 @@ default void addContribution(SpacecraftState s, TimeDerivativesEquations adder) * @param type of the elements */ default > void addContribution(FieldSpacecraftState s, FieldTimeDerivativesEquations adder) { - adder.addNonKeplerianAcceleration(acceleration(s, getParameters(s.getDate().getField()))); - } - - /** Get force model parameters. - * @return force model parameters - * @since 9.0 - */ - default double[] getParameters() { - final List drivers = getParametersDrivers(); - final double[] parameters = new double[drivers.size()]; - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = drivers.get(i).getValue(); - } - return parameters; - } - - /** Get force model parameters. - * @param field field to which the elements belong - * @param type of the elements - * @return force model parameters - * @since 9.0 - */ - default > T[] getParameters(final Field field) { - final List drivers = getParametersDrivers(); - final T[] parameters = MathArrays.buildArray(field, drivers.size()); - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue()); - } - return parameters; + adder.addNonKeplerianAcceleration(acceleration(s, getParameters(s.getDate().getField(), s.getDate()))); } /** Check if force models depends on position only. @@ -157,7 +140,8 @@ default > T[] getParameters(final Field fie /** Compute acceleration. * @param s current state information: date, kinematics, attitude - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters at state date, + * only 1 value for each parameterDriver * @return acceleration in same frame as state * @since 9.0 */ @@ -165,38 +149,11 @@ default > T[] getParameters(final Field fie /** Compute acceleration. * @param s current state information: date, kinematics, attitude - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters at state date, + * only 1 value for each parameterDriver * @return acceleration in same frame as state * @param type of the elements * @since 9.0 */ > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters); - - /** Get the discrete events related to the model. - * @return stream of events detectors - */ - Stream getEventsDetectors(); - - /** Get the discrete events related to the model. - * @param field field to which the state belongs - * @param extends CalculusFieldElement<T> - * @return stream of events detectors - */ - > Stream> getFieldEventsDetectors(Field field); - - /** Get parameter value from its name. - * @param name parameter name - * @return parameter value - * @since 8.0 - */ - ParameterDriver getParameterDriver(String name); - - /** Check if a parameter is supported. - *

          Supported parameters are those listed by {@link #getParametersDrivers()}.

          - * @param name parameter name to check - * @return true if the parameter is supported - * @see #getParametersDrivers() - */ - boolean isSupported(String name); - } diff --git a/src/main/java/org/orekit/forces/Panel.java b/src/main/java/org/orekit/forces/Panel.java new file mode 100644 index 0000000000..d3e3e4b36c --- /dev/null +++ b/src/main/java/org/orekit/forces/Panel.java @@ -0,0 +1,135 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.forces; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; + +/** Base class representing one panel of a satellite. + * @see FixedPanel + * @see PointingPanel + * @see SlewingPanel + * @author Luc Maisonobe + * @since 3.0 + */ +public abstract class Panel { + + /** Area in m². */ + private final double area; + + /** Indicator for double-sided panels (typically solar arrays). */ + private final boolean doubleSided; + + /** Drag coefficient. */ + private final double drag; + + /** Drag lift ratio. */ + private final double liftRatio; + + /** Radiation pressure absorption coefficient. */ + private final double absorption; + + /** Radiation pressure specular reflection coefficient. */ + private final double reflection; + + /** Simple constructor. + *

          + * As the sum of absorption coefficient, specular reflection coefficient and + * diffuse reflection coefficient is exactly 1, only the first two coefficients + * are needed here, the third one is deduced from the other ones. + *

          + * @param area panel area in m² + * @param doubleSided if true, the panel is double-sided (typically solar arrays), + * otherwise it is the side of a box and only relevant for flux coming from its + * positive normal + * @param drag drag coefficient + * @param liftRatio drag lift ratio (proportion between 0 and 1 of atmosphere modecules + * that will experience specular reflection when hitting spacecraft instead + * of experiencing diffuse reflection, hence producing lift) + * @param absorption radiation pressure absorption coefficient (between 0 and 1) + * @param reflection radiation pressure specular reflection coefficient (between 0 and 1) + */ + protected Panel(final double area, final boolean doubleSided, + final double drag, final double liftRatio, + final double absorption, final double reflection) { + this.area = area; + this.doubleSided = doubleSided; + this.drag = drag; + this.liftRatio = liftRatio; + this.absorption = absorption; + this.reflection = reflection; + } + + /** Get panel area. + * @return panel area + */ + public double getArea() { + return area; + } + + /** Check if the panel is double-sided (typically solar arrays). + * @return true if panel is double-sided + */ + public boolean isDoubleSided() { + return doubleSided; + } + + /** Get drag coefficient. + * @return drag coefficient + */ + public double getDrag() { + return drag; + } + + /** Get drag lift ratio. + * @return drag lift ratio + */ + public double getLiftRatio() { + return liftRatio; + } + + /** Get radiation pressure absorption coefficient. + * @return radiation pressure absorption coefficient + */ + public double getAbsorption() { + return absorption; + } + + /** Get radiation pressure specular reflection coefficient. + * @return radiation pressure specular reflection coefficient + */ + public double getReflection() { + return reflection; + } + + /** Get panel normal in spacecraft frame. + * @param state current spacecraft state + * @return panel normal in spacecraft frame + */ + public abstract Vector3D getNormal(SpacecraftState state); + + /** Get panel normal in spacecraft frame. + * @param type of the field elements + * @param state current spacecraft state + * @return panel normal in spacecraft frame + */ + public abstract > FieldVector3D getNormal(FieldSpacecraftState state); + +} diff --git a/src/main/java/org/orekit/forces/PointingPanel.java b/src/main/java/org/orekit/forces/PointingPanel.java new file mode 100644 index 0000000000..ca3a22f93f --- /dev/null +++ b/src/main/java/org/orekit/forces/PointingPanel.java @@ -0,0 +1,125 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.forces; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.ExtendedPVCoordinatesProvider; + +/** Class representing one panel of a satellite, roughly pointing towards some target. + *

          + * It is mainly used to represent a rotating solar array that points towards the Sun. + *

          + *

          + * The panel rotation with respect to satellite body is the best pointing orientation + * achievable when the rotation axix is fixed by body attitude. Target is therefore + * always exactly in meridian plane defined by rotation axis and panel normal vector. + *

          + *

          + * These panels are considered to be always {@link #isDoubleSided() double-sided}. + *

          + * + * @author Luc Maisonobe + * @since 3.0 + */ +public class PointingPanel extends Panel { + + /** Rotation axis. */ + private final Vector3D rotationAxis; + + /** Target towards which the panel will point. */ + private final ExtendedPVCoordinatesProvider target; + + /** Simple constructor. + *

          + * As the sum of absorption coefficient, specular reflection coefficient and + * diffuse reflection coefficient is exactly 1, only the first two coefficients + * are needed here, the third one is deduced from the other ones. + *

          + *

          + * The panel is considered to rotate about one axis in order to make its normal + * point as close as possible to the target. It means the target will always be + * in the plane defined by the rotation axis and the panel normal. + *

          + * @param rotationAxis rotation axis of the panel + * @param target target towards which the panel will point (the Sun for a solar array) + * @param area panel area in m² + * @param drag drag coefficient + * @param liftRatio drag lift ratio (proportion between 0 and 1 of atmosphere modecules + * that will experience specular reflection when hitting spacecraft instead + * of experiencing diffuse reflection, hence producing lift) + * @param absorption radiation pressure absorption coefficient (between 0 and 1) + * @param reflection radiation pressure specular reflection coefficient (between 0 and 1) + */ + public PointingPanel(final Vector3D rotationAxis, final ExtendedPVCoordinatesProvider target, + final double area, + final double drag, final double liftRatio, + final double absorption, final double reflection) { + super(area, true, drag, liftRatio, absorption, reflection); + this.rotationAxis = rotationAxis.normalize(); + this.target = target; + } + + /** {@inheritDoc} */ + @Override + public Vector3D getNormal(final SpacecraftState state) { + + // compute orientation for best pointing + final Vector3D targetInert = target.getPosition(state.getDate(), state.getFrame()). + subtract(state.getPosition()).normalize(); + final Vector3D targetSpacecraft = state.getAttitude().getRotation().applyTo(targetInert); + final double d = Vector3D.dotProduct(targetSpacecraft, rotationAxis); + final double f = 1 - d * d; + if (f < Precision.EPSILON) { + // extremely rare case: the target is along panel rotation axis + // (there will not be much output power if it is a solar array…) + // we set up an arbitrary normal + return rotationAxis.orthogonal(); + } + + final double s = 1.0 / FastMath.sqrt(f); + return new Vector3D(s, targetSpacecraft, -s * d, rotationAxis); + + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getNormal(final FieldSpacecraftState state) { + // compute orientation for best pointing + final FieldVector3D targetInert = target.getPosition(state.getDate(), state.getFrame()). + subtract(state.getPosition()).normalize(); + final FieldVector3D targetSpacecraft = state.getAttitude().getRotation().applyTo(targetInert); + final T d = FieldVector3D.dotProduct(targetSpacecraft, rotationAxis); + final T f = d.multiply(d).subtract(1).negate(); + if (f.getReal() < Precision.EPSILON) { + // extremely rare case: the target is along panel rotation axis + // (there will not be much output power if it is a solar array…) + // we set up an arbitrary normal + return new FieldVector3D<>(f.getField(), rotationAxis.orthogonal()); + } + + final T s = f.sqrt().reciprocal(); + return new FieldVector3D<>(s, targetSpacecraft, + s.multiply(d).negate(), new FieldVector3D<>(state.getDate().getField(), rotationAxis)); + } + +} diff --git a/src/main/java/org/orekit/forces/SlewingPanel.java b/src/main/java/org/orekit/forces/SlewingPanel.java new file mode 100644 index 0000000000..f2da69e440 --- /dev/null +++ b/src/main/java/org/orekit/forces/SlewingPanel.java @@ -0,0 +1,112 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.forces; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.hipparchus.util.SinCos; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; + +/** Class representing one panel of a satellite, slewing about an axis at constant rate. + *

          + * It is mainly used to represent a solar array with fixed rate rotation. + *

          + *

          + * The panel rotation evolves linearly according to a start position and an + * angular rate (which can be set to 0 for non-rotating panels, which may + * occur in special modes or during contingencies). + *

          + *

          + * These panels are considered to be always {@link #isDoubleSided() double-sided}. + *

          + * + * @author Luc Maisonobe + * @since 3.0 + */ +public class SlewingPanel extends Panel { + + /** Rotation rate of the panel (rad/s). */ + private final double rotationRate; + + /** Reference date for the panel rotation. */ + private final AbsoluteDate referenceDate; + + /** Panel reference axis in spacecraft frame (may be null). */ + private final Vector3D rX; + + /** Panel third axis in spacecraft frame (may be null). */ + private final Vector3D rY; + + /** Simple constructor. + *

          + * As the sum of absorption coefficient, specular reflection coefficient and + * diffuse reflection coefficient is exactly 1, only the first two coefficients + * are needed here, the third one is deduced from the other ones. + *

          + *

          + * The panel is considered to rotate about one axis in order to make its normal + * point as close as possible to the target. It means the target will always be + * in the plane defined by the rotation axis and the panel normal. + *

          + * @param rotationAxis rotation axis of the panel + * @param rotationRate rotation rate of the panel (rad/s) + * @param referenceDate reference date for the panel rotation + * @param referenceNormal direction of the panel normal at reference date in spacecraft frame + * @param area panel area in m² + * @param drag drag coefficient + * @param liftRatio drag lift ratio (proportion between 0 and 1 of atmosphere modecules + * that will experience specular reflection when hitting spacecraft instead + * of experiencing diffuse reflection, hence producing lift) + * @param absorption radiation pressure absorption coefficient (between 0 and 1) + * @param reflection radiation pressure specular reflection coefficient (between 0 and 1) + */ + public SlewingPanel(final Vector3D rotationAxis, final double rotationRate, + final AbsoluteDate referenceDate, final Vector3D referenceNormal, + final double area, + final double drag, final double liftRatio, + final double absorption, final double reflection) { + super(area, true, drag, liftRatio, absorption, reflection); + + this.rotationRate = rotationRate; + this.referenceDate = referenceDate; + this.rY = Vector3D.crossProduct(rotationAxis, referenceNormal).normalize(); + this.rX = Vector3D.crossProduct(rY, rotationAxis).normalize(); + + } + + /** {@inheritDoc} */ + @Override + public Vector3D getNormal(final SpacecraftState state) { + // use a simple rotation at fixed rate + final SinCos sc = FastMath.sinCos(state.getDate().durationFrom(referenceDate) * rotationRate); + return new Vector3D(sc.cos(), rX, sc.sin(), rY); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getNormal(final FieldSpacecraftState state) { + // use a simple rotation at fixed rate + final FieldSinCos sc = FastMath.sinCos(state.getDate().durationFrom(referenceDate).multiply(rotationRate)); + return new FieldVector3D<>(sc.cos(), rX, sc.sin(), rY); + } + +} diff --git a/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java b/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java index 33f87cd2f6..20db2b997f 100644 --- a/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java +++ b/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,7 +22,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.models.earth.atmosphere.Atmosphere; @@ -38,7 +38,7 @@ * @author Bryan Cazabonne * @since 10.2 */ -public abstract class AbstractDragForceModel extends AbstractForceModel { +public abstract class AbstractDragForceModel implements ForceModel { /** Atmospheric model. */ private final Atmosphere atmosphere; diff --git a/src/main/java/org/orekit/forces/drag/DragForce.java b/src/main/java/org/orekit/forces/drag/DragForce.java index b4036e82de..329da11187 100644 --- a/src/main/java/org/orekit/forces/drag/DragForce.java +++ b/src/main/java/org/orekit/forces/drag/DragForce.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,9 +17,7 @@ package org.orekit.forces.drag; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.Gradient; @@ -29,8 +27,6 @@ import org.orekit.models.earth.atmosphere.Atmosphere; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; @@ -49,6 +45,7 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @author Pascal Parraud + * @author Melina Vanel */ public class DragForce extends AbstractDragForceModel { @@ -75,14 +72,13 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final AbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); - final Vector3D position = s.getPVCoordinates().getPosition(); + final Vector3D position = s.getPosition(); final double rho = atmosphere.getDensity(date, position, frame); final Vector3D vAtm = atmosphere.getVelocity(date, position, frame); final Vector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity()); - return spacecraft.dragAcceleration(date, frame, position, s.getAttitude().getRotation(), - s.getMass(), rho, relativeVelocity, parameters); + return spacecraft.dragAcceleration(s, rho, relativeVelocity, parameters); } @@ -94,7 +90,7 @@ public > FieldVector3D acceleration(final F final FieldAbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); - final FieldVector3D position = s.getPVCoordinates().getPosition(); + final FieldVector3D position = s.getPosition(); // Density and its derivatives final T rho; @@ -115,8 +111,7 @@ public > FieldVector3D acceleration(final F final FieldVector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity()); // Drag acceleration along with its derivatives - return spacecraft.dragAcceleration(date, frame, position, s.getAttitude().getRotation(), - s.getMass(), rho, relativeVelocity, parameters); + return spacecraft.dragAcceleration(s, rho, relativeVelocity, parameters); } @@ -126,18 +121,6 @@ public List getParametersDrivers() { return spacecraft.getDragParametersDrivers(); } - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** Get the atmospheric model. * @return atmosphere model */ @@ -151,4 +134,5 @@ public Atmosphere getAtmosphere() { public DragSensitive getSpacecraft() { return spacecraft; } + } diff --git a/src/main/java/org/orekit/forces/drag/DragSensitive.java b/src/main/java/org/orekit/forces/drag/DragSensitive.java index 0dc55710ce..f20d6aeef5 100644 --- a/src/main/java/org/orekit/forces/drag/DragSensitive.java +++ b/src/main/java/org/orekit/forces/drag/DragSensitive.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,13 +19,10 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; /** Interface for spacecraft that are sensitive to atmospheric drag forces. @@ -36,7 +33,12 @@ */ public interface DragSensitive { - /** Parameter name for drag coefficient enabling Jacobian processing. */ + /** Parameter name for global multiplicative factor. + * @since 12.0 + */ + String GLOBAL_DRAG_FACTOR = "global drag factor"; + + /** Parameter name for drag coefficient. */ String DRAG_COEFFICIENT = "drag coefficient"; /** Parameter name for lift ration enabling Jacobian processing. @@ -62,19 +64,15 @@ public interface DragSensitive { * The computation includes all spacecraft specific characteristics * like shape, area and coefficients. *

          - * @param date current date - * @param frame inertial reference frame for state (both orbit and attitude) - * @param position position of spacecraft in reference frame - * @param rotation orientation (attitude) of the spacecraft with respect to reference frame - * @param mass current mass + * @param state current state * @param density atmospheric density at spacecraft position * @param relativeVelocity relative velocity of atmosphere with respect to spacecraft, * in the same inertial frame as spacecraft orbit (m/s) * @param parameters values of the force model parameters * @return spacecraft acceleration in the same inertial frame as spacecraft orbit (m/s²) + * @since 12.0 */ - Vector3D dragAcceleration(AbsoluteDate date, Frame frame, Vector3D position, - Rotation rotation, double mass, + Vector3D dragAcceleration(SpacecraftState state, double density, Vector3D relativeVelocity, double[] parameters); @@ -83,22 +81,16 @@ Vector3D dragAcceleration(AbsoluteDate date, Frame frame, Vector3D position, * The computation includes all spacecraft specific characteristics * like shape, area and coefficients. *

          - * @param date current date - * @param frame inertial reference frame for state (both orbit and attitude) - * @param position position of spacecraft in reference frame - * @param rotation orientation (attitude) of the spacecraft with respect to reference frame - * @param mass current mass + * @param state current state * @param density atmospheric density at spacecraft position * @param relativeVelocity relative velocity of atmosphere with respect to spacecraft, * in the same inertial frame as spacecraft orbit (m/s) * @param parameters values of the force model parameters * @param instance of a CalculusFieldElement * @return spacecraft acceleration in the same inertial frame as spacecraft orbit (m/s²) - * @since 9.0 + * @since 12.0 */ - > FieldVector3D dragAcceleration(FieldAbsoluteDate date, Frame frame, - FieldVector3D position, - FieldRotation rotation, T mass, - T density, FieldVector3D relativeVelocity, - T[] parameters); + > FieldVector3D dragAcceleration(FieldSpacecraftState state, + T density, FieldVector3D relativeVelocity, + T[] parameters); } diff --git a/src/main/java/org/orekit/forces/drag/IsotropicDrag.java b/src/main/java/org/orekit/forces/drag/IsotropicDrag.java index 98bd0f1181..9cef8a86f8 100644 --- a/src/main/java/org/orekit/forces/drag/IsotropicDrag.java +++ b/src/main/java/org/orekit/forces/drag/IsotropicDrag.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,18 +16,16 @@ */ package org.orekit.forces.drag; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; /** This class models isotropic drag effects. @@ -51,7 +49,7 @@ public class IsotropicDrag implements DragSensitive { private final double SCALE = FastMath.scalb(1.0, -3); /** Drivers for drag coefficient parameter. */ - private final ParameterDriver dragParametersDrivers; + private final List dragParametersDrivers; /** Cross section (m²). */ private final double crossSection; @@ -74,39 +72,46 @@ public IsotropicDrag(final double crossSection, final double dragCoeff, final double dragCoeffMin, final double dragCoeffMax) { // in some corner cases (unknown spacecraft, fuel leaks, active piloting ...) // the single coefficient may be arbitrary, and even negative - this.dragParametersDrivers = new ParameterDriver(DragSensitive.DRAG_COEFFICIENT, - dragCoeff, SCALE, - dragCoeffMin, dragCoeffMax); + // the DRAG_COEFFICIENT parameter should be sufficient, but GLOBAL_DRAG_FACTOR + // was added as of 12.0 for consistency with BoxAndSolarArraySpacecraft + // that only has a global multiplicatof factor, hence allowing this name + // to be used for both models + this.dragParametersDrivers = new ArrayList<>(2); + dragParametersDrivers.add(new ParameterDriver(DragSensitive.GLOBAL_DRAG_FACTOR, + 1.0, SCALE, + 0.0, Double.POSITIVE_INFINITY)); + dragParametersDrivers.add(new ParameterDriver(DragSensitive.DRAG_COEFFICIENT, + dragCoeff, SCALE, + dragCoeffMin, dragCoeffMax)); this.crossSection = crossSection; } /** {@inheritDoc} */ @Override public List getDragParametersDrivers() { - return Collections.singletonList(dragParametersDrivers); + return Collections.unmodifiableList(dragParametersDrivers); } /** {@inheritDoc} */ @Override - public Vector3D dragAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position, - final Rotation rotation, final double mass, + public Vector3D dragAcceleration(final SpacecraftState state, final double density, final Vector3D relativeVelocity, final double[] parameters) { - final double dragCoeff = parameters[0]; - return new Vector3D(relativeVelocity.getNorm() * density * dragCoeff * crossSection / (2 * mass), + final double dragCoeff = parameters[0] * parameters[1]; + return new Vector3D(relativeVelocity.getNorm() * density * dragCoeff * crossSection / (2 * state.getMass()), relativeVelocity); } /** {@inheritDoc} */ @Override public > FieldVector3D - dragAcceleration(final FieldAbsoluteDate date, final Frame frame, - final FieldVector3D position, final FieldRotation rotation, - final T mass, final T density, + dragAcceleration(final FieldSpacecraftState state, final T density, final FieldVector3D relativeVelocity, final T[] parameters) { - final T dragCoeff = parameters[0]; - return new FieldVector3D<>(relativeVelocity.getNorm().multiply(density.multiply(dragCoeff).multiply(crossSection / 2)).divide(mass), + final T dragCoeff = parameters[0].multiply(parameters[1]); + return new FieldVector3D<>(relativeVelocity.getNorm(). + multiply(density.multiply(dragCoeff).multiply(crossSection / 2)). + divide(state.getMass()), relativeVelocity); } } diff --git a/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java b/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java index 121f743268..f4050e0de2 100644 --- a/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java +++ b/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,9 @@ */ package org.orekit.forces.drag; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; -import java.util.NavigableSet; import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; @@ -40,6 +40,7 @@ import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeStamped; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.ParameterDriver; @@ -230,15 +231,6 @@ public TimeSpanMap extractDragSensitiveRange(final AbsoluteDate s return dragSensitiveTimeSpanMap.extractRange(start, end); } - /** Get the {@link Transition}s of the drag sensitive time span map. - * @return the {@link Transition}s for the drag sensitive time span map - * @deprecated as of 11.1, replaced by {@link #getFirstSpan()} - */ - @Deprecated - public NavigableSet> getTransitions() { - return dragSensitiveTimeSpanMap.getTransitions(); - } - /** Get the first {@link Span time span} of the drag sensitive time span map. * @return the first {@link Span time span} of the drag sensitive time span map * @since 11.1 @@ -254,7 +246,7 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) // Local atmospheric density final AbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); - final Vector3D position = s.getPVCoordinates().getPosition(); + final Vector3D position = s.getPosition(); final double rho = atmosphere.getDensity(date, position, frame); // Spacecraft relative velocity with respect to the atmosphere @@ -265,8 +257,7 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final double[] extractedParameters = extractParameters(parameters, date); // Compute and return drag acceleration - return getDragSensitive(date).dragAcceleration(date, frame, position, s.getAttitude().getRotation(), - s.getMass(), rho, relativeVelocity, extractedParameters); + return getDragSensitive(date).dragAcceleration(s, rho, relativeVelocity, extractedParameters); } @@ -278,7 +269,7 @@ public > FieldVector3D acceleration(final F // Local atmospheric density final FieldAbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); - final FieldVector3D position = s.getPVCoordinates().getPosition(); + final FieldVector3D position = s.getPosition(); // Density and its derivatives final T rho; @@ -302,8 +293,7 @@ public > FieldVector3D acceleration(final F final T[] extractedParameters = extractParameters(parameters, date); // Compute and return drag acceleration - return getDragSensitive(date.toAbsoluteDate()).dragAcceleration(date, frame, position, s.getAttitude().getRotation(), - s.getMass(), rho, relativeVelocity, extractedParameters); + return getDragSensitive(date.toAbsoluteDate()).dragAcceleration(s, rho, relativeVelocity, extractedParameters); } /**{@inheritDoc} @@ -313,7 +303,7 @@ public > FieldVector3D acceleration(final F *

          */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { // Get the transitions' dates from the TimeSpanMap final AbsoluteDate[] transitionDates = getTransitionDates(); @@ -321,9 +311,7 @@ public Stream getEventsDetectors() { // Initialize the date detector final DateDetector datesDetector = new DateDetector(transitionDates[0]). withMaxCheck(60.). - withHandler((SpacecraftState state, DateDetector d, boolean increasing) -> { - return Action.RESET_DERIVATIVES; - }); + withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES); // Add all transitions' dates to the date detector for (int i = 1; i < transitionDates.length; i++) { datesDetector.addEventDate(transitionDates[i]); @@ -340,20 +328,20 @@ public Stream getEventsDetectors() { *

          */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { // Get the transitions' dates from the TimeSpanMap final AbsoluteDate[] transitionDates = getTransitionDates(); // Initialize the date detector + @SuppressWarnings("unchecked") final FieldDateDetector datesDetector = - new FieldDateDetector<>(new FieldAbsoluteDate<>(field, transitionDates[0])). - withMaxCheck(field.getZero().add(60.)). - withHandler((FieldSpacecraftState state, FieldDateDetector d, boolean increasing) -> { - return Action.RESET_DERIVATIVES; - }); + new FieldDateDetector<>(field, (FieldTimeStamped[]) Array.newInstance(FieldTimeStamped.class, 0)). + withMaxCheck(60.0). + withHandler((FieldSpacecraftState state, FieldEventDetector detector, boolean increasing) -> + Action.RESET_DERIVATIVES); // Add all transitions' dates to the date detector - for (int i = 1; i < transitionDates.length; i++) { + for (int i = 0; i < transitionDates.length; i++) { datesDetector.addEventDate(new FieldAbsoluteDate<>(field, transitionDates[i])); } @@ -494,7 +482,9 @@ private DragSensitive changeDragParameterDriversNames(final DragSensitive dragSe // If the name is the default name for DragSensitive parameter drivers // Modify the name to add the prefix and the date - if (driverName.equals(DragSensitive.DRAG_COEFFICIENT) || driverName.equals(DragSensitive.LIFT_RATIO)) { + if (driverName.equals(DragSensitive.GLOBAL_DRAG_FACTOR) || + driverName.equals(DragSensitive.DRAG_COEFFICIENT) || + driverName.equals(DragSensitive.LIFT_RATIO)) { driver.setName(driverName + datePrefix + date.toString(timeScale)); } } diff --git a/src/main/java/org/orekit/forces/drag/package-info.java b/src/main/java/org/orekit/forces/drag/package-info.java index b415d3b98c..3ee09b6c4c 100644 --- a/src/main/java/org/orekit/forces/drag/package-info.java +++ b/src/main/java/org/orekit/forces/drag/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/empirical/AccelerationModel.java b/src/main/java/org/orekit/forces/empirical/AccelerationModel.java index b5dbad732c..84b7599604 100644 --- a/src/main/java/org/orekit/forces/empirical/AccelerationModel.java +++ b/src/main/java/org/orekit/forces/empirical/AccelerationModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,19 +16,17 @@ */ package org.orekit.forces.empirical; -import java.util.List; - import org.hipparchus.CalculusFieldElement; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** Acceleration model used by empirical force. * @author Bryan Cazabonne * @since 10.3 */ -public interface AccelerationModel { +public interface AccelerationModel extends ParameterDriversProvider { /** Initialize the acceleration model at the start of the propagation. *

          @@ -65,10 +63,4 @@ default void init(SpacecraftState initialState, AbsoluteDate target) { * @return norm of the acceleration */ > T signedAmplitude(FieldSpacecraftState state, T[] parameters); - - /** Get the drivers for acceleration model parameters. - * @return drivers for acceleration model parameters - */ - List getParametersDrivers(); - } diff --git a/src/main/java/org/orekit/forces/empirical/HarmonicAccelerationModel.java b/src/main/java/org/orekit/forces/empirical/HarmonicAccelerationModel.java index 53a4e876d2..e59b24cb03 100644 --- a/src/main/java/org/orekit/forces/empirical/HarmonicAccelerationModel.java +++ b/src/main/java/org/orekit/forces/empirical/HarmonicAccelerationModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/empirical/ParametricAcceleration.java b/src/main/java/org/orekit/forces/empirical/ParametricAcceleration.java index 8e0e37fdc3..f4f63272d4 100644 --- a/src/main/java/org/orekit/forces/empirical/ParametricAcceleration.java +++ b/src/main/java/org/orekit/forces/empirical/ParametricAcceleration.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,19 +19,20 @@ import java.util.List; import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.FieldAttitude; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; /** This class implements a parametric acceleration. @@ -61,8 +62,9 @@ * @since 10.3 * @author Luc Maisonobe * @author Bryan Cazabonne + * @author Melina Vanel */ -public class ParametricAcceleration extends AbstractForceModel { +public class ParametricAcceleration implements ForceModel { /** Direction of the acceleration in defining frame. */ private final Vector3D direction; @@ -155,20 +157,23 @@ public void init(final SpacecraftState initialState, final AbsoluteDate target) public Vector3D acceleration(final SpacecraftState state, final double[] parameters) { + // Date + final AbsoluteDate date = state.getDate(); + final Vector3D inertialDirection; if (isInertial) { // the acceleration direction is already defined in the inertial frame inertialDirection = direction; } else { - final Attitude attitude; + final Rotation rotation; if (attitudeOverride == null) { // the acceleration direction is defined in spacecraft frame as set by the propagator - attitude = state.getAttitude(); + rotation = state.getAttitude().getRotation(); } else { // the acceleration direction is defined in a dedicated frame - attitude = attitudeOverride.getAttitude(state.getOrbit(), state.getDate(), state.getFrame()); + rotation = attitudeOverride.getAttitudeRotation(state.getOrbit(), date, state.getFrame()); } - inertialDirection = attitude.getRotation().applyInverseTo(direction); + inertialDirection = rotation.applyInverseTo(direction); } // Call the acceleration model to compute the acceleration @@ -181,20 +186,23 @@ public Vector3D acceleration(final SpacecraftState state, public > FieldVector3D acceleration(final FieldSpacecraftState state, final T[] parameters) { + // Date + final FieldAbsoluteDate date = state.getDate(); + final FieldVector3D inertialDirection; if (isInertial) { // the acceleration direction is already defined in the inertial frame - inertialDirection = new FieldVector3D<>(state.getDate().getField(), direction); + inertialDirection = new FieldVector3D<>(date.getField(), direction); } else { - final FieldAttitude attitude; + final FieldRotation rotation; if (attitudeOverride == null) { // the acceleration direction is defined in spacecraft frame as set by the propagator - attitude = state.getAttitude(); + rotation = state.getAttitude().getRotation(); } else { // the acceleration direction is defined in a dedicated frame - attitude = attitudeOverride.getAttitude(state.getOrbit(), state.getDate(), state.getFrame()); + rotation = attitudeOverride.getAttitudeRotation(state.getOrbit(), date, state.getFrame()); } - inertialDirection = attitude.getRotation().applyInverseTo(direction); + inertialDirection = rotation.applyInverseTo(direction); } // Call the acceleration model to compute the acceleration @@ -202,15 +210,16 @@ public > FieldVector3D acceleration(final F } + /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { return Stream.empty(); } /** {@inheritDoc} */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { return Stream.empty(); } diff --git a/src/main/java/org/orekit/forces/empirical/PolynomialAccelerationModel.java b/src/main/java/org/orekit/forces/empirical/PolynomialAccelerationModel.java index 0c75bac9c7..813194c610 100644 --- a/src/main/java/org/orekit/forces/empirical/PolynomialAccelerationModel.java +++ b/src/main/java/org/orekit/forces/empirical/PolynomialAccelerationModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/empirical/TimeSpanParametricAcceleration.java b/src/main/java/org/orekit/forces/empirical/TimeSpanParametricAcceleration.java index 58caddf8a6..ab8dff6c68 100644 --- a/src/main/java/org/orekit/forces/empirical/TimeSpanParametricAcceleration.java +++ b/src/main/java/org/orekit/forces/empirical/TimeSpanParametricAcceleration.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,18 +19,17 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.NavigableSet; import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.MathArrays; -import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.FieldAttitude; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; @@ -40,7 +39,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeSpanMap.Span; -import org.orekit.utils.TimeSpanMap.Transition; /** Time span parametric acceleration model. *

          @@ -76,7 +74,7 @@ * @author Bryan Cazabonne * @since 10.3 */ -public class TimeSpanParametricAcceleration extends AbstractForceModel { +public class TimeSpanParametricAcceleration implements ForceModel { /** Prefix for dates before in the parameter drivers' name. */ public static final String DATE_BEFORE = " - Before "; @@ -219,15 +217,6 @@ public TimeSpanMap extractAccelerationModelRange(final Absolu return accelerationModelTimeSpanMap.extractRange(start, end); } - /** Get the {@link Transition}s of the acceleration model time span map. - * @return the {@link Transition}s for the acceleration model time span map - * @deprecated as of 11.1, replace by {@link #getFirstSpan()} - */ - @Deprecated - public NavigableSet> getTransitions() { - return accelerationModelTimeSpanMap.getTransitions(); - } - /** Get the first {@link Span time span} of the acceleration model time span map. * @return the first {@link Span time span} of the acceleration model time span map * @since 11.1 @@ -256,15 +245,15 @@ public Vector3D acceleration(final SpacecraftState state, // the acceleration direction is already defined in the inertial frame inertialDirection = direction; } else { - final Attitude attitude; + final Rotation rotation; if (attitudeOverride == null) { // the acceleration direction is defined in spacecraft frame as set by the propagator - attitude = state.getAttitude(); + rotation = state.getAttitude().getRotation(); } else { // the acceleration direction is defined in a dedicated frame - attitude = attitudeOverride.getAttitude(state.getOrbit(), date, state.getFrame()); + rotation = attitudeOverride.getAttitudeRotation(state.getOrbit(), date, state.getFrame()); } - inertialDirection = attitude.getRotation().applyInverseTo(direction); + inertialDirection = rotation.applyInverseTo(direction); } // Extract the proper parameters valid at date from the input array @@ -289,15 +278,15 @@ public > FieldVector3D acceleration(final F // the acceleration direction is already defined in the inertial frame inertialDirection = new FieldVector3D<>(state.getDate().getField(), direction); } else { - final FieldAttitude attitude; + final FieldRotation rotation; if (attitudeOverride == null) { // the acceleration direction is defined in spacecraft frame as set by the propagator - attitude = state.getAttitude(); + rotation = state.getAttitude().getRotation(); } else { // the acceleration direction is defined in a dedicated frame - attitude = attitudeOverride.getAttitude(state.getOrbit(), date, state.getFrame()); + rotation = attitudeOverride.getAttitudeRotation(state.getOrbit(), date, state.getFrame()); } - inertialDirection = attitude.getRotation().applyInverseTo(direction); + inertialDirection = rotation.applyInverseTo(direction); } // Extract the proper parameters valid at date from the input array @@ -310,13 +299,13 @@ public > FieldVector3D acceleration(final F /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { return Stream.empty(); } /** {@inheritDoc} */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { return Stream.empty(); } diff --git a/src/main/java/org/orekit/forces/empirical/package-info.java b/src/main/java/org/orekit/forces/empirical/package-info.java index a26539aa45..79887be90e 100644 --- a/src/main/java/org/orekit/forces/empirical/package-info.java +++ b/src/main/java/org/orekit/forces/empirical/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/DeSitterRelativity.java b/src/main/java/org/orekit/forces/gravity/DeSitterRelativity.java index 57c4119d6b..d2e3cc94fd 100644 --- a/src/main/java/org/orekit/forces/gravity/DeSitterRelativity.java +++ b/src/main/java/org/orekit/forces/gravity/DeSitterRelativity.java @@ -18,9 +18,7 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -28,11 +26,9 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.bodies.CelestialBody; import org.orekit.data.DataContext; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; @@ -49,7 +45,7 @@ * @author Bryan Cazabonne * @since 10.3 */ -public class DeSitterRelativity extends AbstractForceModel { +public class DeSitterRelativity implements ForceModel { /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; @@ -171,18 +167,6 @@ public > FieldVector3D acceleration(final F return new FieldVector3D<>(gm.multiply(-3.0).divide(r3.multiply(c2)), vEarth.crossProduct(pEarth).crossProduct(vSat)); } - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ @Override public List getParametersDrivers() { diff --git a/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java b/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java index 6e7edd49ac..21bf1c3aff 100644 --- a/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java +++ b/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,10 +19,8 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -32,17 +30,16 @@ import org.hipparchus.linear.RealMatrix; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider.NormalizedSphericalHarmonics; import org.orekit.forces.gravity.potential.TideSystem; import org.orekit.forces.gravity.potential.TideSystemProvider; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; @@ -70,12 +67,12 @@ * paper but not used due to the large memory requirements. Since 2002, even low end * computers and mobile devices do have sufficient memory so this caching has become * feasible nowadays. - *

          + *

          * @author Luc Maisonobe * @since 6.0 */ -public class HolmesFeatherstoneAttractionModel extends AbstractForceModel implements TideSystemProvider { +public class HolmesFeatherstoneAttractionModel implements ForceModel, TideSystemProvider { /** Exponent scaling to avoid floating point overflow. *

          The paper uses 10^280, we prefer a power of two to preserve accuracy thanks to @@ -174,12 +171,23 @@ public TideSystem getTideSystem() { } /** Get the central attraction coefficient μ. - * @return mu central attraction coefficient (m³/s²) + * @return mu central attraction coefficient (m³/s²), + * will throw an exception if gm PDriver has several + * values driven (in this case the method + * {@link #getMu(AbsoluteDate)} must be used. */ public double getMu() { return gmParameterDriver.getValue(); } + /** Get the central attraction coefficient μ. + * @param date date at which mu wants to be known + * @return mu central attraction coefficient (m³/s²) + */ + public double getMu(final AbsoluteDate date) { + return gmParameterDriver.getValue(date); + } + /** Compute the value of the gravity field. * @param date current date * @param position position at which gravity field is desired in body frame @@ -209,24 +217,25 @@ public double nonCentralPart(final AbsoluteDate date, final Vector3D position, f double[] pnm0 = new double[degree + 1]; // compute polar coordinates - final double x = position.getX(); - final double y = position.getY(); - final double z = position.getZ(); - final double x2 = x * x; - final double y2 = y * y; - final double z2 = z * z; - final double r2 = x2 + y2 + z2; - final double r = FastMath.sqrt (r2); - final double rho = FastMath.sqrt(x2 + y2); - final double t = z / r; // cos(theta), where theta is the polar angle - final double u = rho / r; // sin(theta), where theta is the polar angle - final double tOu = z / rho; + final double x = position.getX(); + final double y = position.getY(); + final double z = position.getZ(); + final double x2 = x * x; + final double y2 = y * y; + final double z2 = z * z; + final double rho2 = x2 + y2; + final double r2 = rho2 + z2; + final double r = FastMath.sqrt(r2); + final double rho = FastMath.sqrt(rho2); + final double t = z / r; // cos(theta), where theta is the polar angle + final double u = rho / r; // sin(theta), where theta is the polar angle + final double tOu = z / rho; // compute distance powers final double[] aOrN = createDistancePowersArray(provider.getAe() / r); // compute longitude cosines/sines - final double[][] cosSinLambda = createCosSinArrays(position.getX() / rho, position.getY() / rho); + final double[][] cosSinLambda = createCosSinArrays(x / rho, y / rho); // outer summation over order int index = 0; @@ -306,7 +315,7 @@ public double[] gradient(final AbsoluteDate date, final Vector3D position, final final double[] aOrN = createDistancePowersArray(provider.getAe() / r); // compute longitude cosines/sines - final double[][] cosSinLambda = createCosSinArrays(position.getX() / rho, position.getY() / rho); + final double[][] cosSinLambda = createCosSinArrays(x / rho, y / rho); // outer summation over order int index = 0; @@ -409,11 +418,11 @@ public > T[] gradient(final FieldAbsoluteDate< final T z = position.getZ(); final T x2 = x.multiply(x); final T y2 = y.multiply(y); - final T z2 = z.multiply(z); - final T r2 = x2.add(y2).add(z2); - final T r = r2.sqrt(); final T rho2 = x2.add(y2); final T rho = rho2.sqrt(); + final T z2 = z.multiply(z); + final T r2 = rho2.add(z2); + final T r = r2.sqrt(); final T t = z.divide(r); // cos(theta), where theta is the polar angle final T u = rho.divide(r); // sin(theta), where theta is the polar angle final T tOu = z.divide(rho); @@ -422,7 +431,7 @@ public > T[] gradient(final FieldAbsoluteDate< final T[] aOrN = createDistancePowersArray(r.reciprocal().multiply(provider.getAe())); // compute longitude cosines/sines - final T[][] cosSinLambda = createCosSinArrays(rho.reciprocal().multiply(position.getX()), rho.reciprocal().multiply(position.getY())); + final T[][] cosSinLambda = createCosSinArrays(x.divide(rho), y.divide(rho)); // outer summation over order int index = 0; T value = zero; @@ -485,14 +494,14 @@ public > T[] gradient(final FieldAbsoluteDate< // apply the global mu/r factor final T muOr = r.reciprocal().multiply(mu); value = value.multiply(muOr); - gradient[0] = muOr.multiply(gradient[0]).subtract(value.divide(r)); + gradient[0] = muOr.multiply(gradient[0]).subtract(value.divide(r)); gradient[1] = gradient[1].multiply(muOr); gradient[2] = gradient[2].multiply(muOr); // convert gradient from spherical to Cartesian // Cartesian coordinates // remaining spherical coordinates - final T rPos = position.getNorm(); + // intermediate variables final T xPos = position.getX(); final T yPos = position.getY(); @@ -500,6 +509,7 @@ public > T[] gradient(final FieldAbsoluteDate< final T rho2Pos = x.multiply(x).add(y.multiply(y)); final T rhoPos = rho2.sqrt(); final T r2Pos = rho2.add(z.multiply(z)); + final T rPos = r2Pos.sqrt(); final T[][] jacobianPos = MathArrays.buildArray(zero.getField(), 3, 3); @@ -514,8 +524,9 @@ public > T[] gradient(final FieldAbsoluteDate< // jacobian[1][2] is already set to 0 at allocation time // row representing the gradient of phi - jacobianPos[2][0] = xPos.multiply(zPos).divide(rhoPos.multiply(r2Pos)); - jacobianPos[2][1] = yPos.multiply(zPos).divide(rhoPos.multiply(r2Pos)); + final T rhoPosTimesR2Pos = rhoPos.multiply(r2Pos); + jacobianPos[2][0] = xPos.multiply(zPos).divide(rhoPosTimesR2Pos); + jacobianPos[2][1] = yPos.multiply(zPos).divide(rhoPosTimesR2Pos); jacobianPos[2][2] = rhoPos.negate().divide(r2Pos); final T[] cartGradPos = MathArrays.buildArray(zero.getField(), 3); cartGradPos[0] = gradient[0].multiply(jacobianPos[0][0]).add(gradient[1].multiply(jacobianPos[1][0])).add(gradient[2].multiply(jacobianPos[2][0])); @@ -552,10 +563,10 @@ private GradientHessian gradientHessian(final AbsoluteDate date, final Vector3D final double x2 = x * x; final double y2 = y * y; final double z2 = z * z; - final double r2 = x2 + y2 + z2; - final double r = FastMath.sqrt (r2); final double rho2 = x2 + y2; final double rho = FastMath.sqrt(rho2); + final double r2 = rho2 + z2; + final double r = FastMath.sqrt(r2); final double t = z / r; // cos(theta), where theta is the polar angle final double u = rho / r; // sin(theta), where theta is the polar angle final double tOu = z / rho; @@ -564,7 +575,7 @@ private GradientHessian gradientHessian(final AbsoluteDate date, final Vector3D final double[] aOrN = createDistancePowersArray(provider.getAe() / r); // compute longitude cosines/sines - final double[][] cosSinLambda = createCosSinArrays(position.getX() / rho, position.getY() / rho); + final double[][] cosSinLambda = createCosSinArrays(x / rho, y / rho); // outer summation over order int index = 0; @@ -1036,10 +1047,9 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) // get the position in body frame final AbsoluteDate date = s.getDate(); - final StaticTransform fromBodyFrame = - bodyFrame.getStaticTransformTo(s.getFrame(), date); + final StaticTransform fromBodyFrame = bodyFrame.getStaticTransformTo(s.getFrame(), date); final StaticTransform toBodyFrame = fromBodyFrame.getInverse(); - final Vector3D position = toBodyFrame.transformPosition(s.getPVCoordinates().getPosition()); + final Vector3D position = toBodyFrame.transformPosition(s.getPosition()); // gradient of the non-central part of the gravity field return fromBodyFrame.transformVector(new Vector3D(gradient(date, position, mu))); @@ -1055,7 +1065,7 @@ public > FieldVector3D acceleration(final F // check for faster computation dedicated to derivatives with respect to state if (isGradientStateDerivative(s)) { @SuppressWarnings("unchecked") - final FieldVector3D p = (FieldVector3D) s.getPVCoordinates().getPosition(); + final FieldVector3D p = (FieldVector3D) s.getPosition(); @SuppressWarnings("unchecked") final FieldVector3D a = (FieldVector3D) accelerationWrtState(s.getDate().toAbsoluteDate(), s.getFrame(), p, @@ -1063,7 +1073,7 @@ public > FieldVector3D acceleration(final F return a; } else if (isDSStateDerivative(s)) { @SuppressWarnings("unchecked") - final FieldVector3D p = (FieldVector3D) s.getPVCoordinates().getPosition(); + final FieldVector3D p = (FieldVector3D) s.getPosition(); @SuppressWarnings("unchecked") final FieldVector3D a = (FieldVector3D) accelerationWrtState(s.getDate().toAbsoluteDate(), s.getFrame(), p, @@ -1072,28 +1082,16 @@ public > FieldVector3D acceleration(final F } // get the position in body frame - final FieldAbsoluteDate date = s.getDate(); - final StaticTransform fromBodyFrame = - bodyFrame.getStaticTransformTo(s.getFrame(), date.toAbsoluteDate()); - final StaticTransform toBodyFrame = fromBodyFrame.getInverse(); - final FieldVector3D position = toBodyFrame.transformPosition(s.getPVCoordinates().getPosition()); + final FieldAbsoluteDate date = s.getDate(); + final FieldStaticTransform fromBodyFrame = bodyFrame.getStaticTransformTo(s.getFrame(), date); + final FieldStaticTransform toBodyFrame = fromBodyFrame.getInverse(); + final FieldVector3D position = toBodyFrame.transformPosition(s.getPosition()); // gradient of the non-central part of the gravity field return fromBodyFrame.transformVector(new FieldVector3D<>(gradient(date, position, mu))); } - /** {@inheritDoc} */ - public Stream getEventsDetectors() { - return Stream.empty(); - } - - @Override - /** {@inheritDoc} */ - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** Check if a field state corresponds to derivatives with respect to state. * @param state state to check * @param type of the filed elements @@ -1206,8 +1204,7 @@ private FieldVector3D accelerationWrtState(final AbsoluteDa final int freeParameters = mu.getFreeParameters(); // get the position in body frame - final StaticTransform fromBodyFrame = - bodyFrame.getStaticTransformTo(frame, date); + final StaticTransform fromBodyFrame = bodyFrame.getStaticTransformTo(frame, date); final StaticTransform toBodyFrame = fromBodyFrame.getInverse(); final Vector3D positionBody = toBodyFrame.transformPosition(position.toVector3D()); diff --git a/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java b/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java index 712b0b448b..72e6d5dc5e 100644 --- a/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java +++ b/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,21 +18,17 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.forces.AbstractForceModel; -import org.orekit.frames.FieldTransform; +import org.orekit.forces.ForceModel; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; @@ -50,7 +46,7 @@ * @author Bryan Cazabonne * @since 10.3 */ -public class LenseThirringRelativity extends AbstractForceModel { +public class LenseThirringRelativity implements ForceModel { /** Intensity of the Earth's angular momentum per unit mass [m²/s]. */ private static final double J = 9.8e8; @@ -140,8 +136,8 @@ public > FieldVector3D acceleration(final F final T r2 = r.multiply(r); // Earth’s angular momentum per unit mass - final FieldTransform t = bodyFrame.getTransformTo(s.getFrame(), s.getDate()); - final FieldVector3D j = t.transformVector(Vector3D.PLUS_K).scalarMultiply(J); + final FieldStaticTransform t = bodyFrame.getStaticTransformTo(s.getFrame(), s.getDate()); + final FieldVector3D j = t.transformVector(Vector3D.PLUS_K).scalarMultiply(J); return new FieldVector3D<>(p.dotProduct(j).multiply(3.0).divide(r2), p.crossProduct(v), @@ -150,18 +146,6 @@ public > FieldVector3D acceleration(final F .scalarMultiply(gm.multiply(2.0).divide(r2.multiply(r).multiply(c2))); } - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ @Override public List getParametersDrivers() { diff --git a/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java b/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java index 7c2277e471..6a891e860b 100644 --- a/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java @@ -18,26 +18,25 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.numerical.FieldTimeDerivativesEquations; import org.orekit.propagation.numerical.TimeDerivativesEquations; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; /** Force model for Newtonian central body attraction. * @author Luc Maisonobe */ -public class NewtonianAttraction extends AbstractForceModel { +public class NewtonianAttraction implements ForceModel { /** Name of the single parameter of this model: the central attraction coefficient. */ public static final String CENTRAL_ATTRACTION_COEFFICIENT = "central attraction coefficient"; @@ -69,26 +68,28 @@ public boolean dependsOnPositionOnly() { } /** Get the central attraction coefficient μ. + * @param date date at which the mu value wants to be known * @return mu central attraction coefficient (m³/s²) */ - public double getMu() { - return gmParameterDriver.getValue(); + public double getMu(final AbsoluteDate date) { + return gmParameterDriver.getValue(date); } /** Get the central attraction coefficient μ. * @param the type of the field element * @param field field to which the state belongs + * @param date date at which the mu value wants to be known * @return mu central attraction coefficient (m³/s²) */ - public > T getMu(final Field field) { + public > T getMu(final Field field, final FieldAbsoluteDate date) { final T zero = field.getZero(); - return zero.add(gmParameterDriver.getValue()); + return zero.add(gmParameterDriver.getValue(date.toAbsoluteDate())); } /** {@inheritDoc} */ @Override public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder) { - adder.addKeplerContribution(getMu()); + adder.addKeplerContribution(getMu(s.getDate())); } /** {@inheritDoc} */ @@ -96,15 +97,15 @@ public void addContribution(final SpacecraftState s, final TimeDerivativesEquati public > void addContribution(final FieldSpacecraftState s, final FieldTimeDerivativesEquations adder) { final Field field = s.getDate().getField(); - adder.addKeplerContribution(getMu(field)); + adder.addKeplerContribution(getMu(field, s.getDate())); } /** {@inheritDoc} */ @Override public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { final double mu = parameters[0]; - final double r2 = s.getPVCoordinates().getPosition().getNormSq(); - return new Vector3D(-mu / (FastMath.sqrt(r2) * r2), s.getPVCoordinates().getPosition()); + final double r2 = s.getPosition().getNormSq(); + return new Vector3D(-mu / (FastMath.sqrt(r2) * r2), s.getPosition()); } /** {@inheritDoc} */ @@ -112,20 +113,8 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) public > FieldVector3D acceleration(final FieldSpacecraftState s, final T[] parameters) { final T mu = parameters[0]; - final T r2 = s.getPVCoordinates().getPosition().getNormSq(); - return new FieldVector3D<>(r2.sqrt().multiply(r2).reciprocal().multiply(mu).negate(), s.getPVCoordinates().getPosition()); - } - - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); + final T r2 = s.getPosition().getNormSq(); + return new FieldVector3D<>(r2.sqrt().multiply(r2).reciprocal().multiply(mu).negate(), s.getPosition()); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/forces/gravity/OceanTides.java b/src/main/java/org/orekit/forces/gravity/OceanTides.java index 6027e9b39b..7b333df597 100644 --- a/src/main/java/org/orekit/forces/gravity/OceanTides.java +++ b/src/main/java/org/orekit/forces/gravity/OceanTides.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,13 +19,12 @@ import java.util.List; import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; -import org.orekit.forces.AbstractForceModel; import org.orekit.forces.ForceModel; import org.orekit.forces.gravity.potential.CachedNormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.GravityFields; @@ -47,7 +46,7 @@ * @since 6.1 * @author Luc Maisonobe */ -public class OceanTides extends AbstractForceModel { +public class OceanTides implements ForceModel { /** Default step for tides field sampling (seconds). */ public static final double DEFAULT_STEP = 600.0; @@ -187,16 +186,16 @@ public > FieldVector3D acceleration(final F /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { // delegate to underlying attraction model - return attractionModel.getEventsDetectors(); + return attractionModel.getEventDetectors(); } /** {@inheritDoc} */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { // delegate to underlying attraction model - return attractionModel.getFieldEventsDetectors(field); + return attractionModel.getFieldEventDetectors(field); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/forces/gravity/OceanTidesField.java b/src/main/java/org/orekit/forces/gravity/OceanTidesField.java index 3bc07ca5a6..57222a54c9 100644 --- a/src/main/java/org/orekit/forces/gravity/OceanTidesField.java +++ b/src/main/java/org/orekit/forces/gravity/OceanTidesField.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -131,13 +131,6 @@ public AbsoluteDate getReferenceDate() { return AbsoluteDate.ARBITRARY_EPOCH; } - /** {@inheritDoc} */ - @Override - @Deprecated - public double getOffset(final AbsoluteDate date) { - return date.durationFrom(getReferenceDate()); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { diff --git a/src/main/java/org/orekit/forces/gravity/Relativity.java b/src/main/java/org/orekit/forces/gravity/Relativity.java index 82d16bdb8f..6d2fd1d84d 100644 --- a/src/main/java/org/orekit/forces/gravity/Relativity.java +++ b/src/main/java/org/orekit/forces/gravity/Relativity.java @@ -18,18 +18,14 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; @@ -45,7 +41,7 @@ * @see "Montenbruck, Oliver, and Gill, Eberhard. Satellite orbits : models, methods, and * applications. Berlin New York: Springer, 2000." */ -public class Relativity extends AbstractForceModel { +public class Relativity implements ForceModel { /** Central attraction scaling factor. *

          @@ -125,18 +121,6 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ @Override public List getParametersDrivers() { diff --git a/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java b/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java index 92bea49c0d..9f71cdeedb 100644 --- a/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,20 +18,16 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.bodies.CelestialBodies; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.ParameterDriver; /** Body attraction force model computed as absolute acceleration towards a body. @@ -65,7 +61,7 @@ * @author Luc Maisonobe * @author Julio Hernanz */ -public class SingleBodyAbsoluteAttraction extends AbstractForceModel { +public class SingleBodyAbsoluteAttraction implements ForceModel { /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; @@ -108,8 +104,8 @@ public boolean dependsOnPositionOnly() { public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { // compute bodies separation vectors and squared norm - final Vector3D bodyPosition = body.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); - final Vector3D satToBody = bodyPosition.subtract(s.getPVCoordinates().getPosition()); + final Vector3D bodyPosition = body.getPosition(s.getDate(), s.getFrame()); + final Vector3D satToBody = bodyPosition.subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); // compute absolute acceleration @@ -122,9 +118,8 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) public > FieldVector3D acceleration(final FieldSpacecraftState s, final T[] parameters) { // compute bodies separation vectors and squared norm - final FieldVector3D centralToBody = new FieldVector3D<>(s.getA().getField(), - body.getPVCoordinates(s.getDate().toAbsoluteDate(), s.getFrame()).getPosition()); - final FieldVector3D satToBody = centralToBody.subtract(s.getPVCoordinates().getPosition()); + final FieldVector3D centralToBody = body.getPosition(s.getDate(), s.getFrame()); + final FieldVector3D satToBody = centralToBody.subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); // compute absolute acceleration @@ -132,17 +127,6 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - public Stream getEventsDetectors() { - return Stream.empty(); - } - - @Override - /** {@inheritDoc} */ - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ public List getParametersDrivers() { return Collections.singletonList(gmParameterDriver); diff --git a/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java b/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java index a64d3c15a7..fa10fb3ff7 100644 --- a/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,20 +18,16 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.bodies.CelestialBodies; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; @@ -40,7 +36,7 @@ * @author Luc Maisonabe * @author Julio Hernanz */ -public class SingleBodyRelativeAttraction extends AbstractForceModel { +public class SingleBodyRelativeAttraction implements ForceModel { /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; @@ -83,7 +79,7 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) // compute bodies separation vectors and squared norm final PVCoordinates bodyPV = body.getPVCoordinates(s.getDate(), s.getFrame()); - final Vector3D satToBody = bodyPV.getPosition().subtract(s.getPVCoordinates().getPosition()); + final Vector3D satToBody = bodyPV.getPosition().subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); // compute relative acceleration @@ -99,7 +95,7 @@ public > FieldVector3D acceleration(final F // compute bodies separation vectors and squared norm final FieldPVCoordinates bodyPV = body.getPVCoordinates(s.getDate(), s.getFrame()); - final FieldVector3D satToBody = bodyPV.getPosition().subtract(s.getPVCoordinates().getPosition()); + final FieldVector3D satToBody = bodyPV.getPosition().subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); // compute relative acceleration @@ -109,17 +105,6 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - public Stream getEventsDetectors() { - return Stream.empty(); - } - - @Override - /** {@inheritDoc} */ - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ public List getParametersDrivers() { return Collections.singletonList(gmDriver); diff --git a/src/main/java/org/orekit/forces/gravity/SolidTides.java b/src/main/java/org/orekit/forces/gravity/SolidTides.java index 5e202afc2f..1b22ff6500 100644 --- a/src/main/java/org/orekit/forces/gravity/SolidTides.java +++ b/src/main/java/org/orekit/forces/gravity/SolidTides.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,12 +19,11 @@ import java.util.List; import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.AbstractForceModel; import org.orekit.forces.ForceModel; import org.orekit.forces.gravity.potential.CachedNormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; @@ -45,7 +44,7 @@ * @since 6.1 * @author Luc Maisonobe */ -public class SolidTides extends AbstractForceModel { +public class SolidTides implements ForceModel { /** Default step for tides field sampling (seconds). */ public static final double DEFAULT_STEP = 600.0; @@ -141,16 +140,16 @@ public > FieldVector3D acceleration(final F /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { // delegate to underlying attraction model - return attractionModel.getEventsDetectors(); + return attractionModel.getEventDetectors(); } /** {@inheritDoc} */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { // delegate to underlying attraction model - return attractionModel.getFieldEventsDetectors(field); + return attractionModel.getFieldEventDetectors(field); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/forces/gravity/SolidTidesField.java b/src/main/java/org/orekit/forces/gravity/SolidTidesField.java index 76f563e7cd..0bd282bcf3 100644 --- a/src/main/java/org/orekit/forces/gravity/SolidTidesField.java +++ b/src/main/java/org/orekit/forces/gravity/SolidTidesField.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -166,13 +166,6 @@ public AbsoluteDate getReferenceDate() { return AbsoluteDate.ARBITRARY_EPOCH; } - /** {@inheritDoc} */ - @Override - @Deprecated - public double getOffset(final AbsoluteDate date) { - return date.durationFrom(getReferenceDate()); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { @@ -197,7 +190,7 @@ public NormalizedSphericalHarmonics onDate(final AbsoluteDate date) { for (final CelestialBody body : bodies) { // compute tide generating body state - final Vector3D position = body.getPVCoordinates(date, centralBodyFrame).getPosition(); + final Vector3D position = body.getPosition(date, centralBodyFrame); // compute polar coordinates final double x = position.getX(); diff --git a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java index 34ab9c0e62..cc779b5bda 100644 --- a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,20 +18,16 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.bodies.CelestialBodies; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.ParameterDriver; /** Third body attraction force model. @@ -39,7 +35,7 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane */ -public class ThirdBodyAttraction extends AbstractForceModel { +public class ThirdBodyAttraction implements ForceModel { /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; @@ -84,9 +80,9 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final double gm = parameters[0]; // compute bodies separation vectors and squared norm - final Vector3D centralToBody = body.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D centralToBody = body.getPosition(s.getDate(), s.getFrame()); final double r2Central = centralToBody.getNormSq(); - final Vector3D satToBody = centralToBody.subtract(s.getPVCoordinates().getPosition()); + final Vector3D satToBody = centralToBody.subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); // compute relative acceleration @@ -103,10 +99,9 @@ public > FieldVector3D acceleration(final F final T gm = parameters[0]; // compute bodies separation vectors and squared norm - final FieldVector3D centralToBody = new FieldVector3D<>(s.getA().getField(), - body.getPVCoordinates(s.getDate().toAbsoluteDate(), s.getFrame()).getPosition()); + final FieldVector3D centralToBody = body.getPosition(s.getDate(), s.getFrame()); final T r2Central = centralToBody.getNormSq(); - final FieldVector3D satToBody = centralToBody.subtract(s.getPVCoordinates().getPosition()); + final FieldVector3D satToBody = centralToBody.subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); // compute relative acceleration @@ -115,17 +110,6 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ @Override public List getParametersDrivers() { diff --git a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java index 8102d8d9f2..4ab43568e5 100644 --- a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java +++ b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -56,7 +56,7 @@ private FieldVector3D accelerationToEpoch(final SpacecraftState s, fin final double gm = parameters[0]; // compute bodies separation vectors and squared norm - final Vector3D centralToBody = body.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D centralToBody = body.getPosition(s.getDate(), s.getFrame()); // Spacecraft Position final double rx = centralToBody.getX(); @@ -72,7 +72,7 @@ private FieldVector3D accelerationToEpoch(final SpacecraftState s, fin final Gradient r2Central = centralToBodyFV.getNormSq(); - final FieldVector3D satToBody = centralToBodyFV.subtract(s.getPVCoordinates().getPosition()); + final FieldVector3D satToBody = centralToBodyFV.subtract(s.getPosition()); final Gradient r2Sat = satToBody.getNormSq(); return new FieldVector3D<>(gm, satToBody.scalarMultiply(r2Sat.multiply(r2Sat.sqrt()).reciprocal()), diff --git a/src/main/java/org/orekit/forces/gravity/package-info.java b/src/main/java/org/orekit/forces/gravity/package-info.java index 8eb878b710..f3504b2b57 100644 --- a/src/main/java/org/orekit/forces/gravity/package-info.java +++ b/src/main/java/org/orekit/forces/gravity/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/AstronomicalAmplitudeReader.java b/src/main/java/org/orekit/forces/gravity/potential/AstronomicalAmplitudeReader.java index 7373a122dd..b705996dcf 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/AstronomicalAmplitudeReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/AstronomicalAmplitudeReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/CachedNormalizedSphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/CachedNormalizedSphericalHarmonicsProvider.java index 9f4f5c2b49..f4750f7c35 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/CachedNormalizedSphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/CachedNormalizedSphericalHarmonicsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -108,13 +108,6 @@ public AbsoluteDate getReferenceDate() { return rawProvider.getReferenceDate(); } - /** {@inheritDoc} */ - @Deprecated - @Override - public double getOffset(final AbsoluteDate date) { - return rawProvider.getOffset(date); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { @@ -153,8 +146,8 @@ public List generate(final AbsoluteDate existingD if (existingDate == null) { // no prior existing transforms, just generate a first set - for (int i = 0; i < cache.getNeighborsSize(); ++i) { - final AbsoluteDate t = date.shiftedBy((i - cache.getNeighborsSize() / 2) * step); + for (int i = 0; i < cache.getMaxNeighborsSize(); ++i) { + final AbsoluteDate t = date.shiftedBy((i - cache.getMaxNeighborsSize() / 2) * step); fillArray(rawProvider.onDate(t), cnmsnm); generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm)); } diff --git a/src/main/java/org/orekit/forces/gravity/potential/ConstantSphericalHarmonics.java b/src/main/java/org/orekit/forces/gravity/potential/ConstantSphericalHarmonics.java index 02233dab1b..24b1cc0035 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/ConstantSphericalHarmonics.java +++ b/src/main/java/org/orekit/forces/gravity/potential/ConstantSphericalHarmonics.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -45,23 +45,6 @@ class ConstantSphericalHarmonics implements RawSphericalHarmonicsProvider { /** Raw tesseral-sectorial coefficients matrix. */ private final double[] rawS; - /** Simple constructor. - * @param ae central body reference radius - * @param mu central body attraction coefficient - * @param tideSystem tide system - * @param rawC raw tesseral-sectorial coefficients - * @param rawS raw tesseral-sectorial coefficients - * @deprecated as of 11.1, replaced by {@link #ConstantSphericalHarmonics(double, - * double, TideSystem, Flattener, double[], double[])} - */ - @Deprecated - ConstantSphericalHarmonics(final double ae, final double mu, - final TideSystem tideSystem, - final double[][] rawC, final double[][] rawS) { - this(ae, mu, tideSystem, buildFlattener(rawC), - buildFlattener(rawC).flatten(rawC), buildFlattener(rawS).flatten(rawS)); - } - /** Simple constructor. * @param ae central body reference radius * @param mu central body attraction coefficient @@ -107,15 +90,6 @@ class ConstantSphericalHarmonics implements RawSphericalHarmonicsProvider { } - /** Get a flattener for a triangular array. - * @param triangular triangular array to flatten - * @return flattener suited for triangular array dimensions - * @since 11.1 - */ - private static Flattener buildFlattener(final double[][] triangular) { - return new Flattener(triangular.length - 1, triangular[triangular.length - 1].length - 1); - } - /** {@inheritDoc} */ public int getMaxDegree() { return flattener.getDegree(); @@ -145,12 +119,6 @@ public AbsoluteDate getReferenceDate() { return null; } - /** {@inheritDoc} */ - @Deprecated - public double getOffset(final AbsoluteDate date) { - return 0.0; - } - /** {@inheritDoc} */ public TideSystem getTideSystem() { return tideSystem; diff --git a/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java index 4648750904..b384781338 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/EGMFormatReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/FESCHatEpsilonReader.java b/src/main/java/org/orekit/forces/gravity/potential/FESCHatEpsilonReader.java index 1b2eeeb6cb..9ba52c7d38 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/FESCHatEpsilonReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/FESCHatEpsilonReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/FESCnmSnmReader.java b/src/main/java/org/orekit/forces/gravity/potential/FESCnmSnmReader.java index ee6135aadd..f575b38ffb 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/FESCnmSnmReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/FESCnmSnmReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/Flattener.java b/src/main/java/org/orekit/forces/gravity/potential/Flattener.java index 1ef27cbed6..229c4cb798 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/Flattener.java +++ b/src/main/java/org/orekit/forces/gravity/potential/Flattener.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java index 2d9ae11ca7..83cca9dafd 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/GRGSFormatReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java b/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java index 4333fec889..b82645e123 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java +++ b/src/main/java/org/orekit/forces/gravity/potential/GravityFieldFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,6 +24,7 @@ import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; /** Factory used to read gravity field files in several supported formats. * @author Fabien Maussion @@ -196,14 +197,15 @@ public static void clearOceanTidesReaders() { *

          * @param degree maximal degree * @param order maximal order + * @param freezingDate freezing epoch * @return a gravity field coefficients provider containing already loaded data - * @since 6.0 + * @since 12.0 * @see #getNormalizedProvider(int, int) */ @DefaultDataContext - public static NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, - final int order) { - return getGravityFields().getConstantNormalizedProvider(degree, order); + public static NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, final int order, + final AbsoluteDate freezingDate) { + return getGravityFields().getConstantNormalizedProvider(degree, order, freezingDate); } /** Get the gravity field coefficients provider from the first supported file. @@ -219,7 +221,7 @@ public static NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider * @param order maximal order * @return a gravity field coefficients provider containing already loaded data * @since 6.0 - * @see #getConstantNormalizedProvider(int, int) + * @see #getConstantNormalizedProvider(int, int, AbsoluteDate) */ @DefaultDataContext public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree, @@ -238,14 +240,15 @@ public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final i *

          * @param degree maximal degree * @param order maximal order + * @param freezingDate freezing epoch * @return a gravity field coefficients provider containing already loaded data * @since 6.0 * @see #getUnnormalizedProvider(int, int) */ @DefaultDataContext - public static UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, - final int order) { - return getGravityFields().getConstantUnnormalizedProvider(degree, order); + public static UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, final int order, + final AbsoluteDate freezingDate) { + return getGravityFields().getConstantUnnormalizedProvider(degree, order, freezingDate); } /** Get the gravity field coefficients provider from the first supported file. @@ -261,7 +264,7 @@ public static UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProv * @param order maximal order * @return a gravity field coefficients provider containing already loaded data * @since 6.0 - * @see #getConstantUnnormalizedProvider(int, int) + * @see #getConstantUnnormalizedProvider(int, int, AbsoluteDate) */ @DefaultDataContext public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree, diff --git a/src/main/java/org/orekit/forces/gravity/potential/GravityFields.java b/src/main/java/org/orekit/forces/gravity/potential/GravityFields.java index 49f2019d6a..5c426d4a27 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/GravityFields.java +++ b/src/main/java/org/orekit/forces/gravity/potential/GravityFields.java @@ -18,6 +18,8 @@ import java.util.List; +import org.orekit.time.AbsoluteDate; + /** * Defines methods for obtaining gravity fields. * @@ -30,16 +32,18 @@ */ public interface GravityFields { - /** Get a constant gravity field normalized coefficients provider. + /** Get a constant gravity field normalized coefficients provider + * frozen at a given epoch. * * @param degree maximal degree * @param order maximal order + * @param freezingDate freezing epoch * @return a gravity field coefficients provider containing already loaded data - * @since 6.0 + * @since 12.0 * @see #getNormalizedProvider(int, int) */ - NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(int degree, - int order); + NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(int degree, int order, + AbsoluteDate freezingDate); /** Get a gravity field normalized coefficients provider. * @@ -47,21 +51,23 @@ NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(int degree, * @param order maximal order * @return a gravity field coefficients provider containing already loaded data * @since 6.0 - * @see #getConstantNormalizedProvider(int, int) + * @see #getConstantNormalizedProvider(int, int, AbsoluteDate) */ NormalizedSphericalHarmonicsProvider getNormalizedProvider(int degree, int order); - /** Get a constant gravity field unnormalized coefficients provider. + /** Get a constant gravity field unnormalized coefficients provider + * frozen at a given epoch. * * @param degree maximal degree * @param order maximal order + * @param freezingDate freezing epoch * @return a gravity field coefficients provider containing already loaded data - * @since 6.0 + * @since 12.0 * @see #getUnnormalizedProvider(int, int) */ - UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(int degree, - int order); + UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(int degree, int order, + AbsoluteDate freezingDate); /** Get a gravity field unnormalized coefficients provider. * @@ -69,7 +75,7 @@ UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(int degre * @param order maximal order * @return a gravity field coefficients provider containing already loaded data * @since 6.0 - * @see #getConstantUnnormalizedProvider(int, int) + * @see #getConstantUnnormalizedProvider(int, int, AbsoluteDate) */ UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(int degree, int order); diff --git a/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java index 2c2ce40ed1..1bab840f5d 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/ICGEMFormatReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java b/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java index ed70c30075..4be5f71103 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java +++ b/src/main/java/org/orekit/forces/gravity/potential/LazyLoadedGravityFields.java @@ -24,6 +24,7 @@ import org.orekit.data.DataProvidersManager; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; /** @@ -246,14 +247,14 @@ public PotentialCoefficientsReader readGravityField(final int maxParseDegree, * method will be called automatically. */ @Override - public NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, - final int order) { + public NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, final int order, + final AbsoluteDate freezingDate) { final RawSphericalHarmonicsProvider provider; synchronized (readers) { final PotentialCoefficientsReader reader = readGravityField(degree, order); provider = reader.getProvider(true, degree, order); } - final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(provider.getReferenceDate(), provider); + final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(freezingDate, provider); return new WrappingNormalizedProvider(frozen); } @@ -289,14 +290,14 @@ public NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degr * method will be called automatically. */ @Override - public UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, - final int order) { + public UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, final int order, + final AbsoluteDate freezingDate) { final RawSphericalHarmonicsProvider provider; synchronized (readers) { final PotentialCoefficientsReader reader = readGravityField(degree, order); provider = reader.getProvider(false, degree, order); } - final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(provider.getReferenceDate(), provider); + final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(freezingDate, provider); return new WrappingUnnormalizedProvider(frozen); } diff --git a/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java index 5976dd746b..f986f9afcd 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/Normalizer.java b/src/main/java/org/orekit/forces/gravity/potential/Normalizer.java index 08bf3668dd..1427f41988 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/Normalizer.java +++ b/src/main/java/org/orekit/forces/gravity/potential/Normalizer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -70,13 +70,6 @@ public AbsoluteDate getReferenceDate() { return unnormalized.getReferenceDate(); } - /** {@inheritDoc} */ - @Deprecated - @Override - public double getOffset(final AbsoluteDate date) { - return unnormalized.getOffset(date); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { diff --git a/src/main/java/org/orekit/forces/gravity/potential/OceanLoadDeformationCoefficients.java b/src/main/java/org/orekit/forces/gravity/potential/OceanLoadDeformationCoefficients.java index 3d830181cb..74d5b4b201 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/OceanLoadDeformationCoefficients.java +++ b/src/main/java/org/orekit/forces/gravity/potential/OceanLoadDeformationCoefficients.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/OceanTidesReader.java b/src/main/java/org/orekit/forces/gravity/potential/OceanTidesReader.java index 1276d3a1ec..44510dc5f2 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/OceanTidesReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/OceanTidesReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/OceanTidesWave.java b/src/main/java/org/orekit/forces/gravity/potential/OceanTidesWave.java index 4fda995ed9..c1f79750e2 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/OceanTidesWave.java +++ b/src/main/java/org/orekit/forces/gravity/potential/OceanTidesWave.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/PiecewisePart.java b/src/main/java/org/orekit/forces/gravity/potential/PiecewisePart.java index 67d9ba5a13..b2c770732d 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/PiecewisePart.java +++ b/src/main/java/org/orekit/forces/gravity/potential/PiecewisePart.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/PiecewiseSphericalHarmonics.java b/src/main/java/org/orekit/forces/gravity/potential/PiecewiseSphericalHarmonics.java index 53f2bbe479..911b9b26d3 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/PiecewiseSphericalHarmonics.java +++ b/src/main/java/org/orekit/forces/gravity/potential/PiecewiseSphericalHarmonics.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -112,12 +112,6 @@ public AbsoluteDate getReferenceDate() { return last; } - /** {@inheritDoc} */ - @Deprecated - public double getOffset(final AbsoluteDate date) { - return date.durationFrom(references[0]); - } - /** {@inheritDoc} */ public TideSystem getTideSystem() { return constant.getTideSystem(); diff --git a/src/main/java/org/orekit/forces/gravity/potential/PotentialCoefficientsReader.java b/src/main/java/org/orekit/forces/gravity/potential/PotentialCoefficientsReader.java index 28f59b336f..653e25b970 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/PotentialCoefficientsReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/PotentialCoefficientsReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,9 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.text.ParseException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Locale; import org.hipparchus.util.FastMath; @@ -42,7 +40,7 @@ * interface represents all the methods that should be implemented by a reader. * The proper way to use this interface is to call the {@link GravityFieldFactory} * which will determine which reader to use with the selected potential - * coefficients file.

          + * coefficients file.

          * * @see GravityFields * @author Fabien Maussion @@ -233,23 +231,6 @@ protected TideSystem getTideSystem() { return tideSystem; } - /** Set the tesseral-sectorial coefficients matrix. - * @param rawNormalized if true, raw coefficients are normalized - * @param c raw tesseral-sectorial coefficients matrix - * @param s raw tesseral-sectorial coefficients matrix - * @param name name of the file (or zip entry) - * @deprecated as of 11.1, replaced by {@link #setRawCoefficients(boolean, - * Flattener, double[], double[], String)} - */ - @Deprecated - protected void setRawCoefficients(final boolean rawNormalized, - final double[][] c, final double[][] s, - final String name) { - setRawCoefficients(rawNormalized, buildFlattener(c), - buildFlattener(c).flatten(c), buildFlattener(s).flatten(s), - name); - } - /** Set the tesseral-sectorial coefficients matrix. * @param rawNormalized if true, raw coefficients are normalized * @param f converter from triangular to flat form @@ -362,7 +343,6 @@ public abstract void loadData(InputStream input, String name) * @param degree maximal degree * @param order maximal order * @return a new provider - * @see #getConstantProvider(boolean, int, int) * @since 6.0 */ public abstract RawSphericalHarmonicsProvider getProvider(boolean wantNormalized, int degree, int order); @@ -394,48 +374,6 @@ protected ConstantSphericalHarmonics getBaseProvider(final boolean wantNormalize } - /** Get a time-independent provider for read spherical harmonics coefficients. - * @param wantNormalized if true, the raw provider must provide normalized coefficients, - * otherwise it will provide un-normalized coefficients - * @param degree maximal degree - * @param order maximal order - * @return a new provider, with no time-dependent parts - * @see #getProvider(boolean, int, int) - * @since 6.0 - * @deprecated as of 11.1, not used anymore - */ - @Deprecated - protected ConstantSphericalHarmonics getConstantProvider(final boolean wantNormalized, - final int degree, final int order) { - return getBaseProvider(wantNormalized, degree, order); - } - - /** Get a flattener for a triangular array. - * @param triangular triangular array to flatten - * @return flattener suited for triangular array dimensions - * @since 11.1 - */ - private static Flattener buildFlattener(final double[][] triangular) { - return new Flattener(triangular.length - 1, triangular[triangular.length - 1].length - 1); - } - - /** Build a coefficients triangular array. - * @param degree array degree - * @param order array order - * @param value initial value to put in array elements - * @return built array - * @deprecated as of 11.1, replaced by {@link #buildFlatArray(Flattener, double)} - */ - @Deprecated - protected static double[][] buildTriangularArray(final int degree, final int order, final double value) { - final int rows = degree + 1; - final double[][] array = new double[rows][]; - for (int k = 0; k < array.length; ++k) { - array[k] = buildRow(k, order, value); - } - return array; - } - /** Build a coefficients array in flat form. * @param flattener converter from triangular to flat form * @param value initial value to put in array elements @@ -471,93 +409,6 @@ protected static double[] buildRow(final int degree, final int order, final doub return row; } - /** Extend a list of lists of coefficients if needed. - * @param list list of lists of coefficients - * @param degree degree required to be present - * @param order order required to be present - * @param value initial value to put in list elements - * @deprecated as of 11.1, not used anymore - */ - @Deprecated - protected void extendListOfLists(final List> list, final int degree, final int order, - final double value) { - for (int i = list.size(); i <= degree; ++i) { - list.add(new ArrayList<>()); - } - final List listN = list.get(degree); - final Double v = value; - for (int j = listN.size(); j <= order; ++j) { - listN.add(v); - } - } - - /** Convert a list of list into an array. - * @param list list of lists of coefficients - * @return a new array - * @deprecated as of 11.1, not used anymore - */ - @Deprecated - protected double[][] toArray(final List> list) { - final double[][] array = new double[list.size()][]; - for (int i = 0; i < array.length; ++i) { - array[i] = new double[list.get(i).size()]; - for (int j = 0; j < array[i].length; ++j) { - array[i][j] = list.get(i).get(j); - } - } - return array; - } - - /** Parse a coefficient. - * @param field text field to parse - * @param list list where to put the coefficient - * @param i first index in the list - * @param j second index in the list - * @param cName name of the coefficient - * @param name name of the file - * @deprecated as of 11.1, replaced by {@link #parseCoefficient(String, - * Flattener, double[], int, int, String, String)} - */ - @Deprecated - protected void parseCoefficient(final String field, final List> list, - final int i, final int j, - final String cName, final String name) { - final double value = parseDouble(field); - final double oldValue = list.get(i).get(j); - if (Double.isNaN(oldValue) || Precision.equals(oldValue, 0.0, 0)) { - // the coefficient was not already initialized - list.get(i).set(j, value); - } else { - throw new OrekitException(OrekitMessages.DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE, - name, i, j, name); - } - } - - /** Parse a coefficient. - * @param field text field to parse - * @param array array where to put the coefficient - * @param i first index in the list - * @param j second index in the list - * @param cName name of the coefficient - * @param name name of the file - * @deprecated as of 11.1, replaced by {@link #parseCoefficient(String, - * Flattener, double[], int, int, String, String)} - */ - @Deprecated - protected void parseCoefficient(final String field, final double[][] array, - final int i, final int j, - final String cName, final String name) { - final double value = parseDouble(field); - final double oldValue = array[i][j]; - if (Double.isNaN(oldValue) || Precision.equals(oldValue, 0.0, 0)) { - // the coefficient was not already initialized - array[i][j] = value; - } else { - throw new OrekitException(OrekitMessages.DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE, - name, i, j, name); - } - } - /** Parse a coefficient. * @param field text field to parse * @param f converter from triangular to flat form @@ -583,76 +434,6 @@ protected void parseCoefficient(final String field, final Flattener f, } } - /** Rescale coefficients arrays. - * @param scale general scaling factor to apply to all elements - * @param normalizedOrigin if true, the origin coefficients are normalized - * @param originC cosine part of the original coefficients - * @param originS sine part of the origin coefficients - * @param wantNormalized if true, the rescaled coefficients must be normalized - * @param rescaledC cosine part of the rescaled coefficients to fill in (may be the originC array) - * @param rescaledS sine part of the rescaled coefficients to fill in (may be the originS array) - * @deprecated as of 11.1, replaced by {@link #rescale(double, boolean, Flattener, Flattener, double[])} - */ - @Deprecated - protected static void rescale(final double scale, - final boolean normalizedOrigin, final double[][] originC, - final double[][] originS, final boolean wantNormalized, - final double[][] rescaledC, final double[][] rescaledS) { - - if (wantNormalized == normalizedOrigin) { - // apply only the general scaling factor - for (int i = 0; i < rescaledC.length; ++i) { - final double[] rCi = rescaledC[i]; - final double[] rSi = rescaledS[i]; - final double[] oCi = originC[i]; - final double[] oSi = originS[i]; - for (int j = 0; j < rCi.length; ++j) { - rCi[j] = oCi[j] * scale; - rSi[j] = oSi[j] * scale; - } - } - } else { - - // we have to re-scale the coefficients - // (we use rescaledC.length - 1 for the order instead of rescaledC[rescaledC.length - 1].length - 1 - // because typically trend or pulsation arrays are irregular, some test cases have - // order 2 elements at degree 2, but only order 1 elements for higher degrees for example) - final double[][] factors = GravityFieldFactory.getUnnormalizationFactors(rescaledC.length - 1, - rescaledC.length - 1); - - if (wantNormalized) { - // normalize the coefficients - for (int i = 0; i < rescaledC.length; ++i) { - final double[] rCi = rescaledC[i]; - final double[] rSi = rescaledS[i]; - final double[] oCi = originC[i]; - final double[] oSi = originS[i]; - final double[] fi = factors[i]; - for (int j = 0; j < rCi.length; ++j) { - final double factor = scale / fi[j]; - rCi[j] = oCi[j] * factor; - rSi[j] = oSi[j] * factor; - } - } - } else { - // un-normalize the coefficients - for (int i = 0; i < rescaledC.length; ++i) { - final double[] rCi = rescaledC[i]; - final double[] rSi = rescaledS[i]; - final double[] oCi = originC[i]; - final double[] oSi = originS[i]; - final double[] fi = factors[i]; - for (int j = 0; j < rCi.length; ++j) { - final double factor = scale * fi[j]; - rCi[j] = oCi[j] * factor; - rSi[j] = oSi[j] * factor; - } - } - } - - } - } - /** Rescale coefficients arrays. *

          * The normalized/unnormalized nature of original coefficients is inherited from previous parsing. diff --git a/src/main/java/org/orekit/forces/gravity/potential/PulsatingSphericalHarmonics.java b/src/main/java/org/orekit/forces/gravity/potential/PulsatingSphericalHarmonics.java deleted file mode 100644 index a5be3cd315..0000000000 --- a/src/main/java/org/orekit/forces/gravity/potential/PulsatingSphericalHarmonics.java +++ /dev/null @@ -1,190 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.forces.gravity.potential; - -import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; -import org.hipparchus.util.SinCos; -import org.orekit.time.AbsoluteDate; - -/** Simple implementation of {@link RawSphericalHarmonicsProvider} for pulsating gravity fields. - * @author Luc Maisonobe - * @since 6.0 - * @deprecated as of 11.1, replaced by {@link PiecewiseSphericalHarmonics} - */ -@Deprecated -class PulsatingSphericalHarmonics implements RawSphericalHarmonicsProvider { - - /** Underlying part of the field. */ - private final RawSphericalHarmonicsProvider provider; - - /** Pulsation (rad/s). */ - private final double pulsation; - - /** Converter from triangular to flatten array. - * @since 11.1 - */ - private final Flattener flattener; - - /** Cosine component of the cosine coefficients. */ - private final double[] cosC; - - /** Sine component of the cosine coefficients. */ - private final double[] sinC; - - /** Cosine component of the sine coefficients. */ - private final double[] cosS; - - /** Sine component of the sine coefficients. */ - private final double[] sinS; - - /** Simple constructor. - * @param provider underlying part of the field - * @param period period of the pulsation (s) - * @param cosC cosine component of the cosine coefficients - * @param sinC sine component of the cosine coefficients - * @param cosS cosine component of the sine coefficients - * @param sinS sine component of the sine coefficients - * @deprecated as of 11.1, replaced by {@link #PulsatingSphericalHarmonics(RawSphericalHarmonicsProvider, - * double, Flattener, double[], double[], double[], double[])} - */ - @Deprecated - PulsatingSphericalHarmonics(final RawSphericalHarmonicsProvider provider, - final double period, - final double[][] cosC, final double[][] sinC, - final double[][] cosS, final double[][] sinS) { - this(provider, period, buildFlattener(cosC), - buildFlattener(cosC).flatten(cosC), buildFlattener(sinC).flatten(sinC), - buildFlattener(cosS).flatten(cosS), buildFlattener(sinS).flatten(sinS)); - } - - /** Simple constructor. - * @param provider underlying part of the field - * @param period period of the pulsation (s) - * @param flattener flattener from triangular to flatten array - * @param cosC cosine component of the cosine coefficients - * @param sinC sine component of the cosine coefficients - * @param cosS cosine component of the sine coefficients - * @param sinS sine component of the sine coefficients - * @since 11.1 - */ - PulsatingSphericalHarmonics(final RawSphericalHarmonicsProvider provider, - final double period, final Flattener flattener, - final double[] cosC, final double[] sinC, - final double[] cosS, final double[] sinS) { - this.provider = provider; - this.pulsation = MathUtils.TWO_PI / period; - this.flattener = flattener; - this.cosC = cosC.clone(); - this.sinC = sinC.clone(); - this.cosS = cosS.clone(); - this.sinS = sinS.clone(); - } - - /** Get a flattener for a triangular array. - * @param triangular triangular array to flatten - * @return flattener suited for triangular array dimensions - * @since 11.1 - */ - private static Flattener buildFlattener(final double[][] triangular) { - return new Flattener(triangular.length - 1, triangular[triangular.length - 1].length - 1); - } - - /** {@inheritDoc} */ - public int getMaxDegree() { - return FastMath.max(flattener.getDegree(), provider.getMaxDegree()); - } - - /** {@inheritDoc} */ - public int getMaxOrder() { - return FastMath.max(flattener.getOrder(), provider.getMaxOrder()); - } - - /** {@inheritDoc} */ - public double getMu() { - return provider.getMu(); - } - - /** {@inheritDoc} */ - public double getAe() { - return provider.getAe(); - } - - /** {@inheritDoc} */ - public AbsoluteDate getReferenceDate() { - return provider.getReferenceDate(); - } - - /** {@inheritDoc} */ - @Deprecated - public double getOffset(final AbsoluteDate date) { - return provider.getOffset(date); - } - - /** {@inheritDoc} */ - public TideSystem getTideSystem() { - return provider.getTideSystem(); - } - - @Override - @Deprecated - public RawSphericalHarmonics onDate(final AbsoluteDate date) { - //raw (constant) harmonics - final RawSphericalHarmonics raw = provider.onDate(date); - //phase angle, will loose precision for large offsets - final double alpha = pulsation * provider.getOffset(date); - //pre-compute transcendental functions - final SinCos scAlpha = FastMath.sinCos(alpha); - return new RawSphericalHarmonics() { - - @Override - public AbsoluteDate getDate() { - return date; - } - - /** {@inheritDoc} */ - public double getRawCnm(final int n, final int m) { - - // retrieve the underlying part of the coefficient - double cnm = raw.getRawCnm(n, m); - - if (flattener.withinRange(n, m)) { - // add pulsation - cnm += cosC[flattener.index(n, m)] * scAlpha.cos() + sinC[flattener.index(n, m)] * scAlpha.sin(); - } - - return cnm; - } - - /** {@inheritDoc} */ - public double getRawSnm(final int n, final int m) { - - // retrieve the constant part of the coefficient - double snm = raw.getRawSnm(n, m); - - if (flattener.withinRange(n, m)) { - // add pulsation - snm += cosS[flattener.index(n, m)] * scAlpha.cos() + sinS[flattener.index(n, m)] * scAlpha.sin(); - } - - return snm; - } - - }; - } - -} diff --git a/src/main/java/org/orekit/forces/gravity/potential/RawSphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/RawSphericalHarmonicsProvider.java index 5a2cc45906..1359567c52 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/RawSphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/RawSphericalHarmonicsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/SHMFormatReader.java b/src/main/java/org/orekit/forces/gravity/potential/SHMFormatReader.java index ae50ce7e50..a33352e0b9 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/SHMFormatReader.java +++ b/src/main/java/org/orekit/forces/gravity/potential/SHMFormatReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/SecularTrendSphericalHarmonics.java b/src/main/java/org/orekit/forces/gravity/potential/SecularTrendSphericalHarmonics.java index d6f3675457..71c9ee9a52 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/SecularTrendSphericalHarmonics.java +++ b/src/main/java/org/orekit/forces/gravity/potential/SecularTrendSphericalHarmonics.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -42,22 +42,6 @@ class SecularTrendSphericalHarmonics implements RawSphericalHarmonicsProvider { /** Secular trend of the sine coefficients. */ private final double[] sTrend; - /** Simple constructor. - * @param provider underlying provider for the non secular part - * @param referenceDate reference date for the harmonics (considered to be at 12:00 TT) - * @param cTrend secular trend of the cosine coefficients (s-1) - * @param sTrend secular trend of the sine coefficients (s-1) - * @deprecated as of 11.1, replaced by {@link #SecularTrendSphericalHarmonics(RawSphericalHarmonicsProvider, - * AbsoluteDate, Flattener, double[], double[]) - */ - @Deprecated - SecularTrendSphericalHarmonics(final RawSphericalHarmonicsProvider provider, - final AbsoluteDate referenceDate, - final double[][] cTrend, final double[][] sTrend) { - this(provider, referenceDate, buildFlattener(cTrend), - buildFlattener(cTrend).flatten(cTrend), buildFlattener(sTrend).flatten(sTrend)); - } - /** Simple constructor. * @param provider underlying provider for the non secular part * @param referenceDate reference date for the harmonics (considered to be at 12:00 TT) @@ -75,15 +59,6 @@ class SecularTrendSphericalHarmonics implements RawSphericalHarmonicsProvider { this.sTrend = sTrend.clone(); } - /** Get a flattener for a triangular array. - * @param triangular triangular array to flatten - * @return flattener suited for triangular array dimensions - * @since 11.1 - */ - private static Flattener buildFlattener(final double[][] triangular) { - return new Flattener(triangular.length - 1, triangular[triangular.length - 1].length - 1); - } - /** {@inheritDoc} */ public int getMaxDegree() { return FastMath.max(flattener.getDegree(), provider.getMaxDegree()); @@ -109,12 +84,6 @@ public AbsoluteDate getReferenceDate() { return referenceDate; } - /** {@inheritDoc} */ - @Deprecated - public double getOffset(final AbsoluteDate date) { - return date.durationFrom(referenceDate); - } - /** {@inheritDoc} */ public TideSystem getTideSystem() { return provider.getTideSystem(); diff --git a/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java index 6e74ddd1af..a48da324c4 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -69,14 +69,4 @@ public interface SphericalHarmonicsProvider extends TideSystemProvider { */ AbsoluteDate getReferenceDate(); - /** Get the offset from {@link #getReferenceDate reference date} for the harmonics. - * @param date current date - * @return offset between current date and reference date if there is a reference - * date, or 0.0 if there are no reference dates (i.e. if {@link #getReferenceDate} - * returns null) - * @deprecated as of 11.1, this method is only called by deprecated methods - */ - @Deprecated - double getOffset(AbsoluteDate date); - } diff --git a/src/main/java/org/orekit/forces/gravity/potential/TideSystem.java b/src/main/java/org/orekit/forces/gravity/potential/TideSystem.java index 63e693ba8d..442ea5c8df 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/TideSystem.java +++ b/src/main/java/org/orekit/forces/gravity/potential/TideSystem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/TideSystemProvider.java b/src/main/java/org/orekit/forces/gravity/potential/TideSystemProvider.java index c385ec753b..e7a0ecee72 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/TideSystemProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/TideSystemProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/TimeDependentHarmonic.java b/src/main/java/org/orekit/forces/gravity/potential/TimeDependentHarmonic.java index e055e29fd4..bc905d6cda 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/TimeDependentHarmonic.java +++ b/src/main/java/org/orekit/forces/gravity/potential/TimeDependentHarmonic.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java index b590a8e88d..0453e50820 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/gravity/potential/Unnormalizer.java b/src/main/java/org/orekit/forces/gravity/potential/Unnormalizer.java index 780c1e49f2..f68a6f12a7 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/Unnormalizer.java +++ b/src/main/java/org/orekit/forces/gravity/potential/Unnormalizer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -70,13 +70,6 @@ public AbsoluteDate getReferenceDate() { return normalized.getReferenceDate(); } - /** {@inheritDoc} */ - @Deprecated - @Override - public double getOffset(final AbsoluteDate date) { - return normalized.getOffset(date); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { diff --git a/src/main/java/org/orekit/forces/gravity/potential/WrappingNormalizedProvider.java b/src/main/java/org/orekit/forces/gravity/potential/WrappingNormalizedProvider.java index c9ce58cdd3..72f5ec849b 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/WrappingNormalizedProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/WrappingNormalizedProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -69,13 +69,6 @@ public AbsoluteDate getReferenceDate() { return rawProvider.getReferenceDate(); } - /** {@inheritDoc} */ - @Deprecated - @Override - public double getOffset(final AbsoluteDate date) { - return rawProvider.getOffset(date); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { diff --git a/src/main/java/org/orekit/forces/gravity/potential/WrappingUnnormalizedProvider.java b/src/main/java/org/orekit/forces/gravity/potential/WrappingUnnormalizedProvider.java index 6fe7cf4bbf..3aa9402695 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/WrappingUnnormalizedProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/WrappingUnnormalizedProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -69,13 +69,6 @@ public AbsoluteDate getReferenceDate() { return rawProvider.getReferenceDate(); } - /** {@inheritDoc} */ - @Deprecated - @Override - public double getOffset(final AbsoluteDate date) { - return rawProvider.getOffset(date); - } - /** {@inheritDoc} */ @Override public TideSystem getTideSystem() { diff --git a/src/main/java/org/orekit/forces/gravity/potential/package-info.java b/src/main/java/org/orekit/forces/gravity/potential/package-info.java index 3da7170cf3..fb64a82eaa 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/package-info.java +++ b/src/main/java/org/orekit/forces/gravity/potential/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/inertia/InertialForces.java b/src/main/java/org/orekit/forces/inertia/InertialForces.java index 9a41fac954..4fa8cda174 100644 --- a/src/main/java/org/orekit/forces/inertia/InertialForces.java +++ b/src/main/java/org/orekit/forces/inertia/InertialForces.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,25 +18,20 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.Transform; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.AbsolutePVCoordinates; import org.orekit.utils.ParameterDriver; @@ -66,7 +61,7 @@ * @author Guillaume Obrecht * @author Luc Maisonobe */ -public class InertialForces extends AbstractForceModel { +public class InertialForces implements ForceModel { /** Reference inertial frame to use to compute inertial forces. */ private Frame referenceInertialFrame; @@ -101,7 +96,7 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final Vector3D o1 = inertToStateFrame.getAngular().getRotationRate(); final Vector3D oDot1 = inertToStateFrame.getAngular().getRotationAcceleration(); - final Vector3D p2 = s.getPVCoordinates().getPosition(); + final Vector3D p2 = s.getPosition(); final Vector3D v2 = s.getPVCoordinates().getVelocity(); final Vector3D crossCrossP = Vector3D.crossProduct(o1, Vector3D.crossProduct(o1, p2)); @@ -125,7 +120,7 @@ public > FieldVector3D acceleration(final F final FieldVector3D o1 = inertToStateFrame.getAngular().getRotationRate(); final FieldVector3D oDot1 = inertToStateFrame.getAngular().getRotationAcceleration(); - final FieldVector3D p2 = s.getPVCoordinates().getPosition(); + final FieldVector3D p2 = s.getPosition(); final FieldVector3D v2 = s.getPVCoordinates().getVelocity(); final FieldVector3D crossCrossP = FieldVector3D.crossProduct(o1, FieldVector3D.crossProduct(o1, p2)); @@ -138,29 +133,9 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.empty(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> - getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ @Override public List getParametersDrivers() { return Collections.emptyList(); } - - /** {@inheritDoc} */ - @Override - public ParameterDriver getParameterDriver(final String name) { - throw new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, ""); - } - } diff --git a/src/main/java/org/orekit/forces/inertia/package-info.java b/src/main/java/org/orekit/forces/inertia/package-info.java index c6c1640430..549d319312 100644 --- a/src/main/java/org/orekit/forces/inertia/package-info.java +++ b/src/main/java/org/orekit/forces/inertia/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuver.java b/src/main/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuver.java index 6c79dd0ed2..0ec0d68426 100644 --- a/src/main/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuver.java @@ -20,18 +20,16 @@ import org.orekit.forces.maneuvers.propulsion.AbstractConstantThrustPropulsionModel; import org.orekit.forces.maneuvers.propulsion.BasicConstantThrustPropulsionModel; import org.orekit.forces.maneuvers.propulsion.ThrustDirectionAndAttitudeProvider; -import org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers; import org.orekit.forces.maneuvers.trigger.ManeuverTriggers; -import org.orekit.propagation.events.AbstractDetector; -import org.orekit.propagation.events.EventDetector; +import org.orekit.time.AbsoluteDate; /** * This class implements a configurable low thrust maneuver. *

          * The maneuver is composed of succession of a burn interval. Burn intervals are * defined by two detectors. See - * {@link org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers - * EventBasedManeuverTriggers} for more details on the detectors. The attitude + * {@link org.orekit.forces.maneuvers.trigger.StartStopEventsTrigger + * StartStopEventsTrigger} for more details on the detectors. The attitude * and the thrust direction are provided by an instance of * ThrustDirectionProvider See * {@link org.orekit.forces.maneuvers.propulsion.ThrustDirectionAndAttitudeProvider @@ -46,40 +44,14 @@ public class ConfigurableLowThrustManeuver extends Maneuver { /** To be used for ParameterDriver to make thrust non constant. */ private static String THRUST_MODEL_IDENTIFIER = "ConfigurableLowThrustManeuver"; - /** Thrust direction and spaceraft attitude provided by an external object. */ + /** Thrust direction and spacecraft attitude provided by an external object. */ private final ThrustDirectionAndAttitudeProvider thrustDirectionProvider; /** * Constructor. *

          - * This legacy constructor forbids backward propagation. - *

          - *

          - * See {@link org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers - * EventBasedManeuverTriggers} for requirements on detectors - *

          - * @param thrustDirectionProvider thrust direction and attitude provider - * @param startFiringDetector detector to start thrusting (start when - * increasing) - * @param stopFiringDetector detector to stop thrusting (stop when - * increasing) - * @param thrust the thrust force (N) - * @param isp engine specific impulse (s) - */ - public ConfigurableLowThrustManeuver(final ThrustDirectionAndAttitudeProvider thrustDirectionProvider, - final AbstractDetector startFiringDetector, - final AbstractDetector stopFiringDetector, - final double thrust, final double isp) { - this(thrustDirectionProvider, - new EventBasedManeuverTriggers(startFiringDetector, stopFiringDetector), - thrust, isp); - } - - /** - * Constructor. - *

          - * See {@link org.orekit.forces.maneuvers.trigger.EventBasedManeuverTriggers - * EventBasedManeuverTriggers} for requirements on detectors + * See {@link org.orekit.forces.maneuvers.trigger.StartStopEventsTrigger + * StartStopEventsTrigger} for requirements on detectors *

          * @param thrustDirectionProvider thrust direction and attitude provider * @param trigger maneuver triggers @@ -112,7 +84,7 @@ private static BasicConstantThrustPropulsionModel buildBasicConstantThrustPropul } /** - * Getter on Thrust direction and spaceraft attitude provided by an external + * Getter on Thrust direction and spacecraft attitude provided by an external * object. * @return internal field */ @@ -121,20 +93,40 @@ public ThrustDirectionAndAttitudeProvider getThrustDirectionProvider() { } /** - * Get the thrust. - * + * Get the thrust magnitude. + * @param date at which the Thrust wants to be known * @return thrust force (N). */ - public double getThrust() { + public double getThrustMagnitude(final AbsoluteDate date) { + return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getThrustVector(date).getNorm(); + } + + /** + * Get the thrust magnitude. + * @return thrust force (N). Will throw + * an exception if the Thrust driver has several + * values driven + */ + public double getThrustMagnitude() { return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getThrustVector().getNorm(); } /** * Get the specific impulse. - * + * @param date at which the ISP wants to be known * @return specific impulse (s). */ - public double getISP() { + public double getIsp(final AbsoluteDate date) { + return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getIsp(date); + } + + /** + * Get the specific impulse. + * @return specific impulse (s). Will throw + * an exception if the Thrust driver has several + * values driven + */ + public double getIsp() { return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getIsp(); } diff --git a/src/main/java/org/orekit/forces/maneuvers/ConstantThrustManeuver.java b/src/main/java/org/orekit/forces/maneuvers/ConstantThrustManeuver.java index fd80df7fae..f48b37ec83 100644 --- a/src/main/java/org/orekit/forces/maneuvers/ConstantThrustManeuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/ConstantThrustManeuver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -63,6 +63,35 @@ public ConstantThrustManeuver(final AbsoluteDate date, final double duration, this(date, duration, thrust, isp, direction, ""); } + /** Simple constructor for a constant direction and constant thrust. + *

          + * It uses the propulsion model {@link BasicConstantThrustPropulsionModel} and + * the maneuver triggers {@link DateBasedManeuverTriggers} + *

          + * Calling this constructor is equivalent to call {@link + * #ConstantThrustManeuver(AbsoluteDate, double, double, double, Vector3D, String) + * ConstantThrustManeuver(date, duration, thrust, isp, direction, "")}, + * hence not using any prefix for the parameters drivers names. + *

          + * @param date maneuver date + * @param duration the duration of the thrust (s) (if negative, + * the date is considered to be the stop date) + * @param thrust the thrust force (N) + * @param isp engine specific impulse (s) + * @param attitudeOverride the attitude provider to use for the maneuver, or + * null if the attitude from the propagator should be used + * @param direction the acceleration direction in satellite frame. + * @param name name of the maneuver, used as a prefix for the {@link #getParametersDrivers() parameters drivers} + * @since 12.0 + */ + public ConstantThrustManeuver(final AbsoluteDate date, final double duration, + final double thrust, final double isp, + final AttitudeProvider attitudeOverride, + final Vector3D direction, + final String name) { + this(date, duration, thrust, isp, attitudeOverride, direction, Control3DVectorCostType.TWO_NORM, name); + } + /** Simple constructor for a constant direction and constant thrust. *

          * It uses the propulsion model {@link BasicConstantThrustPropulsionModel} and @@ -135,14 +164,16 @@ public ConstantThrustManeuver(final AbsoluteDate date, final double duration, * @param attitudeOverride the attitude provider to use for the maneuver, or * null if the attitude from the propagator should be used * @param direction the acceleration direction in satellite frame + * @param control3DVectorCostType control vector's cost type * @param name name of the maneuver, used as a prefix for the {@link #getParametersDrivers() parameters drivers} - * @since 9.2 + * @since 12.0 */ public ConstantThrustManeuver(final AbsoluteDate date, final double duration, - final double thrust, final double isp, - final AttitudeProvider attitudeOverride, final Vector3D direction, + final double thrust, final double isp, final AttitudeProvider attitudeOverride, + final Vector3D direction, final Control3DVectorCostType control3DVectorCostType, final String name) { - this(date, duration, attitudeOverride, new BasicConstantThrustPropulsionModel(thrust, isp, direction, name)); + this(date, duration, attitudeOverride, + new BasicConstantThrustPropulsionModel(thrust, isp, direction, control3DVectorCostType, name)); } /** Simple constructor for a constant direction and constant thrust. @@ -190,6 +221,17 @@ public ConstantThrustManeuver(final AttitudeProvider attitudeOverride, super(attitudeOverride, dateBasedManeuverTriggers, constantThrustPropulsionModel); } + /** Get the thrust vector (N) in S/C frame. + * @param date date at which the thrust vector wants to be known, + * often the date parameter will not be important and can be whatever + * if the thrust parameter driver as only value estimated over the all + * orbit determination interval + * @return thrust vector (N) in S/C frame. + */ + public Vector3D getThrustVector(final AbsoluteDate date) { + return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getThrustVector(date); + } + /** Get the thrust vector (N) in S/C frame. * @return thrust vector (N) in S/C frame. */ @@ -197,20 +239,50 @@ public Vector3D getThrustVector() { return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getThrustVector(); } - /** Get the thrust. + /** Get the thrust magnitude. + * @param date date at which the thrust vector wants to be known, + * often the date parameter will not be important and can be whatever + * if the thrust parameter driver as only value estimated over the all + * orbit determination interval + * @return thrust force (N). + */ + public double getThrustMagnitude(final AbsoluteDate date) { + return getThrustVector(date).getNorm(); + } + + /** Get the thrust magnitude. * @return thrust force (N). */ - public double getThrust() { + public double getThrustMagnitude() { return getThrustVector().getNorm(); } + /** Get the specific impulse at given date. + * @param date date at which the thrust vector wants to be known, + * often the date parameter will not be important and can be whatever + * if the thrust parameter driver as only value estimated over the all + * orbit determination interval + * @return specific impulse (s). + */ + public double getIsp(final AbsoluteDate date) { + return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getIsp(date); + } + /** Get the specific impulse. * @return specific impulse (s). */ - public double getISP() { + public double getIsp() { return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getIsp(); } + /** Get the flow rate at given date. + * @param date at which the Thrust wants to be known + * @return flow rate (negative, kg/s). + */ + public double getFlowRate(final AbsoluteDate date) { + return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getFlowRate(date); + } + /** Get the flow rate. * @return flow rate (negative, kg/s). */ @@ -218,6 +290,15 @@ public double getFlowRate() { return ((AbstractConstantThrustPropulsionModel) getPropulsionModel()).getFlowRate(); } + /** Get the direction. + * @param date at which the Thrust wants to be known + * @return the direction + * @since 9.2 + */ + public Vector3D getDirection(final AbsoluteDate date) { + return getThrustVector(date).normalize(); + } + /** Get the direction. * @return the direction * @since 9.2 diff --git a/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java b/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java new file mode 100644 index 0000000000..ae8f19ea26 --- /dev/null +++ b/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java @@ -0,0 +1,103 @@ +/* Copyright 2022-2023 Romain Serra + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.forces.maneuvers; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; + +/** Enumerate on types of cost for 3D control vector (thrust as a force or acceleration, including an impulse) + * at a given time. It is typically a norm (for a single, gimbaled thruster it would be the Euclidean one) + * and relates to the mass flow rate. + * See ROSS, I. Michael. Space Trajectory Optimization and L1-norm Optimal Control Problems. + * Modern astrodynamics, 2006, vol. 1, p. 155. + *

          It is used widely across the {@link org.orekit.forces.maneuvers} package.

          + *

          Note that norms in finite-dimensional vector spaces are all equivalent in a topological sense.

          + * @see org.orekit.forces.maneuvers.ImpulseManeuver + * @see org.orekit.forces.maneuvers.FieldImpulseManeuver + * @see org.orekit.forces.maneuvers.Maneuver + * @author Romain Serra + * @since 12.0 + */ +public enum Control3DVectorCostType { + + /** Zero cost (free control). */ + NONE { + @Override + public double evaluate(final Vector3D controlVector) { + return 0.; + } + + @Override + public > T evaluate(final FieldVector3D controlVector) { + return controlVector.getX().getField().getZero(); + } + }, + + /** 1-norm. */ + ONE_NORM { + @Override + public double evaluate(final Vector3D controlVector) { + return controlVector.getNorm1(); + } + + @Override + public > T evaluate(final FieldVector3D controlVector) { + return controlVector.getNorm1(); + } + }, + + /** 2-norm also known as Euclidean. */ + TWO_NORM { + @Override + public double evaluate(final Vector3D controlVector) { + return controlVector.getNorm(); + } + + @Override + public > T evaluate(final FieldVector3D controlVector) { + return controlVector.getNorm(); + } + }, + + /** Infinite norm also known as Max. */ + INF_NORM { + @Override + public double evaluate(final Vector3D controlVector) { + return controlVector.getNormInf(); + } + + @Override + public > T evaluate(final FieldVector3D controlVector) { + return controlVector.getNormInf(); + } + }; + + /** Evaluate the cost of the input seen as a 3D control vector. + * @param controlVector vector + * @return cost of vector + */ + public abstract double evaluate(Vector3D controlVector); + + /** Evaluate the cost of the input seen as a 3D control vector. + * @param CalculusFieldElement used + * @param controlVector vector + * @return cost of vector + */ + public abstract > T evaluate(FieldVector3D controlVector); + +} diff --git a/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java b/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java new file mode 100644 index 0000000000..e354f010a1 --- /dev/null +++ b/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java @@ -0,0 +1,291 @@ +/* Copyright 2020-2023 Exotrail + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.forces.maneuvers; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.FastMath; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.FieldAbstractDetector; +import org.orekit.propagation.events.FieldAdaptableInterval; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldArrayDictionary; +import org.orekit.utils.FieldPVCoordinates; + +/** Impulse maneuver model for propagators working with Fields. + *

          This class implements an impulse maneuver as a discrete event + * that can be provided to any {@link org.orekit.propagation.FieldPropagator + * Propagator} and mirrors the standard version + * {@link org.orekit.forces.maneuvers.ImpulseManeuver}.

          + *

          The maneuver is triggered when an underlying event generates a + * {@link Action#STOP STOP} event, in which case this class will generate a {@link + * Action#RESET_STATE RESET_STATE} + * event (the stop event from the underlying object is therefore filtered out). + * In the simple cases, the underlying event detector may be a basic + * {@link org.orekit.propagation.events.FieldDateDetector date event}, but it + * can also be a more elaborate {@link + * org.orekit.propagation.events.FieldApsideDetector apside event} for apogee + * maneuvers for example.

          + *

          The maneuver is defined by a single velocity increment. + * If no AttitudeProvider is given, the current attitude of the spacecraft, + * defined by the current spacecraft state, will be used as the + * {@link AttitudeProvider} so the velocity increment should be given in + * the same pseudoinertial frame as the {@link FieldSpacecraftState} used to + * construct the propagator that will handle the maneuver. + * If an AttitudeProvider is given, the velocity increment given should be + * defined appropriately in consideration of that provider. So, a typical + * case for tangential maneuvers is to provide a {@link org.orekit.attitudes.LofOffset LOF aligned} + * attitude provider along with a velocity increment defined in accordance with + * that LOF aligned attitude provider; e.g. if the LOF aligned attitude provider + * was constructed using LOFType.VNC the velocity increment should be + * provided in VNC coordinates.

          + *

          The norm through which the delta-V maps to the mass consumption is chosen via the + * enum {@link Control3DVectorCostType}. Default is Euclidean.

          + *

          Beware that the triggering event detector must behave properly both + * before and after maneuver. If for example a node detector is used to trigger + * an inclination maneuver and the maneuver change the orbit to an equatorial one, + * the node detector will fail just after the maneuver, being unable to find a + * node on an equatorial orbit! This is a real case that has been encountered + * during validation ...

          + * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) + * @see org.orekit.forces.maneuvers.ImpulseManeuver + * @author Romain Serra + * @since 12.0 + * @param type of the detector + * @param type of the field elements + */ +public class FieldImpulseManeuver, T extends CalculusFieldElement> + extends FieldAbstractDetector, T> { + + /** The attitude to override during the maneuver, if set. */ + private final AttitudeProvider attitudeOverride; + + /** Triggering event. */ + private final D trigger; + + /** Velocity increment in satellite frame. */ + private final FieldVector3D deltaVSat; + + /** Specific impulse. */ + private final T isp; + + /** Engine exhaust velocity. */ + private final T vExhaust; + + /** Indicator for forward propagation. */ + private boolean forward; + + /** Type of norm linking delta-V to mass consumption. */ + private final Control3DVectorCostType control3DVectorCostType; + + /** Build a new instance. + * @param trigger triggering event + * @param deltaVSat velocity increment in satellite frame + * @param isp engine specific impulse (s) + */ + public FieldImpulseManeuver(final D trigger, final FieldVector3D deltaVSat, final T isp) { + this(trigger, null, deltaVSat, isp); + } + + /** Build a new instance. + * @param trigger triggering event + * @param attitudeOverride the attitude provider to use for the maneuver + * @param deltaVSat velocity increment in satellite frame + * @param isp engine specific impulse (s) + */ + public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOverride, + final FieldVector3D deltaVSat, final T isp) { + this(trigger.getMaxCheckInterval(), trigger.getThreshold(), trigger.getMaxIterationCount(), + new Handler<>(), trigger, attitudeOverride, deltaVSat, isp, + Control3DVectorCostType.TWO_NORM); + } + + /** Build a new instance. + * @param trigger triggering event + * @param attitudeOverride the attitude provider to use for the maneuver + * @param deltaVSat velocity increment in satellite frame + * @param isp engine specific impulse (s) + * @param control3DVectorCostType increment's norm for mass consumption + */ + public FieldImpulseManeuver(final D trigger, final AttitudeProvider attitudeOverride, + final FieldVector3D deltaVSat, final T isp, + final Control3DVectorCostType control3DVectorCostType) { + this(trigger.getMaxCheckInterval(), trigger.getThreshold(), trigger.getMaxIterationCount(), + new Handler<>(), trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); + } + + /** Private constructor with full parameters. + *

          + * This constructor is private as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

          + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param eventHandler event handler to call at event occurrences + * @param trigger triggering event + * @param attitudeOverride the attitude provider to use for the maneuver + * @param deltaVSat velocity increment in satellite frame + * @param isp engine specific impulse (s) + * @param control3DVectorCostType increment's norm for mass consumption + */ + private FieldImpulseManeuver(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, + final FieldEventHandler eventHandler, final D trigger, + final AttitudeProvider attitudeOverride, final FieldVector3D deltaVSat, + final T isp, final Control3DVectorCostType control3DVectorCostType) { + super(maxCheck, threshold, maxIter, eventHandler); + this.trigger = trigger; + this.deltaVSat = deltaVSat; + this.isp = isp; + this.attitudeOverride = attitudeOverride; + this.control3DVectorCostType = control3DVectorCostType; + this.vExhaust = this.isp.multiply(Constants.G0_STANDARD_GRAVITY); + } + + /** {@inheritDoc} */ + @Override + protected FieldImpulseManeuver create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, + final FieldEventHandler fieldEventHandler) { + return new FieldImpulseManeuver<>(newMaxCheck, newThreshold, newMaxIter, fieldEventHandler, + trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); + } + + /** {@inheritDoc} */ + @Override + public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { + forward = t.durationFrom(s0.getDate()).getReal() >= 0; + // Initialize the triggering event + trigger.init(s0, t); + } + + /** {@inheritDoc} */ + @Override + public T g(final FieldSpacecraftState fieldSpacecraftState) { + return trigger.g(fieldSpacecraftState); + } + + /** + * Get the Attitude Provider to use during maneuver. + * @return the attitude provider + */ + public AttitudeProvider getAttitudeOverride() { + return attitudeOverride; + } + + /** Get the triggering event. + * @return triggering event + */ + public FieldEventDetector getTrigger() { + return trigger; + } + + /** Get the velocity increment in satellite frame. + * @return velocity increment in satellite frame + */ + public FieldVector3D getDeltaVSat() { + return deltaVSat; + } + + /** Get the specific impulse. + * @return specific impulse + */ + public T getIsp() { + return isp; + } + + /** Get the control vector's cost type. + * @return control cost type + * @since 12.0 + */ + public Control3DVectorCostType getControl3DVectorCostType() { + return control3DVectorCostType; + } + + /** Local handler. */ + private static class Handler> implements FieldEventHandler { + + /** {@inheritDoc} */ + @Override + public Action eventOccurred(final FieldSpacecraftState s, + final FieldEventDetector detector, + final boolean increasing) { + // filter underlying event + final FieldImpulseManeuver im = (FieldImpulseManeuver) detector; + final Action underlyingAction = im.trigger.getHandler().eventOccurred(s, im.trigger, + increasing); + + return (underlyingAction == Action.STOP) ? Action.RESET_STATE : Action.CONTINUE; + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState resetState(final FieldEventDetector detector, + final FieldSpacecraftState oldState) { + + final FieldImpulseManeuver im = (FieldImpulseManeuver) detector; + final FieldAbsoluteDate date = oldState.getDate(); + final FieldRotation rotation; + + if (im.getAttitudeOverride() == null) { + rotation = oldState.getAttitude().getRotation(); + } else { + rotation = im.attitudeOverride.getAttitudeRotation(oldState.getOrbit(), date, + oldState.getFrame()); + } + + // convert velocity increment in inertial frame + final FieldVector3D deltaV = rotation.applyInverseTo(im.deltaVSat); + final T one = oldState.getMu().getField().getOne(); + final T sign = (im.forward) ? one : one.negate(); + + // apply increment to position/velocity + final FieldPVCoordinates oldPV = oldState.getPVCoordinates(); + final FieldPVCoordinates newPV = + new FieldPVCoordinates<>(oldPV.getPosition(), + new FieldVector3D<>(one, oldPV.getVelocity(), sign, deltaV)); + final FieldCartesianOrbit newOrbit = + new FieldCartesianOrbit<>(newPV, oldState.getFrame(), date, oldState.getMu()); + + // compute new mass + final T normDeltaV = im.control3DVectorCostType.evaluate(im.deltaVSat); + final T newMass = oldState.getMass().multiply(FastMath.exp(normDeltaV.multiply(sign.negate()).divide(im.vExhaust))); + + // pack everything in a new state + FieldSpacecraftState newState = new FieldSpacecraftState<>(oldState.getOrbit().getType().normalize(newOrbit, oldState.getOrbit()), + oldState.getAttitude(), newMass); + + for (final FieldArrayDictionary.Entry entry : oldState.getAdditionalStatesValues().getData()) { + newState = newState.addAdditionalState(entry.getKey(), entry.getValue()); + } + for (final FieldArrayDictionary.Entry entry : oldState.getAdditionalStatesDerivatives().getData()) { + newState = newState.addAdditionalStateDerivative(entry.getKey(), entry.getValue()); + } + + return newState; + } + + } +} diff --git a/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java b/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java index 528d0517fa..e09b6b1632 100644 --- a/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/ImpulseManeuver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,14 +16,15 @@ */ package org.orekit.forces.maneuvers; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.events.Action; import org.hipparchus.util.FastMath; -import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.orbits.CartesianOrbit; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.AbstractDetector; +import org.orekit.propagation.events.AdaptableInterval; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; @@ -57,6 +58,8 @@ * that LOF aligned attitude provider; e.g. if the LOF aligned attitude provider * was constructed using LOFType.VNC the velocity increment should be * provided in VNC coordinates.

          + *

          The norm through which the delta-V maps to the mass consumption is chosen via the + * enum {@link Control3DVectorCostType}. Default is Euclidean.

          *

          Beware that the triggering event detector must behave properly both * before and after maneuver. If for example a node detector is used to trigger * an inclination maneuver and the maneuver change the orbit to an equatorial one, @@ -64,16 +67,15 @@ * node on an equatorial orbit! This is a real case that has been encountered * during validation ...

          * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) - * @param class type for the generic version * @author Luc Maisonobe */ -public class ImpulseManeuver extends AbstractDetector> { +public class ImpulseManeuver extends AbstractDetector { /** The attitude to override during the maneuver, if set. */ private final AttitudeProvider attitudeOverride; /** Triggering event. */ - private final T trigger; + private final EventDetector trigger; /** Velocity increment in satellite frame. */ private final Vector3D deltaVSat; @@ -87,15 +89,16 @@ public class ImpulseManeuver extends AbstractDetector(), - trigger, null, deltaVSat, isp); + public ImpulseManeuver(final EventDetector trigger, final Vector3D deltaVSat, final double isp) { + this(trigger, null, deltaVSat, isp); } @@ -105,19 +108,34 @@ public ImpulseManeuver(final T trigger, final Vector3D deltaVSat, final double i * @param deltaVSat velocity increment in satellite frame * @param isp engine specific impulse (s) */ - public ImpulseManeuver(final T trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat, final double isp) { + public ImpulseManeuver(final EventDetector trigger, final AttitudeProvider attitudeOverride, + final Vector3D deltaVSat, final double isp) { + this(trigger.getMaxCheckInterval(), trigger.getThreshold(), + trigger.getMaxIterationCount(), new Handler(), + trigger, attitudeOverride, deltaVSat, isp, Control3DVectorCostType.TWO_NORM); + } + + /** Build a new instance. + * @param trigger triggering event + * @param attitudeOverride the attitude provider to use for the maneuver + * @param deltaVSat velocity increment in satellite frame + * @param isp engine specific impulse (s) + * @param control3DVectorCostType increment's norm for mass consumption + */ + public ImpulseManeuver(final EventDetector trigger, final AttitudeProvider attitudeOverride, + final Vector3D deltaVSat, final double isp, final Control3DVectorCostType control3DVectorCostType) { this(trigger.getMaxCheckInterval(), trigger.getThreshold(), - trigger.getMaxIterationCount(), new Handler(), - trigger, attitudeOverride, deltaVSat, isp); + trigger.getMaxIterationCount(), new Handler(), + trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

          - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

          - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -125,26 +143,28 @@ public ImpulseManeuver(final T trigger, final AttitudeProvider attitudeOverride, * @param attitudeOverride the attitude provider to use for the maneuver * @param deltaVSat velocity increment in satellite frame * @param isp engine specific impulse (s) + * @param control3DVectorCostType increment's norm for mass consumption * @since 6.1 */ - private ImpulseManeuver(final double maxCheck, final double threshold, - final int maxIter, final EventHandler> handler, - final T trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat, - final double isp) { + protected ImpulseManeuver(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final EventDetector trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat, + final double isp, final Control3DVectorCostType control3DVectorCostType) { super(maxCheck, threshold, maxIter, handler); this.attitudeOverride = attitudeOverride; this.trigger = trigger; this.deltaVSat = deltaVSat; this.isp = isp; this.vExhaust = Constants.G0_STANDARD_GRAVITY * isp; + this.control3DVectorCostType = control3DVectorCostType; } /** {@inheritDoc} */ @Override - protected ImpulseManeuver create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler> newHandler) { - return new ImpulseManeuver(newMaxCheck, newThreshold, newMaxIter, newHandler, - trigger, attitudeOverride, deltaVSat, isp); + protected ImpulseManeuver create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new ImpulseManeuver(newMaxCheck, newThreshold, newMaxIter, newHandler, + trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType); } /** {@inheritDoc} */ @@ -170,7 +190,7 @@ public AttitudeProvider getAttitudeOverride() { /** Get the triggering event. * @return triggering event */ - public T getTrigger() { + public EventDetector getTrigger() { return trigger; } @@ -188,17 +208,24 @@ public double getIsp() { return isp; } - /** Local handler. - * @param class type for the generic version + /** Get the control vector's cost type. + * @return control cost type + * @since 12.0 */ - private static class Handler implements EventHandler> { + public Control3DVectorCostType getControl3DVectorCostType() { + return control3DVectorCostType; + } + + /** Local handler. */ + private static class Handler implements EventHandler { /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final ImpulseManeuver im, + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { // filter underlying event - final Action underlyingAction = im.trigger.eventOccurred(s, increasing); + final ImpulseManeuver im = (ImpulseManeuver) detector; + final Action underlyingAction = im.trigger.getHandler().eventOccurred(s, im.trigger, increasing); return (underlyingAction == Action.STOP) ? Action.RESET_STATE : Action.CONTINUE; @@ -206,20 +233,21 @@ public Action eventOccurred(final SpacecraftState s, final ImpulseManeuver im /** {@inheritDoc} */ @Override - public SpacecraftState resetState(final ImpulseManeuver im, final SpacecraftState oldState) { + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { + final ImpulseManeuver im = (ImpulseManeuver) detector; final AbsoluteDate date = oldState.getDate(); final AttitudeProvider override = im.getAttitudeOverride(); - final Attitude attitude; + final Rotation rotation; if (override == null) { - attitude = oldState.getAttitude(); + rotation = oldState.getAttitude().getRotation(); } else { - attitude = override.getAttitude(oldState.getOrbit(), date, oldState.getFrame()); + rotation = override.getAttitudeRotation(oldState.getOrbit(), date, oldState.getFrame()); } // convert velocity increment in inertial frame - final Vector3D deltaV = attitude.getRotation().applyInverseTo(im.deltaVSat); + final Vector3D deltaV = rotation.applyInverseTo(im.deltaVSat); final double sign = im.forward ? +1 : -1; // apply increment to position/velocity @@ -231,11 +259,12 @@ public SpacecraftState resetState(final ImpulseManeuver im, final SpacecraftS new CartesianOrbit(newPV, oldState.getFrame(), date, oldState.getMu()); // compute new mass - final double newMass = oldState.getMass() * FastMath.exp(-sign * deltaV.getNorm() / im.vExhaust); + final double normDeltaV = im.control3DVectorCostType.evaluate(im.deltaVSat); + final double newMass = oldState.getMass() * FastMath.exp(-sign * normDeltaV / im.vExhaust); // pack everything in a new state SpacecraftState newState = new SpacecraftState(oldState.getOrbit().getType().normalize(newOrbit, oldState.getOrbit()), - attitude, newMass); + oldState.getAttitude(), newMass); for (final DoubleArrayDictionary.Entry entry : oldState.getAdditionalStatesValues().getData()) { newState = newState.addAdditionalState(entry.getKey(), entry.getValue()); } @@ -244,7 +273,6 @@ public SpacecraftState resetState(final ImpulseManeuver im, final SpacecraftS } return newState; - } } diff --git a/src/main/java/org/orekit/forces/maneuvers/Maneuver.java b/src/main/java/org/orekit/forces/maneuvers/Maneuver.java index 2dfe8dccb5..1090c572fc 100644 --- a/src/main/java/org/orekit/forces/maneuvers/Maneuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/Maneuver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,12 +24,14 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FieldAttitude; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.forces.maneuvers.propulsion.PropulsionModel; import org.orekit.forces.maneuvers.trigger.ManeuverTriggers; import org.orekit.propagation.FieldSpacecraftState; @@ -43,9 +45,11 @@ import org.orekit.utils.ParameterDriver; -/** A generic model for maneuvers. +/** A generic model for maneuvers with finite-valued acceleration magnitude, as opposed to instantaneous changes + * in the velocity vector which are defined via detectors (in {@link org.orekit.forces.maneuvers.ImpulseManeuver} and + * {@link org.orekit.forces.maneuvers.FieldImpulseManeuver}). * It contains: - * - An attitude override, this is the attitude used during the maneuver, it can be different than the one + * - An attitude override, this is the attitude used during the maneuver, it can be different from the one * used for propagation; * - A maneuver triggers object from the trigger sub-package. It defines the triggers used to start and stop the maneuvers (dates or events for example). * - A propulsion model from sub-package propulsion. It defines the thrust or ΔV, isp, flow rate etc.. @@ -55,7 +59,7 @@ * @author Maxime Journot * @since 10.2 */ -public class Maneuver extends AbstractForceModel { +public class Maneuver implements ForceModel { /** The attitude to override during the maneuver, if set. */ private final AttitudeProvider attitudeOverride; @@ -105,6 +109,14 @@ public AttitudeProvider getAttitudeOverride() { return attitudeOverride; } + /** Get the control vector's cost type. + * @return control cost type + * @since 12.0 + */ + public Control3DVectorCostType getControl3DVectorCostType() { + return propulsionModel.getControl3DVectorCostType(); + } + /** Get the propulsion model. * @return the propulsion model */ @@ -144,7 +156,7 @@ public > void init(final FieldSpacecraftState< public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder) { // Get the parameters associated to the maneuver (from ForceModel) - final double[] parameters = getParameters(); + final double[] parameters = getParameters(s.getDate()); // If the maneuver is active, compute and add its contribution // Maneuver triggers are used to check if the maneuver is currently firing or not @@ -166,7 +178,7 @@ public > void addContribution(final FieldSpace final FieldTimeDerivativesEquations adder) { // Get the parameters associated to the maneuver (from ForceModel) - final T[] parameters = getParameters(s.getDate().getField()); + final T[] parameters = getParameters(s.getDate().getField(), s.getDate()); // If the maneuver is active, compute and add its contribution // Maneuver triggers are used to check if the maneuver is currently firing or not @@ -174,6 +186,8 @@ public > void addContribution(final FieldSpace if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) { // Compute thrust acceleration in inertial frame + // the acceleration method extracts the parameter in its core, that is why we call it with + // parameters and not extracted parameters adder.addNonKeplerianAcceleration(acceleration(s, parameters)); // Compute flow rate using the propulsion model @@ -191,12 +205,14 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) { // Attitude during maneuver - final Attitude maneuverAttitude = - attitudeOverride == null ? - s.getAttitude() : - attitudeOverride.getAttitude(s.getOrbit(), - s.getDate(), - s.getFrame()); + final Attitude maneuverAttitude; + if (attitudeOverride == null) { + maneuverAttitude = s.getAttitude(); + } else { + final Rotation rotation = attitudeOverride.getAttitudeRotation(s.getOrbit(), s.getDate(), s.getFrame()); + // use dummy rates to build full attitude as they should not be used + maneuverAttitude = new Attitude(s.getDate(), s.getFrame(), rotation, Vector3D.ZERO, Vector3D.ZERO); + } // Compute acceleration from propulsion model // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface @@ -216,12 +232,15 @@ public > FieldVector3D acceleration(final F if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) { // Attitude during maneuver - final FieldAttitude maneuverAttitude = - attitudeOverride == null ? - s.getAttitude() : - attitudeOverride.getAttitude(s.getOrbit(), - s.getDate(), - s.getFrame()); + final FieldAttitude maneuverAttitude; + if (attitudeOverride == null) { + maneuverAttitude = s.getAttitude(); + } else { + final FieldRotation rotation = attitudeOverride.getAttitudeRotation(s.getOrbit(), s.getDate(), s.getFrame()); + // use dummy rates to build full attitude as they should not be used + final FieldVector3D zeroVector3D = FieldVector3D.getZero(s.getDate().getField()); + maneuverAttitude = new FieldAttitude<>(s.getDate(), s.getFrame(), rotation, zeroVector3D, zeroVector3D); + } // Compute acceleration from propulsion model // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface @@ -234,16 +253,18 @@ public > FieldVector3D acceleration(final F /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { - // Event detectors are extracted from the maneuver triggers - return maneuverTriggers.getEventsDetectors(); + public Stream getEventDetectors() { + // Event detectors are extracted from both the maneuver triggers and the propulsion model + return Stream.concat(maneuverTriggers.getEventDetectors(), + propulsionModel.getEventDetectors()); } /** {@inheritDoc} */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { - // Event detectors are extracted from the maneuver triggers - return maneuverTriggers.getFieldEventsDetectors(field); + public > Stream> getFieldEventDetectors(final Field field) { + // Event detectors are extracted from both the maneuver triggers and the propulsion model + return Stream.concat(maneuverTriggers.getFieldEventDetectors(field), + propulsionModel.getFieldEventDetectors(field)); } @Override diff --git a/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java b/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java index c8cf1602c6..1cf853570a 100644 --- a/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.AdapterPropagator; import org.orekit.time.AbsoluteDate; @@ -125,7 +125,7 @@ public SmallManeuverAnalyticalModel(final SpacecraftState state0, final Frame fr final double[][] fullJacobian = new double[6][6]; j0 = new double[6][3]; final Orbit orbit0 = type.convertType(state0.getOrbit()); - orbit0.getJacobianWrtCartesian(PositionAngle.MEAN, fullJacobian); + orbit0.getJacobianWrtCartesian(PositionAngleType.MEAN, fullJacobian); for (int i = 0; i < j0.length; ++i) { System.arraycopy(fullJacobian[i], 3, j0[i], 0, 3); } @@ -172,7 +172,7 @@ public Frame getInertialFrame() { * @return orbit at t₁, taking the maneuver * into account if t₁ > t₀ * @see #apply(SpacecraftState) - * @see #getJacobian(Orbit, PositionAngle, double[][]) + * @see #getJacobian(Orbit, PositionAngleType, double[][]) */ public Orbit apply(final Orbit orbit1) { @@ -191,7 +191,7 @@ public Orbit apply(final Orbit orbit1) { * @return spacecraft state at t₁, taking the maneuver * into account if t₁ > t₀ * @see #apply(Orbit) - * @see #getJacobian(Orbit, PositionAngle, double[][]) + * @see #getJacobian(Orbit, PositionAngleType, double[][]) */ public SpacecraftState apply(final SpacecraftState state1) { @@ -224,13 +224,13 @@ private Orbit updateOrbit(final Orbit orbit1) { // convert current orbital state to Keplerian or equinoctial elements final double[] parameters = new double[6]; - type.mapOrbitToArray(type.convertType(orbit1), PositionAngle.MEAN, parameters, null); + type.mapOrbitToArray(type.convertType(orbit1), PositionAngleType.MEAN, parameters, null); for (int i = 0; i < delta.length; ++i) { parameters[i] += delta[i]; } // build updated orbit as Keplerian or equinoctial elements - return type.mapArrayToOrbit(parameters, null, PositionAngle.MEAN, + return type.mapArrayToOrbit(parameters, null, PositionAngleType.MEAN, orbit1.getDate(), orbit1.getMu(), orbit1.getFrame()); } @@ -240,19 +240,19 @@ private Orbit updateOrbit(final Orbit orbit1) { * The Jacobian matrix is a 6x4 matrix. Element jacobian[i][j] corresponds to * the partial derivative of orbital parameter i with respect to maneuver * parameter j. The rows order is the same order as used in {@link - * Orbit#getJacobianWrtCartesian(PositionAngle, double[][]) Orbit.getJacobianWrtCartesian} + * Orbit#getJacobianWrtCartesian(PositionAngleType, double[][]) Orbit.getJacobianWrtCartesian} * method. Columns (0, 1, 2) correspond to the velocity increment coordinates * (ΔVx, ΔVy, ΔVz) in the * inertial frame returned by {@link #getInertialFrame()}, and column 3 * corresponds to the maneuver date t₀. *

          * @param orbit1 original orbit at t₁, without maneuver - * @param positionAngle type of the position angle to use + * @param positionAngleType type of the position angle to use * @param jacobian placeholder 6x4 (or larger) matrix to be filled with the Jacobian, if matrix * is larger than 6x4, only the 6x4 upper left corner will be modified * @see #apply(Orbit) */ - public void getJacobian(final Orbit orbit1, final PositionAngle positionAngle, + public void getJacobian(final Orbit orbit1, final PositionAngleType positionAngleType, final double[][] jacobian) { final double dt = orbit1.getDate().durationFrom(state0.getDate()); @@ -283,13 +283,13 @@ public void getJacobian(final Orbit orbit1, final PositionAngle positionAngle, final double da = j0[0][0] * x + j0[0][1] * y + j0[0][2] * z; jacobian[5][3] += ksi * (jacobian[0][3] * dt - da); - if (orbit1.getType() != type || positionAngle != PositionAngle.MEAN) { + if (orbit1.getType() != type || positionAngleType != PositionAngleType.MEAN) { // convert to derivatives of Cartesian parameters final double[][] j2 = new double[6][6]; final double[][] pvJacobian = new double[6][4]; final Orbit updated = updateOrbit(orbit1); - updated.getJacobianWrtParameters(PositionAngle.MEAN, j2); + updated.getJacobianWrtParameters(PositionAngleType.MEAN, j2); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 4; ++j) { pvJacobian[i][j] = j2[i][0] * jacobian[0][j] + j2[i][1] * jacobian[1][j] + @@ -300,7 +300,7 @@ public void getJacobian(final Orbit orbit1, final PositionAngle positionAngle, // convert to derivatives of specified parameters final double[][] j3 = new double[6][6]; - orbit1.getType().convertType(updated).getJacobianWrtCartesian(positionAngle, j3); + orbit1.getType().convertType(updated).getJacobianWrtCartesian(positionAngleType, j3); for (int j = 0; j < 4; ++j) { for (int i = 0; i < 6; ++i) { jacobian[i][j] = j3[i][0] * pvJacobian[0][j] + j3[i][1] * pvJacobian[1][j] + @@ -325,9 +325,9 @@ private void evaluateJ0Dot() { // compute shifted Jacobians final double[][] j0m1 = new double[6][6]; - orbit.shiftedBy(-1 * dt).getJacobianWrtCartesian(PositionAngle.MEAN, j0m1); + orbit.shiftedBy(-1 * dt).getJacobianWrtCartesian(PositionAngleType.MEAN, j0m1); final double[][] j0p1 = new double[6][6]; - orbit.shiftedBy(+1 * dt).getJacobianWrtCartesian(PositionAngle.MEAN, j0p1); + orbit.shiftedBy(+1 * dt).getJacobianWrtCartesian(PositionAngleType.MEAN, j0p1); // evaluate derivative by finite differences for (int i = 0; i < j0Dot.length; ++i) { diff --git a/src/main/java/org/orekit/forces/maneuvers/jacobians/Duration.java b/src/main/java/org/orekit/forces/maneuvers/jacobians/Duration.java index 8c17eed1f5..0eb3cb6fab 100644 --- a/src/main/java/org/orekit/forces/maneuvers/jacobians/Duration.java +++ b/src/main/java/org/orekit/forces/maneuvers/jacobians/Duration.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -63,7 +63,7 @@ public String getName() { *

          */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !(state.hasAdditionalState(startName) && state.hasAdditionalState(stopName)); } diff --git a/src/main/java/org/orekit/forces/maneuvers/jacobians/MassDepletionDelay.java b/src/main/java/org/orekit/forces/maneuvers/jacobians/MassDepletionDelay.java index d587c05e87..0df1040adc 100644 --- a/src/main/java/org/orekit/forces/maneuvers/jacobians/MassDepletionDelay.java +++ b/src/main/java/org/orekit/forces/maneuvers/jacobians/MassDepletionDelay.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -78,13 +78,6 @@ public void init(final SpacecraftState initialState, final AbsoluteDate target) forward = target.isAfterOrEqualTo(initialState); } - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - /** {@inheritDoc} */ @Override public CombinedDerivatives combinedDerivatives(final SpacecraftState state) { @@ -96,14 +89,16 @@ public CombinedDerivatives combinedDerivatives(final SpacecraftState state) { if (forward == manageStart) { // current acceleration - final double[] parameters = maneuver.getParameters(); + final double[] parameters = maneuver.getParameters(state.getDate()); + // for the acceleration method we need all the span values of all the parameters driver + // as in the acceleration method an exctractParameter method is called final Vector3D acceleration = maneuver.acceleration(state, parameters); // we have acceleration Γ = F/m and m = m₀ - q (t - tₛ) // where m is current mass, m₀ is initial mass and tₛ is maneuver trigger time // a delay dtₛ on trigger time induces delaying mass depletion // we get: dΓ = -F/m² dm = -F/m² q dtₛ = -Γ q/m dtₛ - final double minusQ = maneuver.getPropulsionModel().getMassDerivatives(state, parameters); + final double minusQ = maneuver.getPropulsionModel().getMassDerivatives(state, maneuver.getParameters(state.getDate())); final double m = state.getMass(); final double ratio = minusQ / m; diff --git a/src/main/java/org/orekit/forces/maneuvers/jacobians/MedianDate.java b/src/main/java/org/orekit/forces/maneuvers/jacobians/MedianDate.java index 946554798a..f331c79795 100644 --- a/src/main/java/org/orekit/forces/maneuvers/jacobians/MedianDate.java +++ b/src/main/java/org/orekit/forces/maneuvers/jacobians/MedianDate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -63,7 +63,7 @@ public String getName() { *

          */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !(state.hasAdditionalState(startName) && state.hasAdditionalState(stopName)); } diff --git a/src/main/java/org/orekit/forces/maneuvers/jacobians/TriggerDate.java b/src/main/java/org/orekit/forces/maneuvers/jacobians/TriggerDate.java index 1f73e8b3e2..41a59019f8 100644 --- a/src/main/java/org/orekit/forces/maneuvers/jacobians/TriggerDate.java +++ b/src/main/java/org/orekit/forces/maneuvers/jacobians/TriggerDate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -182,7 +182,7 @@ public String getName() { *

          */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !(state.hasAdditionalState(stmName) && state.hasAdditionalState(massDepletionDelay.getName())); } @@ -256,7 +256,7 @@ public SpacecraftState resetState(final SpacecraftState state) { // get the acceleration near trigger time final SpacecraftState stateWhenFiring = state.shiftedBy((manageStart ? 2 : -2) * threshold); - final Vector3D acceleration = maneuver.acceleration(stateWhenFiring, maneuver.getParameters()); + final Vector3D acceleration = maneuver.acceleration(stateWhenFiring, maneuver.getParameters(state.getDate())); // initialize derivatives computation final double sign = (forward == manageStart) ? -1 : +1; diff --git a/src/main/java/org/orekit/forces/maneuvers/jacobians/package-info.java b/src/main/java/org/orekit/forces/maneuvers/jacobians/package-info.java index fe46f39739..8cc72c8e23 100644 --- a/src/main/java/org/orekit/forces/maneuvers/jacobians/package-info.java +++ b/src/main/java/org/orekit/forces/maneuvers/jacobians/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/maneuvers/package-info.java b/src/main/java/org/orekit/forces/maneuvers/package-info.java index dc321aff23..89c0babcc3 100644 --- a/src/main/java/org/orekit/forces/maneuvers/package-info.java +++ b/src/main/java/org/orekit/forces/maneuvers/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/AbstractConstantThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/AbstractConstantThrustPropulsionModel.java index dd03bcb159..a3335eaecb 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/AbstractConstantThrustPropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/AbstractConstantThrustPropulsionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,8 +20,10 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.forces.maneuvers.Control3DVectorCostType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; /** This abstract class simply serve as a container for a constant thrust maneuver. @@ -34,12 +36,18 @@ */ public abstract class AbstractConstantThrustPropulsionModel implements ThrustPropulsionModel { + /** Default control vector cost type. */ + static final Control3DVectorCostType DEFAULT_CONTROL_3D_VECTOR_COST_TYPE = Control3DVectorCostType.TWO_NORM; + /** Initial thrust vector (N) in S/C frame, when building the object. */ private final Vector3D initialThrustVector; /** Initial flow rate (kg/s), when building the object. */ private final double initialFlowRate; + /** Type of norm linking thrust vector to mass flow rate. */ + private final Control3DVectorCostType control3DVectorCostType; + /** User-defined name of the maneuver. * This String attribute is empty by default. * It is added as a prefix to the parameter drivers of the maneuver. @@ -55,22 +63,45 @@ public abstract class AbstractConstantThrustPropulsionModel implements ThrustPro * @param thrust initial thrust value (N) * @param isp initial isp value (s) * @param direction initial thrust direction in S/C frame + * @param control3DVectorCostType control cost type * @param name name of the maneuver + * @since 12.0 */ public AbstractConstantThrustPropulsionModel(final double thrust, final double isp, final Vector3D direction, + final Control3DVectorCostType control3DVectorCostType, final String name) { this.name = name; this.initialThrustVector = direction.normalize().scalarMultiply(thrust); - this.initialFlowRate = -thrust / (Constants.G0_STANDARD_GRAVITY * isp); + this.control3DVectorCostType = control3DVectorCostType; + this.initialFlowRate = -control3DVectorCostType.evaluate(initialThrustVector) / (Constants.G0_STANDARD_GRAVITY * isp); + } + + /** Constructor with default control cost type. + * @param thrust initial thrust value (N) + * @param isp initial isp value (s) + * @param direction initial thrust direction in S/C frame + * @param name name of the maneuver + */ + public AbstractConstantThrustPropulsionModel(final double thrust, + final double isp, + final Vector3D direction, + final String name) { + this(thrust, isp, direction, DEFAULT_CONTROL_3D_VECTOR_COST_TYPE, name); } + /** Get the initial thrust vector. + * @return the initial thrust vector + */ protected Vector3D getInitialThrustVector() { return initialThrustVector; } - protected double getInitialFlowrate() { + /** Get the initial flow rate. + * @return the initial flow rate + */ + protected double getInitialFlowRate() { return initialFlowRate; } @@ -80,36 +111,75 @@ public String getName() { return name; } + /** {@inheritDoc} */ + @Override + public Control3DVectorCostType getControl3DVectorCostType() { + return control3DVectorCostType; + } + /** Get the specific impulse. - * @return specific impulse (s). + * @return specific impulse (s), will throw exception if + * used on PDriver having several driven values, because + * in this case a date is needed. */ public double getIsp() { - final double thrust = getThrust(); final double flowRate = getFlowRate(); - return -thrust / (Constants.G0_STANDARD_GRAVITY * flowRate); + return -control3DVectorCostType.evaluate(getThrustVector()) / (Constants.G0_STANDARD_GRAVITY * flowRate); + } + + /** Get the specific impulse at given date. + * @param date date at which the Isp wants to be known + * @return specific impulse (s). + */ + public double getIsp(final AbsoluteDate date) { + final double flowRate = getFlowRate(date); + return -control3DVectorCostType.evaluate(getThrustVector(date)) / (Constants.G0_STANDARD_GRAVITY * flowRate); } /** Get the thrust direction in S/C frame. + * @param date date at which the direction wants to be known * @return the thrust direction in S/C frame */ + public Vector3D getDirection(final AbsoluteDate date) { + return getThrustVector(date).normalize(); + } + + /** Get the thrust direction in S/C frame. + * @return the thrust direction in S/C frame, will throw exception if + * used on PDriver having several driven values, because + * in this case a date is needed. + */ public Vector3D getDirection() { return getThrustVector().normalize(); } - /** Get the thrust value (N). - * @return the thrust value (N) + /** Get the thrust magnitude (N). + * @return the thrust value (N), will throw + * an exception if called of a driver having several + * values driven */ - public double getThrust() { + public double getThrustMagnitude() { return getThrustVector().getNorm(); } + /** Get the thrust magnitude (N) at given date. + * @param date date at which the thrust vector wants to be known, + * often the date parameter will not be important and can be whatever + * if the thrust parameter driver as only value estimated over the all + * orbit determination interval + * @return the thrust value (N) + */ + public double getThrustMagnitude(final AbsoluteDate date) { + return getThrustVector(date).getNorm(); + } + /** {@inheritDoc} * Here the thrust vector do not depend on current S/C state. */ @Override public Vector3D getThrustVector(final SpacecraftState s) { // Call the abstract function that do not depend on current S/C state - return getThrustVector(); + return getThrustVector(s.getDate()); } /** {@inheritDoc} @@ -118,7 +188,7 @@ public Vector3D getThrustVector(final SpacecraftState s) { @Override public double getFlowRate(final SpacecraftState s) { // Call the abstract function that do not depend on current S/C state - return getFlowRate(); + return getFlowRate(s.getDate()); } /** {@inheritDoc} @@ -157,16 +227,40 @@ public > T getFlowRate(final FieldSpacecraftSt /** Get the thrust vector in spacecraft frame (N). * Here it does not depend on current S/C state. - * @return thrust vector in spacecraft frame (N) + * @return thrust vector in spacecraft frame (N), + * will throw an exception if used on driver + * containing several value spans */ public abstract Vector3D getThrustVector(); + /** Get the thrust vector in spacecraft frame (N). + * Here it does not depend on current S/C state. + * @param date date at which the thrust vector wants to be known, + * often the date parameter will not be important and can be whatever + * if the thrust parameter driver as only value estimated over the all + * orbit determination interval + * @return thrust vector in spacecraft frame (N) + */ + public abstract Vector3D getThrustVector(AbsoluteDate date); + /** Get the flow rate (kg/s). * Here it does not depend on current S/C. * @return flow rate (kg/s) + * will throw an exception if used on driver + * containing several value spans */ public abstract double getFlowRate(); + /** Get the flow rate (kg/s). + * Here it does not depend on current S/C. + * @param date date at which the thrust vector wants to be known, + * often the date parameter will not be important and can be whatever + * if the thrust parameter driver as only value estimated over the all + * orbit determination interval + * @return flow rate (kg/s) + */ + public abstract double getFlowRate(AbsoluteDate date); + /** Get the thrust vector in spacecraft frame (N). * Here it does not depend on current S/C state. * @param parameters propulsion model parameters diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/BasicConstantThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/BasicConstantThrustPropulsionModel.java index aad5ead862..9cc5030bc3 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/BasicConstantThrustPropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/BasicConstantThrustPropulsionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,13 +18,15 @@ package org.orekit.forces.maneuvers.propulsion; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.utils.Constants; +import org.orekit.forces.maneuvers.Control3DVectorCostType; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; /** Constant thrust propulsion model with: @@ -67,20 +69,23 @@ public class BasicConstantThrustPropulsionModel extends AbstractConstantThrustPr /** Thrust direction in spacecraft frame. */ private final Vector3D direction; - /** Simple constructor. + /** Generic constructor. * @param thrust thrust (N) * @param isp isp (s) * @param direction direction in spacecraft frame + * @param control3DVectorCostType control cost type * @param name name of the maneuver + * @since 12.0 */ public BasicConstantThrustPropulsionModel(final double thrust, final double isp, final Vector3D direction, + final Control3DVectorCostType control3DVectorCostType, final String name) { - super(thrust, isp, direction, name); + super(thrust, isp, direction, control3DVectorCostType, name); this.direction = direction.normalize(); - final double initialFlowRate = -thrust / (Constants.G0_STANDARD_GRAVITY * isp); + final double initialFlowRate = super.getInitialFlowRate(); // Build the parameter drivers, using maneuver name as prefix this.thrustDriver = new ParameterDriver(name + THRUST, thrust, THRUST_SCALE, @@ -89,24 +94,56 @@ public BasicConstantThrustPropulsionModel(final double thrust, Double.NEGATIVE_INFINITY, 0.0 ); } + /** Simple constructor. + * @param thrust thrust (N) + * @param isp isp (s) + * @param direction direction in spacecraft frame + * @param name name of the maneuver + */ + public BasicConstantThrustPropulsionModel(final double thrust, + final double isp, + final Vector3D direction, + final String name) { + this(thrust, isp, direction, DEFAULT_CONTROL_3D_VECTOR_COST_TYPE, name); + } + /** {@inheritDoc} */ @Override public Vector3D getThrustVector() { // Thrust vector does not depend on spacecraft state for a constant maneuver. + // thrustDriver as only 1 value estimated over the whole time period + // by construction thrustDriver has only 1 value estimated over the all period + // that is why no argument is acceptable return direction.scalarMultiply(thrustDriver.getValue()); } + /** {@inheritDoc} */ + @Override + public Vector3D getThrustVector(final AbsoluteDate date) { + // Thrust vector does not depend on spacecraft state for a constant maneuver. + return direction.scalarMultiply(thrustDriver.getValue(date)); + } + /** {@inheritDoc} */ @Override public double getFlowRate() { - // Flow rate does not depend on spacecraft state for a constant maneuver. + // Thrust vector does not depend on spacecraft state for a constant maneuver. + // thrustDriver has only 1 value estimated over the whole time period + // by construction thrustDriver has only 1 value estimated over the all period + // that is why no argument is acceptable return flowRateDriver.getValue(); } + /** {@inheritDoc} */ + @Override + public double getFlowRate(final AbsoluteDate date) { + return flowRateDriver.getValue(date); + } + /** {@inheritDoc} */ @Override public List getParametersDrivers() { - return Arrays.asList(thrustDriver, flowRateDriver); + return Collections.unmodifiableList(Arrays.asList(thrustDriver, flowRateDriver)); } /** {@inheritDoc} */ @@ -125,7 +162,7 @@ public double getFlowRate(final double[] parameters) { /** {@inheritDoc} */ @Override public > FieldVector3D getThrustVector(final T[] parameters) { - return new FieldVector3D(parameters[0], direction); + return new FieldVector3D<>(parameters[0], direction); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/PolynomialThrustSegment.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/PolynomialThrustSegment.java new file mode 100644 index 0000000000..2b59c75ae6 --- /dev/null +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/PolynomialThrustSegment.java @@ -0,0 +1,80 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.forces.maneuvers.propulsion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.polynomials.PolynomialFunction; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** One polynomial segment of a thrust profile. + * @author Luc Maisonobe + * @since 12.0 + */ +public class PolynomialThrustSegment { + + /** Reference date. */ + private final AbsoluteDate referenceDate; + + /** Thrust along X direction (N). */ + private final PolynomialFunction xThrust; + + /** Thrust along Y direction (N). */ + private final PolynomialFunction yThrust; + + /** Thrust along Z direction (N). */ + private final PolynomialFunction zThrust; + + /** Simple constructor. + * @param referenceDate reference date of the polynomials + * @param xThrust thrust along X direction (N) + * @param yThrust thrust along Y direction (N) + * @param zThrust thrust along Z direction (N) + */ + public PolynomialThrustSegment(final AbsoluteDate referenceDate, + final PolynomialFunction xThrust, + final PolynomialFunction yThrust, + final PolynomialFunction zThrust) { + this.referenceDate = referenceDate; + this.xThrust = xThrust; + this.yThrust = yThrust; + this.zThrust = zThrust; + } + + /** Get thrust vector at a specified date. + * @param date date to consider + * @return thrust at {@code date} (N) + */ + public Vector3D getThrustVector(final AbsoluteDate date) { + final double dt = date.durationFrom(referenceDate); + return new Vector3D(xThrust.value(dt), yThrust.value(dt), zThrust.value(dt)); + } + + /** Get thrust vector at a specified date. + * @param type of the field elements + * @param date date to consider + * @return thrust at {@code date} (N) + */ + public > FieldVector3D getThrustVector(final FieldAbsoluteDate date) { + final T dt = date.durationFrom(referenceDate); + return new FieldVector3D<>(xThrust.value(dt), yThrust.value(dt), zThrust.value(dt)); + } + +} diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java new file mode 100644 index 0000000000..3f4ab599d0 --- /dev/null +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModel.java @@ -0,0 +1,207 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.forces.maneuvers.propulsion; + +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.FastMath; +import org.orekit.forces.maneuvers.Control3DVectorCostType; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.DateDetector; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldDateDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeStamped; +import org.orekit.utils.Constants; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; + +/** Thrust propulsion model based on segmented profile. + * @author Luc Maisonobe + * @since 12.0 + */ +public class ProfileThrustPropulsionModel implements ThrustPropulsionModel { + + /** Accuracy of switching events datation (s). */ + private static final double DATATION_ACCURACY = 1.0e-10; + + /** Thrust profile. */ + private final TimeSpanMap profile; + + /** Specific impulse. */ + private final double isp; + + /** Name of the maneuver. */ + private final String name; + + /** Type of norm linking thrust vector to mass flow rate. */ + private final Control3DVectorCostType control3DVectorCostType; + + /** Generic constructor. + * @param profile thrust profile (N) + * @param isp specific impulse (s) + * @param control3DVectorCostType control vector's cost type + * @param name name of the maneuver + */ + public ProfileThrustPropulsionModel(final TimeSpanMap profile, final double isp, + final Control3DVectorCostType control3DVectorCostType, final String name) { + this.name = name; + this.isp = isp; + this.profile = profile; + this.control3DVectorCostType = control3DVectorCostType; + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return name; + } + + /** {@inheritDoc} */ + @Override + public Control3DVectorCostType getControl3DVectorCostType() { + return control3DVectorCostType; + } + + /** {@inheritDoc} */ + @Override + public Vector3D getThrustVector(final SpacecraftState s) { + final PolynomialThrustSegment active = profile.get(s.getDate()); + return active == null ? Vector3D.ZERO : active.getThrustVector(s.getDate()); + } + + /** {@inheritDoc} */ + @Override + public double getFlowRate(final SpacecraftState s) { + return -control3DVectorCostType.evaluate(getThrustVector(s)) / (Constants.G0_STANDARD_GRAVITY * isp); + } + + /** {@inheritDoc} + *

          + * Here the thrust vector does not depend on parameters + *

          + */ + @Override + public Vector3D getThrustVector(final SpacecraftState s, final double[] parameters) { + return getThrustVector(s); + } + + /** {@inheritDoc} + *

          + * Here the flow rate does not depend on parameters + *

          + */ + public double getFlowRate(final SpacecraftState s, final double[] parameters) { + return getFlowRate(s); + } + + /** {@inheritDoc} + *

          + * Here the thrust vector does not depend on parameters + *

          + */ + public > FieldVector3D getThrustVector(final FieldSpacecraftState s, + final T[] parameters) { + final PolynomialThrustSegment active = profile.get(s.getDate().toAbsoluteDate()); + return active == null ? FieldVector3D.getZero(s.getDate().getField()) : active.getThrustVector(s.getDate()); + } + + /** {@inheritDoc} + *

          + * Here the flow rate does not depend on parameters + *

          + */ + public > T getFlowRate(final FieldSpacecraftState s, final T[] parameters) { + return control3DVectorCostType.evaluate(getThrustVector(s, parameters)).divide(-Constants.G0_STANDARD_GRAVITY * isp); + } + + /** {@inheritDoc}. + *

          + * The single detector returned triggers {@link org.hipparchus.ode.events.Action#RESET_DERIVATIVES} events + * at every {@link PolynomialThrustSegment thrust segments} boundaries. + *

          + */ + @Override + public Stream getEventDetectors() { + + final double shortest = shortestSegmentDuration(); + final DateDetector detector = new DateDetector(). + withMaxCheck(0.5 * shortest). + withMinGap(0.5 * shortest). + withThreshold(DATATION_ACCURACY). + withHandler((state, det, increasing) -> Action.RESET_DERIVATIVES); + for (TimeSpanMap.Transition transition = profile.getFirstTransition(); + transition != null; + transition = transition.next()) { + detector.addEventDate(transition.getDate()); + } + return Stream.of(detector); + } + + /** {@inheritDoc}. + *

          + * The single detector returned triggers {@link org.hipparchus.ode.events.Action#RESET_DERIVATIVES} events + * at every {@link PolynomialThrustSegment thrust segments} boundaries. + *

          + */ + @Override + public > Stream> getFieldEventDetectors(final Field field) { + final double shortest = shortestSegmentDuration(); + @SuppressWarnings("unchecked") + final FieldDateDetector detector = new FieldDateDetector<>(field, + (FieldTimeStamped[]) Array.newInstance(FieldTimeStamped.class, 0)). + withMaxCheck(0.5 * shortest). + withMinGap(0.5 * shortest). + withThreshold(field.getZero().newInstance(DATATION_ACCURACY)). + withHandler((state, det, increasing) -> Action.RESET_DERIVATIVES); + for (TimeSpanMap.Transition transition = profile.getFirstTransition(); + transition != null; + transition = transition.next()) { + detector.addEventDate(new FieldAbsoluteDate<>(field, transition.getDate())); + } + return Stream.of(detector); + } + + /** Compute the duration of the shortest segment. + * @return duration of the shortest segment + */ + private double shortestSegmentDuration() { + double shortest = Double.POSITIVE_INFINITY; + for (TimeSpanMap.Span span = profile.getFirstSpan(); + span != null; + span = span.next()) { + shortest = FastMath.min(shortest, span.getEnd().durationFrom(span.getStart())); + } + return shortest; + } + + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/PropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/PropulsionModel.java index d98e3dd04f..af884de881 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/PropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/PropulsionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,26 +17,30 @@ package org.orekit.forces.maneuvers.propulsion; -import java.util.Collections; -import java.util.List; +import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.FieldAttitude; +import org.orekit.forces.maneuvers.Control3DVectorCostType; import org.orekit.forces.maneuvers.Maneuver; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.EventDetectorsProvider; +import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** Generic interface for a propulsion model used in a {@link Maneuver}. * @author Maxime Journot * @since 10.2 */ -public interface PropulsionModel { +public interface PropulsionModel extends ParameterDriversProvider, EventDetectorsProvider { /** Initialization method. * Called in when Maneuver.init(...) is called (from ForceModel.init(...)) @@ -57,6 +61,18 @@ default > void init(FieldSpacecraftState in init(initialState.toSpacecraftState(), target.toAbsoluteDate()); } + /** {@inheritDoc}.*/ + @Override + default Stream getEventDetectors() { + return getEventDetectors(getParametersDrivers()); + } + + /** {@inheritDoc}.*/ + @Override + default > Stream> getFieldEventDetectors(Field field) { + return getFieldEventDetectors(field, getParametersDrivers()); + } + /** Get the acceleration of the spacecraft during maneuver and in maneuver frame. * @param s current spacecraft state * @param maneuverAttitude current attitude in maneuver @@ -73,8 +89,8 @@ default > void init(FieldSpacecraftState in * @return acceleration */ > FieldVector3D getAcceleration(FieldSpacecraftState s, - FieldAttitude maneuverAttitude, - T[] parameters); + FieldAttitude maneuverAttitude, + T[] parameters); /** Get the mass derivative (i.e. flow rate in kg/s) during maneuver. * @param s current spacecraft state @@ -90,15 +106,7 @@ > FieldVector3D getAcceleration(FieldSpacec * @return mass derivative in kg/s */ > T getMassDerivatives(FieldSpacecraftState s, - T[] parameters); - - /** Get the propulsion model parameter drivers. - * @return propulsion model parameter drivers - */ - default List getParametersDrivers() { - return Collections.emptyList(); - } - + T[] parameters); /** Get the maneuver name. * @return the maneuver name */ @@ -106,4 +114,10 @@ default String getName() { return ""; } + /** Get the control vector's cost type. + * @return control cost type + * @since 12.0 + */ + Control3DVectorCostType getControl3DVectorCostType(); + } diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java index de91dc775f..2f0242340e 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,7 @@ package org.orekit.forces.maneuvers.propulsion; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -25,6 +25,7 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; /** Thrust propulsion model with parameters (for estimation) represented by scale factors @@ -96,43 +97,56 @@ private Vector3D getThrustVector(final double scaleFactorX, /** {@inheritDoc} */ @Override public Vector3D getThrustVector() { + // scaleFactorThruster must be drivers with only 1 one value driven return getThrustVector(scaleFactorThrustXDriver.getValue(), scaleFactorThrustYDriver.getValue(), scaleFactorThrustZDriver.getValue()); } + /** {@inheritDoc} */ + @Override + public Vector3D getThrustVector(final AbsoluteDate date) { + return getThrustVector(scaleFactorThrustXDriver.getValue(date), + scaleFactorThrustYDriver.getValue(date), + scaleFactorThrustZDriver.getValue(date)); + } + /** {@inheritDoc} */ @Override public double getFlowRate() { - return getInitialFlowrate(); + return getInitialFlowRate(); + } + + /** {@inheritDoc} */ + @Override + public double getFlowRate(final AbsoluteDate date) { + return getInitialFlowRate(); } /** {@inheritDoc} */ @Override public List getParametersDrivers() { - final List drivers = new ArrayList<>(3); - drivers.add(scaleFactorThrustXDriver); - drivers.add(scaleFactorThrustYDriver); - drivers.add(scaleFactorThrustZDriver); - return Collections.unmodifiableList(drivers); + return Collections.unmodifiableList(Arrays.asList(scaleFactorThrustXDriver, + scaleFactorThrustYDriver, + scaleFactorThrustZDriver)); } /** {@inheritDoc} */ @Override - public Vector3D getThrustVector(final double parameters[]) { + public Vector3D getThrustVector(final double[] parameters) { return getThrustVector(parameters[0], parameters[1], parameters[2]); } /** {@inheritDoc} */ @Override public double getFlowRate(final double[] parameters) { - return getInitialFlowrate(); + return getInitialFlowRate(); } /** {@inheritDoc} */ @Override - public > FieldVector3D getThrustVector(final T parameters[]) { - return new FieldVector3D(parameters[0].multiply(getInitialThrustVector().getX()), + public > FieldVector3D getThrustVector(final T[] parameters) { + return new FieldVector3D<>(parameters[0].multiply(getInitialThrustVector().getX()), parameters[1].multiply(getInitialThrustVector().getY()), parameters[2].multiply(getInitialThrustVector().getZ())); } @@ -140,6 +154,6 @@ public > FieldVector3D getThrustVector(fina /** {@inheritDoc} */ @Override public > T getFlowRate(final T[] parameters) { - return parameters[0].getField().getZero().add(getInitialFlowrate()); + return parameters[0].getField().getZero().add(getInitialFlowRate()); } } diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustDirectionAndAttitudeProvider.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustDirectionAndAttitudeProvider.java index 98cf2b794d..33b9df96b6 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustDirectionAndAttitudeProvider.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustDirectionAndAttitudeProvider.java @@ -25,7 +25,7 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; -import org.orekit.frames.LOFType; +import org.orekit.frames.LOF; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinatesProvider; @@ -34,13 +34,13 @@ /** * This class is used in to both manage the attitude of the satellite and the * direction of thrust. - * + *

          * It is used in ConfigurableLowThrustManeuver to set the spacecraft attitude * according to the expected thrust direction. - * + *

          * The direction can be variable or fixed, defined in the spaceraft frame, a * Local Orbital Frame or a user frame. - * + *

          * It is also possible to use an external attitude provider. * * @author Mikael Fillastre @@ -56,7 +56,7 @@ public class ThrustDirectionAndAttitudeProvider implements AttitudeProvider { private static final String FIELD_NAME_DIRECTION_FRAME = "thrustDirectionFrame"; /** Field name for error message. */ - private static final String FIELD_NAME_LOF_TYPE = "thrustDirectionLofType"; + private static final String FIELD_NAME_LOF_TYPE = "thrustDirectionLof"; /** Types, see builders for details. */ private enum ThrustDirectionAndAttitudeProviderType { @@ -94,9 +94,9 @@ private enum ThrustDirectionAndAttitudeProviderType { private final Frame thrustDirectionFrame; /** - * Local Orbital Frame type, for DIRECTION_IN_LOF type. Set to null otherwise. + * Local Orbital Frame, for DIRECTION_IN_LOF local orbital frame. Set to null otherwise. */ - private final LOFType thrustDirectionLofType; + private final LOF thrustDirectionLof; /** * Internal constructor. @@ -109,17 +109,17 @@ private enum ThrustDirectionAndAttitudeProviderType { * otherwise. * @param thrusterAxisInSatelliteFrame Thruster axis in satellite frame * @param frame Reference frame for thrust direction - * @param thrustDirectionLofType Local Orbital Frame type, for DIRECTION_IN_LOF type + * @param thrustDirectionLof Local Orbital Frame, for DIRECTION_IN_LOF local orbital frame * (set to null otherwise) */ private ThrustDirectionAndAttitudeProvider(final ThrustDirectionAndAttitudeProviderType type, final AttitudeProvider attitudeProvider, final ThrustDirectionProvider variableDirectionInFrame, - final Vector3D thrusterAxisInSatelliteFrame, final Frame frame, final LOFType thrustDirectionLofType) { + final Vector3D thrusterAxisInSatelliteFrame, final Frame frame, final LOF thrustDirectionLof) { this.type = type; this.attitudeProvider = attitudeProvider; this.variableDirectionInFrame = variableDirectionInFrame; this.thrustDirectionFrame = frame; - this.thrustDirectionLofType = thrustDirectionLofType; + this.thrustDirectionLof = thrustDirectionLof; this.thrusterAxisInSatelliteFrame = thrusterAxisInSatelliteFrame; } @@ -191,18 +191,18 @@ public static ThrustDirectionAndAttitudeProvider buildFromDirectionInFrame(final * Build a ThrustDirectionAndAttitudeProvider by a variable direction in a Local * Orbital Frame. * - * @param thrustDirectionLofType Local Orbital Frame type + * @param thrustDirectionLof local orbital frame * @param variableDirectionInFrame the object providing the thrust direction * @param thrusterAxisInSatelliteFrame thruster axis in satellite frame * @return a new instance */ - public static ThrustDirectionAndAttitudeProvider buildFromDirectionInLOF(final LOFType thrustDirectionLofType, + public static ThrustDirectionAndAttitudeProvider buildFromDirectionInLOF(final LOF thrustDirectionLof, final ThrustDirectionProvider variableDirectionInFrame, final Vector3D thrusterAxisInSatelliteFrame) { final ThrustDirectionAndAttitudeProvider obj = new ThrustDirectionAndAttitudeProvider( ThrustDirectionAndAttitudeProviderType.DIRECTION_IN_LOF, null, variableDirectionInFrame, - thrusterAxisInSatelliteFrame, null, thrustDirectionLofType); + thrusterAxisInSatelliteFrame, null, thrustDirectionLof); checkParameterNotNull(variableDirectionInFrame, FIELD_NAME_VARIABLE_DIRECTION, obj.type); - checkParameterNotNull(thrustDirectionLofType, FIELD_NAME_LOF_TYPE, obj.type); + checkParameterNotNull(thrustDirectionLof, FIELD_NAME_LOF_TYPE, obj.type); return obj; } @@ -251,7 +251,7 @@ protected Attitude getAttitudeFromFrame(final PVCoordinatesProvider pvProv, fina if (type.equals(ThrustDirectionAndAttitudeProviderType.DIRECTION_IN_FRAME)) { inertial2ThrusterFrame = frame.getStaticTransformTo(thrustDirectionFrame, date).getRotation(); } else { // LOF - inertial2ThrusterFrame = thrustDirectionLofType.rotationFromInertial(pvProv.getPVCoordinates(date, frame)); + inertial2ThrusterFrame = thrustDirectionLof.rotationFromInertial(date, pvProv.getPVCoordinates(date, frame)); } final Vector3D thrustDirection = variableDirectionInFrame.computeThrustDirection(pvProv, date, frame); diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustPropulsionModel.java index 983170e8f2..b75fd4c064 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustPropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/ThrustPropulsionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Precision; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.FieldAttitude; import org.orekit.propagation.FieldSpacecraftState; @@ -37,25 +38,23 @@ public interface ThrustPropulsionModel extends PropulsionModel { * @return specific impulse (s). */ default double getIsp(SpacecraftState s) { - final double thrust = getThrust(s); final double flowRate = getFlowRate(s); - return -thrust / (Constants.G0_STANDARD_GRAVITY * flowRate); + return -getControl3DVectorCostType().evaluate(getThrustVector(s)) / (Constants.G0_STANDARD_GRAVITY * flowRate); } /** Get the thrust direction in spacecraft frame. + *

          + * Return a zero vector if there is no thrust for given spacecraft state. * @param s current spacecraft state * @return thrust direction in spacecraft frame */ default Vector3D getDirection(SpacecraftState s) { - return getThrustVector(s).normalize(); - } - - /** Get the thrust norm (N). - * @param s current spacecraft state - * @return thrust norm (N) - */ - default double getThrust(SpacecraftState s) { - return getThrustVector(s).getNorm(); + final Vector3D thrustVector = getThrustVector(s); + final double norm = thrustVector.getNorm(); + if (norm <= Precision.EPSILON) { + return Vector3D.ZERO; + } + return thrustVector.scalarMultiply(1. / norm); } /** Get the thrust vector in spacecraft frame (N). @@ -110,6 +109,9 @@ default Vector3D getAcceleration(SpacecraftState s, final Vector3D thrustVector = getThrustVector(s, parameters); final double thrust = thrustVector.getNorm(); + if (thrust == 0) { + return Vector3D.ZERO; + } final Vector3D direction = thrustVector.normalize(); // Compute thrust acceleration in inertial frame @@ -130,13 +132,16 @@ default > FieldVector3D getAcceleration(Fie // Extract thrust & direction from thrust vector final FieldVector3D thrustVector = getThrustVector(s, parameters); final T thrust = thrustVector.getNorm(); + if (thrust.isZero()) { + return FieldVector3D.getZero(s.getDate().getField()); + } final FieldVector3D direction = thrustVector.normalize(); // Compute thrust acceleration in inertial frame // It seems under-efficient to rotate direction and apply thrust // instead of just rotating the whole thrust vector itself. // However it has to be done that way to avoid numerical discrepancies with legacy tests. - return new FieldVector3D<>(s.getMass().reciprocal().multiply(thrust), + return new FieldVector3D<>(thrust.divide(s.getMass()), maneuverAttitude.getRotation().applyInverseTo(direction)); } diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/package-info.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/package-info.java index 737d166c8e..8ba477e9eb 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/package-info.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/AbstractManeuverTriggers.java b/src/main/java/org/orekit/forces/maneuvers/trigger/AbstractManeuverTriggers.java index 9cc6566ac0..7c15e5e7d7 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/AbstractManeuverTriggers.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/AbstractManeuverTriggers.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -61,9 +61,7 @@ public void init(final SpacecraftState initialState, final AbsoluteDate target) forward = target.isAfterOrEqualTo(initialState); firings = new TimeSpanMap<>(Boolean.FALSE); - for (final ManeuverTriggersResetter r : resetters) { - r.init(initialState, target); - } + initializeResetters(initialState, target); if (isFiringOnInitialState(initialState, forward)) { if (forward) { @@ -129,18 +127,14 @@ public TimeSpanMap getFirings() { return firings; } - /** Add a resetter. - * @param resetter resetter to add - */ + /** {@inheritDoc} */ + @Override public void addResetter(final ManeuverTriggersResetter resetter) { resetters.add(resetter); } - /** Add a resetter. - * @param field field to which the state belongs - * @param resetter resetter to add - * @param type of the field elements - */ + /** {@inheritDoc} */ + @Override public > void addResetter(final Field field, final FieldManeuverTriggersResetter resetter) { // check if we already have resetters for this field diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/DateBasedManeuverTriggers.java b/src/main/java/org/orekit/forces/maneuvers/trigger/DateBasedManeuverTriggers.java index 3aaa49d97c..b2f20a73d2 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/DateBasedManeuverTriggers.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/DateBasedManeuverTriggers.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,13 +17,13 @@ package org.orekit.forces.maneuvers.trigger; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.util.FastMath; import org.orekit.propagation.events.FieldAbstractDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.events.FieldParameterDrivenDateIntervalDetector; import org.orekit.propagation.events.ParameterDrivenDateIntervalDetector; import org.orekit.time.AbsoluteDate; @@ -78,10 +78,10 @@ public DateBasedManeuverTriggers(final String name, final AbsoluteDate date, fin private static ParameterDrivenDateIntervalDetector createDetector(final String prefix, final AbsoluteDate date, final double duration) { if (duration >= 0) { return new ParameterDrivenDateIntervalDetector(prefix, date, date.shiftedBy(duration)). - withMaxCheck(FastMath.max(MIN_MAX_CHECK, duration)); + withMaxCheck(FastMath.max(MIN_MAX_CHECK, duration)); } else { return new ParameterDrivenDateIntervalDetector(prefix, date.shiftedBy(duration), date). - withMaxCheck(FastMath.max(MIN_MAX_CHECK, -duration)); + withMaxCheck(FastMath.max(MIN_MAX_CHECK, -duration)); } } @@ -115,13 +115,13 @@ public double getDuration() { /** {@inheritDoc} */ @Override - protected , S extends CalculusFieldElement> - FieldAbstractDetector convertIntervalDetector(final Field field, final ParameterDrivenDateIntervalDetector detector) { + protected , S extends CalculusFieldElement> FieldAbstractDetector convertIntervalDetector(final Field field, + final ParameterDrivenDateIntervalDetector detector) { final FieldParameterDrivenDateIntervalDetector fd = new FieldParameterDrivenDateIntervalDetector(field, "", - detector.getStartDriver().getBaseDate(), - detector.getStopDriver().getBaseDate()); + detector.getStartDriver().getBaseDate(), + detector.getStopDriver().getBaseDate()); fd.getStartDriver().setName(detector.getStartDriver().getName()); fd.getStopDriver().setName(detector.getStopDriver().getName()); fd.getMedianDriver().setName(detector.getMedianDriver().getName()); @@ -136,10 +136,9 @@ FieldAbstractDetector convertIntervalDetector(final Field field, final /** {@inheritDoc} */ @Override public List getParametersDrivers() { - return Arrays.asList(getFiringIntervalDetector().getStartDriver(), - getFiringIntervalDetector().getStopDriver(), - getFiringIntervalDetector().getMedianDriver(), - getFiringIntervalDetector().getDurationDriver()); + return Collections.unmodifiableList(Arrays.asList(getFiringIntervalDetector().getStartDriver(), + getFiringIntervalDetector().getStopDriver(), + getFiringIntervalDetector().getMedianDriver(), + getFiringIntervalDetector().getDurationDriver())); } - } diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/EventBasedManeuverTriggers.java b/src/main/java/org/orekit/forces/maneuvers/trigger/EventBasedManeuverTriggers.java deleted file mode 100644 index 85fb6fbde7..0000000000 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/EventBasedManeuverTriggers.java +++ /dev/null @@ -1,349 +0,0 @@ -/* Copyright 2020 Exotrail - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * Exotrail licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.forces.maneuvers.trigger; - -import java.util.stream.Stream; - -import org.hipparchus.Field; -import org.hipparchus.CalculusFieldElement; -import org.hipparchus.ode.events.Action; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.AbstractDetector; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; -import org.orekit.propagation.events.handlers.EventHandler; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; - -/** - * Maneuver triggers based on start and stop detectors. This allow a succession - * of burn interval. The thruster starts firing when the start detector becomes - * positive. The thruster stops firing when the stop detector becomes positive. - * The 2 detectors should not be positive at the same time. A date detector is - * not suited as it does not delimit an interval. They can be both negative at - * the same time. - * @author Mikael Fillastre - * @author Andrea Fiorentino - * @since 10.2 - */ -public class EventBasedManeuverTriggers implements ManeuverTriggers, EventHandler { - - /** Detector to start firing, only detect increasing sign change. */ - private final AbstractDetector startFiringDetector; - /** - * Detector to stop firing, only detect increasing sign change. e.g. it can be a - * negate detector of the start detector - */ - private final AbstractDetector stopFiringDetector; - - /** Flag for allowing backward propagation. */ - private final boolean allowBackwardPropagation; - - /** - * Flag for init method, called several times : force models + each detector. - */ - private boolean initialized; - - /** Triggered date of engine start. */ - private AbsoluteDate triggeredStart; - - /** Triggered date of engine stop. */ - private AbsoluteDate triggeredEnd; - - /** Propagation direction. */ - private boolean forward; - - /** - * Constructor. - *

          - * This legacy constructor forbids backward propagation. - *

          - * @param startFiringDetector Detector to start firing, only detect increasing - * sign change - * @param stopFiringDetector Detector to stop firing, only detect increasing - * sign change. e.g. it can be a negate detector of - * the start detector. - */ - public EventBasedManeuverTriggers(final AbstractDetector startFiringDetector, - final AbstractDetector stopFiringDetector) { - this(startFiringDetector, stopFiringDetector, false); - } - - /** - * Constructor. - * @param startFiringDetector Detector to start firing, only detect increasing - * sign change - * @param stopFiringDetector Detector to stop firing, only detect increasing - * sign change. e.g. it can be a negate detector of - * the start detector. - * @param allowBackwardPropagation if true, backward propagation is allowed - * @since 11.1 - */ - public EventBasedManeuverTriggers(final AbstractDetector startFiringDetector, - final AbstractDetector stopFiringDetector, - final boolean allowBackwardPropagation) { - if (startFiringDetector == null) { - throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "stopFiringDetector", - EventBasedManeuverTriggers.class.getSimpleName()); - } - if (stopFiringDetector == null) { - throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "startFiringDetector", - EventBasedManeuverTriggers.class.getSimpleName()); - } - this.startFiringDetector = startFiringDetector.withHandler(this); - this.stopFiringDetector = stopFiringDetector.withHandler(this); - this.allowBackwardPropagation = allowBackwardPropagation; - this.triggeredStart = null; - this.triggeredEnd = null; - this.initialized = false; - this.forward = true; - - } - - /** - * Getter for the start firing detector. - * @return Detectors to start firing, - */ - public AbstractDetector getStartFiringDetector() { - return startFiringDetector; - } - - /** - * Getter for the stop firing detector. - * @return Detectors to stop firing - */ - public AbstractDetector getStopFiringDetector() { - return stopFiringDetector; - } - - /** {@inheritDoc} */ - @Override - public void init(final SpacecraftState initialState, final AbsoluteDate target) { - - if (!initialized) { - - initialized = true; - forward = target.isAfterOrEqualTo(initialState); - if (!forward && !allowBackwardPropagation) { - // backward propagation was forbidden - throw new OrekitException(OrekitMessages.BACKWARD_PROPAGATION_NOT_ALLOWED); - } - startFiringDetector.init(initialState, target); - stopFiringDetector.init(initialState, target); - - checkInitialFiringState(initialState); - - } // multiples calls to init : because it is a force model and by each detector - } - - /** - * Method to set the firing state on initialization. can be overloaded by sub - * classes. - * - * @param initialState initial spacecraft state - */ - protected void checkInitialFiringState(final SpacecraftState initialState) { - if (isFiringOnInitialState(initialState)) { - setFiring(true, initialState.getDate()); - } - } - - /** - * Method to check if the thruster is firing on initialization. can be called by - * sub classes - * - * @param initialState initial spacecraft state - * @return true if firing - */ - protected boolean isFiringOnInitialState(final SpacecraftState initialState) { - // set the initial value of firing - final double insideThrustArcG = getStartFiringDetector().g(initialState); - boolean isInsideThrustArc = false; - - if (insideThrustArcG == 0) { - // bound of arc - // check state for the next second (which can be forward or backward) - final double nextSecond = forward ? 1 : -1; - final double nextValue = getStartFiringDetector().g(initialState.shiftedBy(nextSecond)); - isInsideThrustArc = nextValue > 0; - } else { - isInsideThrustArc = insideThrustArcG > 0; - } - return isInsideThrustArc; - } - - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.of(getStartFiringDetector(), getStopFiringDetector()); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - // not implemented, it depends on the input detectors - throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED, - "EventBasedManeuverTriggers.getFieldEventsDetectors"); - } - - /** - * Set the firing start or end date depending on the firing flag. There is no - * effect if the firing state is not changing. - * @param firing true to start a maneuver, false to stop - * @param date date of event - */ - public void setFiring(final boolean firing, final AbsoluteDate date) { - if (forward) { - if (firing) { - if (!date.equals(triggeredEnd)) { - triggeredStart = date; - triggeredEnd = null; - } // else no gap between stop and start, can not handle correctly : skip it - } else { - triggeredEnd = date; - } - } else { // backward propagation - if (firing) { // start firing by end date - if (!date.equals(triggeredStart)) { - triggeredEnd = date; - triggeredStart = null; - } // else no gap between stop and start, can not handle correctly : skip it - } else { - triggeredStart = date; - } - } - } - - /** {@inheritDoc} */ - @Override - public boolean isFiring(final AbsoluteDate date, final double[] parameters) { - // Firing state does not depend on a parameter driver here - return isFiring(date); - } - - /** {@inheritDoc} */ - @Override - public > boolean isFiring(final FieldAbsoluteDate date, final T[] parameters) { - // Firing state does not depend on a parameter driver here - return isFiring(date.toAbsoluteDate()); - } - - /** {@inheritDoc} */ - @Override - public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { - Action action = Action.CONTINUE; // default not taken into account - final boolean detectorManaged = getEventsDetectors().anyMatch(managedDetector -> managedDetector.equals(detector)); - if (detectorManaged) { - if (increasing) { - action = Action.RESET_EVENTS; - if (forward) { - if (detector.equals(startFiringDetector)) { // start of firing arc - setFiring(true, s.getDate()); - action = Action.RESET_DERIVATIVES; - } else if (detector.equals(stopFiringDetector)) { // end of firing arc - setFiring(false, s.getDate()); - action = Action.RESET_DERIVATIVES; - } - } else { // backward propagation. We could write a code on 3 lines but that would be - // harder to understand and debug. So we do prefer explicit code - if (detector.equals(startFiringDetector)) { // end of firing arc - setFiring(false, s.getDate()); - action = Action.RESET_DERIVATIVES; - } else if (detector.equals(stopFiringDetector)) { // start of firing arc - setFiring(true, s.getDate()); - action = Action.RESET_DERIVATIVES; - } - } - } - } - return action; - } - - /** - * Check if maneuvering is on. - * - * @param date current date - * @return true if maneuver is on at this date - */ - public boolean isFiring(final AbsoluteDate date) { - if (forward) { - if (triggeredStart == null) { - // explicitly ignores state date, as propagator did not allow us to introduce - // discontinuity - return false; - } else if (date.isBefore(triggeredStart)) { - // we are unambiguously before maneuver start - // robustness, we should not pass here - return false; - } else { - // after start date - if (triggeredEnd == null) { - // explicitly ignores state date, as propagator did not allow us to introduce - // discontinuity - return true; - } else if (date.isBefore(triggeredEnd)) { - // we are unambiguously before maneuver end - // robustness, we should not pass here - return true; - } else { - // we are at or after maneuver end - return false; - } - } - } else { // backward propagation, start firing by triggeredEnd - if (triggeredEnd == null) { - // explicitly ignores state date, as propagator did not allow us to introduce - // discontinuity - return false; - } else if (date.isAfter(triggeredEnd)) { - // we are unambiguously after maneuver end - return false; - } else { - if (triggeredStart == null) { - // explicitly ignores state date, as propagator did not allow us to introduce - // discontinuity - return true; - } else if (date.isAfter(triggeredStart)) { - // we are unambiguously after maneuver start - return true; - } else { - // we are at or before maneuver start - return false; - } - } - } - } - - /** - * Getter for the triggered date of engine stop. - * @return Triggered date of engine stop - */ - public AbsoluteDate getTriggeredEnd() { - return triggeredEnd; - } - - /** - * Getter triggered date of engine start. - * @return Triggered date of engine start - */ - public AbsoluteDate getTriggeredStart() { - return triggeredStart; - } - -} diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/FieldManeuverTriggersResetter.java b/src/main/java/org/orekit/forces/maneuvers/trigger/FieldManeuverTriggersResetter.java index 31e7967d10..838b3b1457 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/FieldManeuverTriggersResetter.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/FieldManeuverTriggersResetter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/IntervalEventTrigger.java b/src/main/java/org/orekit/forces/maneuvers/trigger/IntervalEventTrigger.java index 71e33f6678..067cc07006 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/IntervalEventTrigger.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/IntervalEventTrigger.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,7 @@ import org.orekit.propagation.events.AbstractDetector; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldAbstractDetector; +import org.orekit.propagation.events.FieldAdaptableInterval; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.FieldEventHandler; @@ -110,12 +111,12 @@ protected boolean isFiringOnInitialState(final SpacecraftState initialState, fin /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { return Stream.of(firingIntervalDetector); } /** {@inheritDoc} */ - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { @SuppressWarnings("unchecked") FieldEventDetector fd = (FieldEventDetector) cached.get(field); @@ -130,7 +131,7 @@ public > Stream> getFiel /** Convert a detector and set up check interval, threshold and new handler. *

          - * This method is not inlined in {@link #getFieldEventsDetectors(Field)} because the + * This method is not inlined in {@link #getFieldEventDetectors(Field)} because the * parameterized types confuses the Java compiler. *

          * @param field field to which the state belongs @@ -140,15 +141,16 @@ public > Stream> getFiel */ private , S extends CalculusFieldElement> D convertAndSetUpHandler(final Field field) { final FieldAbstractDetector converted = convertIntervalDetector(field, firingIntervalDetector); + final FieldAdaptableInterval maxCheck = s -> firingIntervalDetector.getMaxCheckInterval().currentInterval(s.toSpacecraftState()); return converted. - withMaxCheck(field.getZero().newInstance(firingIntervalDetector.getMaxCheckInterval())). + withMaxCheck(maxCheck). withThreshold(field.getZero().newInstance(firingIntervalDetector.getThreshold())). withHandler(new FieldHandler<>()); } /** Convert a primitive firing intervals detector into a field firing intervals detector. *

          - * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(CalculusFieldElement) withMaxCheck}, + * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(FieldAdaptableInterval) withMaxCheck}, * {@link FieldAbstractDetector#withThreshold(CalculusFieldElement) withThreshold}, or * {@link FieldAbstractDetector#withHandler(org.orekit.propagation.events.handlers.FieldEventHandler) withHandler} * in the converted detector, this will be done by caller. @@ -176,25 +178,25 @@ private , S extends CalculusFieldElement type of the field elements * @return converted firing intervals detector */ - protected abstract , S extends CalculusFieldElement> + protected abstract , S extends CalculusFieldElement> FieldAbstractDetector convertIntervalDetector(Field field, T detector); /** Local handler for both start and stop triggers. */ - private class Handler implements EventHandler { + private class Handler implements EventHandler { /** Propagation direction. */ private boolean forward; /** {@inheritDoc} */ @Override - public void init(final SpacecraftState initialState, final AbsoluteDate target, final T detector) { + public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) { forward = target.isAfterOrEqualTo(initialState); initializeResetters(initialState, target); } /** {@inheritDoc} */ @Override - public Action eventOccurred(final SpacecraftState s, final T detector, final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { if (forward) { getFirings().addValidAfter(increasing, s.getDate(), false); } else { @@ -206,7 +208,7 @@ public Action eventOccurred(final SpacecraftState s, final T detector, final boo /** {@inheritDoc} */ @Override - public SpacecraftState resetState(final T detector, final SpacecraftState oldState) { + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { return applyResetters(oldState); } @@ -215,7 +217,7 @@ public SpacecraftState resetState(final T detector, final SpacecraftState oldSta /** Local handler for both start and stop triggers. * @param type of the field elements */ - private class FieldHandler, S extends CalculusFieldElement> implements FieldEventHandler { + private class FieldHandler, S extends CalculusFieldElement> implements FieldEventHandler { /** Propagation direction. */ private boolean forward; @@ -224,14 +226,14 @@ private class FieldHandler, S extends CalculusFi @Override public void init(final FieldSpacecraftState initialState, final FieldAbsoluteDate target, - final D detector) { + final FieldEventDetector detector) { forward = target.isAfterOrEqualTo(initialState); initializeResetters(initialState, target); } /** {@inheritDoc} */ @Override - public Action eventOccurred(final FieldSpacecraftState s, final D detector, final boolean increasing) { + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { if (forward) { getFirings().addValidAfter(increasing, s.getDate().toAbsoluteDate(), false); } else { @@ -243,7 +245,7 @@ public Action eventOccurred(final FieldSpacecraftState s, final D detector, f /** {@inheritDoc} */ @Override - public FieldSpacecraftState resetState(final D detector, final FieldSpacecraftState oldState) { + public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) { return applyResetters(oldState); } diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggers.java b/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggers.java index 65bfb119fd..e9f140e886 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggers.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggers.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,25 +17,21 @@ package org.orekit.forces.maneuvers.trigger; -import java.util.Collections; -import java.util.List; -import java.util.stream.Stream; - import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.orekit.forces.maneuvers.Maneuver; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.EventDetectorsProvider; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; -/** Generic interface for the maneuver triggers used in a {@link org.orekit.forces.maneuvers.Maneuver}. +/** Generic interface for the maneuver triggers used in a {@link Maneuver}. * @author Maxime Journot * @since 10.2 */ -public interface ManeuverTriggers { +public interface ManeuverTriggers extends ParameterDriversProvider, EventDetectorsProvider { /** Initialization method called at propagation start. *

          @@ -61,18 +57,6 @@ default > void init(FieldSpacecraftState in init(initialState.toSpacecraftState(), target.toAbsoluteDate()); } - /** Get the event detectors associated with the triggers. - * @return the event detectors - */ - Stream getEventsDetectors(); - - /** Get the event detectors associated with the triggers. - * @param field field to which the state belongs - * @param type of the field elements - * @return the event detectors - */ - > Stream> getFieldEventsDetectors(Field field); - /** Find out if the maneuver is firing or not. * @param date current date * @param parameters maneuver triggers parameters @@ -88,17 +72,22 @@ default > void init(FieldSpacecraftState in */ > boolean isFiring(FieldAbsoluteDate date, T[] parameters); - /** Get the maneuver triggers parameter drivers. - * @return maneuver triggers parameter drivers - */ - default List getParametersDrivers() { - return Collections.emptyList(); - } - /** Get the maneuver name. * @return the maneuver name */ default String getName() { return ""; } + + /** Add a resetter. + * @param resetter resetter to add + */ + void addResetter(ManeuverTriggersResetter resetter); + + /** Add a resetter. + * @param field field to which the state belongs + * @param resetter resetter to add + * @param type of the field elements + */ + > void addResetter(Field field, FieldManeuverTriggersResetter resetter); } diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggersResetter.java b/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggersResetter.java index e3feabd3dd..251df283f8 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggersResetter.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/ManeuverTriggersResetter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/StartStopEventsTrigger.java b/src/main/java/org/orekit/forces/maneuvers/trigger/StartStopEventsTrigger.java index 89199c9c2a..98f10c9530 100644 --- a/src/main/java/org/orekit/forces/maneuvers/trigger/StartStopEventsTrigger.java +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/StartStopEventsTrigger.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,7 @@ import org.orekit.propagation.events.AbstractDetector; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldAbstractDetector; +import org.orekit.propagation.events.FieldAdaptableInterval; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.FieldEventHandler; @@ -106,6 +107,14 @@ public O getStopDetector() { return stopDetector; } + /** {@inheritDoc} */ + @Override + public void init(final SpacecraftState initialState, final AbsoluteDate target) { + startDetector.init(initialState, target); + stopDetector.init(initialState, target); + super.init(initialState, target); + } + /** {@inheritDoc} */ @Override protected boolean isFiringOnInitialState(final SpacecraftState initialState, final boolean isForward) { @@ -152,13 +161,13 @@ protected boolean isFiringOnInitialState(final SpacecraftState initialState, fin /** {@inheritDoc} */ @Override - public Stream getEventsDetectors() { + public Stream getEventDetectors() { return Stream.of(startDetector, stopDetector); } /** {@inheritDoc} */ @Override - public > Stream> getFieldEventsDetectors(final Field field) { + public > Stream> getFieldEventDetectors(final Field field) { // get the field version of the start detector @SuppressWarnings("unchecked") @@ -182,7 +191,7 @@ public > Stream> getFiel /** Convert a detector and set up new handler. *

          - * This method is not inlined in {@link #getFieldEventsDetectors(Field)} because the + * This method is not inlined in {@link #getFieldEventDetectors(Field)} because the * parameterized types confuses the Java compiler. *

          * @param field field to which the state belongs @@ -192,15 +201,16 @@ public > Stream> getFiel */ private , S extends CalculusFieldElement> D convertAndSetUpStartHandler(final Field field) { final FieldAbstractDetector converted = convertStartDetector(field, startDetector); + final FieldAdaptableInterval maxCheck = s -> startDetector.getMaxCheckInterval().currentInterval(s.toSpacecraftState()); return converted. - withMaxCheck(field.getZero().newInstance(startDetector.getMaxCheckInterval())). + withMaxCheck(maxCheck). withThreshold(field.getZero().newInstance(startDetector.getThreshold())). withHandler(new FieldStartHandler<>()); } /** Convert a detector and set up new handler. *

          - * This method is not inlined in {@link #getFieldEventsDetectors(Field)} because the + * This method is not inlined in {@link #getFieldEventDetectors(Field)} because the * parameterized types confuses the Java compiler. *

          * @param field field to which the state belongs @@ -210,15 +220,16 @@ private , S extends CalculusFieldElement, S extends CalculusFieldElement> D convertAndSetUpStopHandler(final Field field) { final FieldAbstractDetector converted = convertStopDetector(field, stopDetector); + final FieldAdaptableInterval maxCheck = s -> stopDetector.getMaxCheckInterval().currentInterval(s.toSpacecraftState()); return converted. - withMaxCheck(field.getZero().newInstance(stopDetector.getMaxCheckInterval())). + withMaxCheck(maxCheck). withThreshold(field.getZero().newInstance(stopDetector.getThreshold())). withHandler(new FieldStopHandler<>()); } /** Convert a primitive firing start detector into a field firing start detector. *

          - * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(CalculusFieldElement) withMaxCheck}, + * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(FieldAdaptableInterval) withMaxCheck}, * {@link FieldAbstractDetector#withThreshold(CalculusFieldElement) withThreshold}, or * {@link FieldAbstractDetector#withHandler(org.orekit.propagation.events.handlers.FieldEventHandler) withHandler} * in the converted detector, this will be done by caller. @@ -228,7 +239,7 @@ private , S extends CalculusFieldElement *

          {@code
          -     *     protected , S extends CalculusFieldElement>
          +     *     protected , S extends CalculusFieldElement>
                *         FieldAbstractDetector convertStartDetector(final Field field, final XyzDetector detector) {
                *
                *         final FieldAbsoluteDate date  = new FieldAbsoluteDate<>(field, detector.getDate());
          @@ -246,12 +257,12 @@ private , S extends CalculusFieldElement type of the field elements
                * @return converted firing start detector
                */
          -    protected abstract , S extends CalculusFieldElement> FieldAbstractDetector
          +    protected abstract , S extends CalculusFieldElement> FieldAbstractDetector
                   convertStartDetector(Field field, A detector);
           
               /** Convert a primitive firing stop detector into a field firing stop detector.
                * 

          - * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(CalculusFieldElement) withMaxCheck}, + * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(FieldAdaptableInterval) withMaxCheck}, * {@link FieldAbstractDetector#withThreshold(CalculusFieldElement) withThreshold}, or * {@link FieldAbstractDetector#withHandler(org.orekit.propagation.events.handlers.FieldEventHandler) withHandler} * in the converted detector, this will be done by caller. @@ -261,7 +272,7 @@ private , S extends CalculusFieldElement *

          {@code
          -     *     protected , S extends CalculusFieldElement>
          +     *     protected , S extends CalculusFieldElement>
                *         FieldAbstractDetector convertStopDetector(final Field field, final XyzDetector detector) {
                *
                *         final FieldAbsoluteDate date  = new FieldAbsoluteDate<>(field, detector.getDate());
          @@ -279,25 +290,25 @@ private , S extends CalculusFieldElement type of the field elements
                * @return converted firing stop detector
                */
          -    protected abstract , S extends CalculusFieldElement> FieldAbstractDetector
          +    protected abstract , S extends CalculusFieldElement> FieldAbstractDetector
                   convertStopDetector(Field field, O detector);
           
               /** Local handler for start triggers. */
          -    private class StartHandler implements EventHandler {
          +    private class StartHandler implements EventHandler {
           
                   /** Propagation direction. */
                   private boolean forward;
           
                   /** {@inheritDoc} */
                   @Override
          -        public void init(final SpacecraftState initialState, final AbsoluteDate target, final A detector) {
          +        public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) {
                       forward = target.isAfterOrEqualTo(initialState);
                       initializeResetters(initialState, target);
                   }
           
                   /** {@inheritDoc} */
                   @Override
          -        public Action eventOccurred(final SpacecraftState s, final A detector, final boolean increasing) {
          +        public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
                       if (increasing) {
                           // the event is meaningful for maneuver firing
                           if (forward) {
          @@ -315,28 +326,28 @@ public Action eventOccurred(final SpacecraftState s, final A detector, final boo
           
                   /** {@inheritDoc} */
                   @Override
          -        public SpacecraftState resetState(final A detector, final SpacecraftState oldState) {
          +        public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
                       return applyResetters(oldState);
                   }
           
               }
           
               /** Local handler for stop triggers. */
          -    private class StopHandler implements EventHandler {
          +    private class StopHandler implements EventHandler {
           
                   /** Propagation direction. */
                   private boolean forward;
           
                   /** {@inheritDoc} */
                   @Override
          -        public void init(final SpacecraftState initialState, final AbsoluteDate target, final O detector) {
          +        public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) {
                       forward = target.isAfterOrEqualTo(initialState);
                       initializeResetters(initialState, target);
                   }
           
                   /** {@inheritDoc} */
                   @Override
          -        public Action eventOccurred(final SpacecraftState s, final O detector, final boolean increasing) {
          +        public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
                       if (increasing) {
                           // the event is meaningful for maneuver firing
                           if (forward) {
          @@ -354,7 +365,7 @@ public Action eventOccurred(final SpacecraftState s, final O detector, final boo
           
                   /** {@inheritDoc} */
                   @Override
          -        public SpacecraftState resetState(final O detector, final SpacecraftState oldState) {
          +        public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
                       return applyResetters(oldState);
                   }
           
          @@ -363,7 +374,7 @@ public SpacecraftState resetState(final O detector, final SpacecraftState oldSta
               /** Local handler for start triggers.
                * @param  type of the field elements
                */
          -    private class FieldStartHandler, S extends CalculusFieldElement> implements FieldEventHandler {
          +    private class FieldStartHandler> implements FieldEventHandler {
           
                   /** Propagation direction. */
                   private boolean forward;
          @@ -372,14 +383,14 @@ private class FieldStartHandler, S extends Calcu
                   @Override
                   public void init(final FieldSpacecraftState initialState,
                                    final FieldAbsoluteDate target,
          -                         final D detector) {
          +                         final FieldEventDetector detector) {
                       forward = target.isAfterOrEqualTo(initialState);
                       initializeResetters(initialState, target);
                   }
           
                   /** {@inheritDoc} */
                   @Override
          -        public Action eventOccurred(final FieldSpacecraftState s, final D detector, final boolean increasing) {
          +        public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) {
                       if (increasing) {
                           // the event is meaningful for maneuver firing
                           if (forward) {
          @@ -397,7 +408,7 @@ public Action eventOccurred(final FieldSpacecraftState s, final D detector, f
           
                   /** {@inheritDoc} */
                   @Override
          -        public FieldSpacecraftState resetState(final D detector, final FieldSpacecraftState oldState) {
          +        public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) {
                       return applyResetters(oldState);
                   }
           
          @@ -406,7 +417,7 @@ public FieldSpacecraftState resetState(final D detector, final FieldSpacecraf
               /** Local handler for stop triggers.
                * @param  type of the field elements
                */
          -    private class FieldStopHandler, S extends CalculusFieldElement> implements FieldEventHandler {
          +    private class FieldStopHandler> implements FieldEventHandler {
           
                   /** Propagation direction. */
                   private boolean forward;
          @@ -415,14 +426,14 @@ private class FieldStopHandler, S extends Calcul
                   @Override
                   public void init(final FieldSpacecraftState initialState,
                                    final FieldAbsoluteDate target,
          -                         final D detector) {
          +                         final FieldEventDetector detector) {
                       forward = target.isAfterOrEqualTo(initialState);
                       initializeResetters(initialState, target);
                   }
           
                   /** {@inheritDoc} */
                   @Override
          -        public Action eventOccurred(final FieldSpacecraftState s, final D detector, final boolean increasing) {
          +        public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) {
                       if (increasing) {
                           // the event is meaningful for maneuver firing
                           if (forward) {
          @@ -440,7 +451,7 @@ public Action eventOccurred(final FieldSpacecraftState s, final D detector, f
           
                   /** {@inheritDoc} */
                   @Override
          -        public FieldSpacecraftState resetState(final D detector, final FieldSpacecraftState oldState) {
          +        public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) {
                       return applyResetters(oldState);
                   }
           
          diff --git a/src/main/java/org/orekit/forces/maneuvers/trigger/package-info.java b/src/main/java/org/orekit/forces/maneuvers/trigger/package-info.java
          index ca8af3e466..743276c181 100644
          --- a/src/main/java/org/orekit/forces/maneuvers/trigger/package-info.java
          +++ b/src/main/java/org/orekit/forces/maneuvers/trigger/package-info.java
          @@ -1,4 +1,4 @@
          -/* Copyright 2002-2022 CS GROUP
          +/* Copyright 2002-2023 CS GROUP
            * Licensed to CS GROUP (CS) under one or more
            * contributor license agreements.  See the NOTICE file distributed with
            * this work for additional information regarding copyright ownership.
          diff --git a/src/main/java/org/orekit/forces/package-info.java b/src/main/java/org/orekit/forces/package-info.java
          index 2f152e90dc..4441f4fbba 100644
          --- a/src/main/java/org/orekit/forces/package-info.java
          +++ b/src/main/java/org/orekit/forces/package-info.java
          @@ -1,4 +1,4 @@
          -/* Copyright 2002-2022 CS GROUP
          +/* Copyright 2002-2023 CS GROUP
            * Licensed to CS GROUP (CS) under one or more
            * contributor license agreements.  See the NOTICE file distributed with
            * this work for additional information regarding copyright ownership.
          diff --git a/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java b/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java
          index 58c221cf8a..a8f8bb2331 100644
          --- a/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java
          +++ b/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java
          @@ -1,4 +1,4 @@
          -/* Copyright 2002-2022 CS GROUP
          +/* Copyright 2002-2023 CS GROUP
            * Licensed to CS GROUP (CS) under one or more
            * contributor license agreements.  See the NOTICE file distributed with
            * this work for additional information regarding copyright ownership.
          @@ -17,30 +17,29 @@
           package org.orekit.forces.radiation;
           
           import java.lang.reflect.Array;
          -import java.util.HashMap;
          -import java.util.Map;
          +import java.util.ArrayList;
          +import java.util.Collections;
          +import java.util.List;
           import java.util.stream.Stream;
           
          -import org.hipparchus.Field;
           import org.hipparchus.CalculusFieldElement;
          +import org.hipparchus.Field;
           import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
           import org.hipparchus.geometry.euclidean.threed.Vector3D;
           import org.hipparchus.ode.events.Action;
           import org.hipparchus.util.FastMath;
           import org.hipparchus.util.MathArrays;
          -import org.orekit.errors.OrekitException;
          -import org.orekit.errors.OrekitMessages;
          -import org.orekit.forces.AbstractForceModel;
          -import org.orekit.propagation.FieldSpacecraftState;
          -import org.orekit.propagation.SpacecraftState;
          -import org.orekit.propagation.events.AbstractDetector;
          +import org.orekit.bodies.OneAxisEllipsoid;
          +import org.orekit.forces.ForceModel;
          +import org.orekit.frames.Frame;
          +import org.orekit.propagation.events.EclipseDetector;
           import org.orekit.propagation.events.EventDetector;
          -import org.orekit.propagation.events.FieldAbstractDetector;
          +import org.orekit.propagation.events.FieldEclipseDetector;
           import org.orekit.propagation.events.FieldEventDetector;
          -import org.orekit.propagation.events.handlers.EventHandler;
          -import org.orekit.propagation.events.handlers.FieldEventHandler;
           import org.orekit.utils.Constants;
           import org.orekit.utils.ExtendedPVCoordinatesProvider;
          +import org.orekit.utils.ExtendedPVCoordinatesProviderAdapter;
          +import org.orekit.utils.OccultationEngine;
           
           /**
            * Base class for radiation force models.
          @@ -48,30 +47,39 @@
            * @see ECOM2
            * @since 10.2
            */
          -public abstract class AbstractRadiationForceModel extends AbstractForceModel {
          +public abstract class AbstractRadiationForceModel implements ForceModel {
           
               /** Margin to force recompute lighting ratio derivatives when we are really inside penumbra. */
               private static final double ANGULAR_MARGIN = 1.0e-10;
           
          -    /** Central body model. */
          -    private final double equatorialRadius;
          +    /** Max check interval for eclipse detectors. */
          +    private static final double ECLIPSE_MAX_CHECK = 60.0;
          +
          +    /** Threshold for eclipse detectors. */
          +    private static final double ECLIPSE_THRESHOLD = 1.0e-7; // this is consistent with ANGULAR_MARGIN = 10⁻¹⁰ rad for LEO
           
          -    /** Sun model. */
          -    private final ExtendedPVCoordinatesProvider sun;
          +    /** Flatness for spherical bodies. */
          +    private static final double SPHERICAL_BODY_FLATNESS = 0.0;
           
          -    /** Other occulting bodies to consider. The Moon for instance. */
          -    private final Map otherOccultingBodies;
          +    /** Prefix for occulting bodies frames names. */
          +    private static final String OCCULTING_PREFIX = "occulting-";
          +
          +    /** Occulting bodies (central body is always the first one).
          +     * @since 12.0
          +     */
          +    private final List occultingBodies;
           
               /**
                * Default constructor.
                * Only central body is considered.
                * @param sun Sun model
          -     * @param equatorialRadius central body spherical shape model (for umbra/penumbra computation)
          +     * @param centralBody central body shape model (for umbra/penumbra computation)
          +     * @since 12.0
                */
          -    protected AbstractRadiationForceModel(final ExtendedPVCoordinatesProvider sun, final double equatorialRadius) {
          -        this.sun                  = sun;
          -        this.equatorialRadius     = equatorialRadius;
          -        this.otherOccultingBodies = new HashMap<>();
          +    protected AbstractRadiationForceModel(final ExtendedPVCoordinatesProvider sun, final OneAxisEllipsoid centralBody) {
          +        // in most cases, there will be only Earth, sometimes also Moon so an initial capacity of 2 is appropriate
          +        occultingBodies = new ArrayList<>(2);
          +        occultingBodies.add(new OccultationEngine(sun, Constants.SUN_RADIUS, centralBody));
               }
           
               /** {@inheritDoc} */
          @@ -82,65 +90,53 @@ public boolean dependsOnPositionOnly() {
           
               /** {@inheritDoc} */
               @Override
          -    public Stream getEventsDetectors() {
          -        final EventDetector[] detectors = new EventDetector[2 + 2 * otherOccultingBodies.size()];
          -        detectors[0] = new UmbraDetector();
          -        detectors[1] = new PenumbraDetector();
          -        int i = 2;
          -        for (Map.Entry entry : otherOccultingBodies.entrySet()) {
          -            detectors[i]     = new GeneralUmbraDetector(entry.getKey(),    entry.getValue());
          -            detectors[i + 1] = new GeneralPenumbraDetector(entry.getKey(), entry.getValue());
          -            i = i + 2;
          -        }
          -        return Stream.of(detectors);
          +    public Stream getEventDetectors() {
          +        final EventDetector[] detectors = new EventDetector[2 * occultingBodies.size()];
          +        for (int i = 0; i < occultingBodies.size(); ++i) {
          +            final OccultationEngine occulting = occultingBodies.get(i);
          +            detectors[2 * i]     = new EclipseDetector(occulting).
          +                                   withUmbra().
          +                                   withMargin(-ANGULAR_MARGIN).
          +                                   withMaxCheck(ECLIPSE_MAX_CHECK).
          +                                   withThreshold(ECLIPSE_THRESHOLD).
          +                                   withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
          +            detectors[2 * i + 1] = new EclipseDetector(occulting).
          +                                   withPenumbra().
          +                                   withMargin(ANGULAR_MARGIN).
          +                                   withMaxCheck(ECLIPSE_MAX_CHECK).
          +                                   withThreshold(ECLIPSE_THRESHOLD).
          +                                   withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
          +        }
          +        // Fusion between Date detector for parameter driver span change and
          +        // Detector for umbra / penumbra events
          +        return Stream.concat(Stream.of(detectors), ForceModel.super.getEventDetectors());
               }
           
               /** {@inheritDoc} */
               @Override
          -    public > Stream> getFieldEventsDetectors(final Field field) {
          +    public > Stream> getFieldEventDetectors(final Field field) {
                   final T zero = field.getZero();
                   @SuppressWarnings("unchecked")
                   final FieldEventDetector[] detectors = (FieldEventDetector[]) Array.newInstance(FieldEventDetector.class,
          -                                                                                              2 + 2 * otherOccultingBodies.size());
          -        detectors[0] = new FieldUmbraDetector<>(field);
          -        detectors[1] = new FieldPenumbraDetector<>(field);
          -        int i = 2;
          -        for (Map.Entry entry : otherOccultingBodies.entrySet()) {
          -            detectors[i]     = new FieldGeneralUmbraDetector<>(field, entry.getKey(),    zero.newInstance(entry.getValue()));
          -            detectors[i + 1] = new FieldGeneralPenumbraDetector<>(field, entry.getKey(), zero.newInstance(entry.getValue()));
          -            i = i + 2;
          -        }
          -        return Stream.of(detectors);
          +                                                                                              2 * occultingBodies.size());
          +        for (int i = 0; i < occultingBodies.size(); ++i) {
          +            final OccultationEngine occulting = occultingBodies.get(i);
          +            detectors[2 * i]     = new FieldEclipseDetector<>(field, occulting).
          +                                   withUmbra().
          +                                   withMargin(zero.newInstance(-ANGULAR_MARGIN)).
          +                                   withMaxCheck(ECLIPSE_MAX_CHECK).
          +                                   withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)).
          +                                   withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
          +            detectors[2 * i + 1] = new FieldEclipseDetector<>(field, occulting).
          +                                   withPenumbra().
          +                                   withMargin(zero.newInstance(ANGULAR_MARGIN)).
          +                                   withMaxCheck(ECLIPSE_MAX_CHECK).
          +                                   withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)).
          +                                   withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
          +        }
          +        return Stream.concat(Stream.of(detectors), ForceModel.super.getFieldEventDetectors(field));
               }
           
          -    /**
          -     * Get the useful angles for eclipse computation.
          -     * @param sunPosition Sun position in the selected frame
          -     * @param position the satellite's position in the selected frame
          -     * @return the 3 angles {(satCentral, satSun), Central body apparent radius, Sun apparent radius}
          -     */
          -    protected double[] getEclipseAngles(final Vector3D sunPosition, final Vector3D position) {
          -        final double[] angle = new double[3];
          -
          -        final Vector3D satSunVector = sunPosition.subtract(position);
          -
          -        // Sat-Sun / Sat-CentralBody angle
          -        angle[0] = Vector3D.angle(satSunVector, position.negate());
          -
          -        // Central body apparent radius
          -        final double r = position.getNorm();
          -        if (r <= equatorialRadius) {
          -            throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
          -        }
          -        angle[1] = FastMath.asin(equatorialRadius / r);
          -
          -        // Sun apparent radius
          -        angle[2] = FastMath.asin(Constants.SUN_RADIUS / satSunVector.getNorm());
          -
          -        return angle;
          -    }
          -
          -
               /**
                * Get the useful angles for eclipse computation.
                * @param position the satellite's position in the selected frame
          @@ -169,35 +165,6 @@ protected double[] getGeneralEclipseAngles(final Vector3D position, final Vector
                   return angle;
               }
           
          -    /**
          -     * Get the useful angles for eclipse computation.
          -     * @param sunPosition Sun position in the selected frame
          -     * @param position the satellite's position in the selected frame.
          -     * @param  extends CalculusFieldElement
          -     * @return the 3 angles {(satCentral, satSun), Central body apparent radius, Sun apparent radius}
          -     */
          -    protected > T[] getEclipseAngles(final FieldVector3D sunPosition, final FieldVector3D position) {
          -        final T[] angle = MathArrays.buildArray(position.getX().getField(), 3);
          -
          -        final FieldVector3D mP           = position.negate();
          -        final FieldVector3D satSunVector = mP.add(sunPosition);
          -
          -        // Sat-Sun / Sat-CentralBody angle
          -        angle[0] = FieldVector3D.angle(satSunVector, mP);
          -
          -        // Central body apparent radius
          -        final T r = position.getNorm();
          -        if (r.getReal() <= equatorialRadius) {
          -            throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
          -        }
          -        angle[1] = r.reciprocal().multiply(equatorialRadius).asin();
          -
          -        // Sun apparent radius
          -        angle[2] = satSunVector.getNorm().reciprocal().multiply(Constants.SUN_RADIUS).asin();
          -
          -        return angle;
          -    }
          -
               /**
                * Get the useful angles for eclipse computation.
                * @param occultingPosition Oculting body position in the selected frame
          @@ -230,519 +197,70 @@ protected > T[] getGeneralEclipseAngles(final
           
               /**
                * Add a new occulting body.
          +     * 

          * Central body is already considered, it shall not be added this way. + *

          * @param provider body PV provider * @param radius body mean radius + * @see #addOccultingBody(OneAxisEllipsoid) */ public void addOccultingBody(final ExtendedPVCoordinatesProvider provider, final double radius) { - otherOccultingBodies.put(provider, radius); - } - /** - * Getter for other occulting bodies to consider. - * @return the map of other occulting bodies and corresponding mean radiuses - */ - public Map getOtherOccultingBodies() { - return otherOccultingBodies; - } - - /** - * Getter for equatorial radius. - * @return central body equatorial radius - */ - public double getEquatorialRadius() { - return equatorialRadius; - } - - - /** This class defines the umbra entry/exit detector. */ - private class UmbraDetector extends AbstractDetector { - - /** Build a new instance. */ - UmbraDetector() { - super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler() { - - /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final UmbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); + // as parent frame for occulting body frame, + // we select the first inertial frame in central body hierarchy + Frame parent = occultingBodies.get(0).getOcculting().getBodyFrame(); + while (!parent.isPseudoInertial()) { + parent = parent.getParent(); } - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - * @since 6.1 - */ - private UmbraDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); - } + // as the occulting body will be spherical, we can use an inertially oriented body frame + final Frame inertiallyOrientedBodyFrame = + new ExtendedPVCoordinatesProviderAdapter(parent, + provider, + OCCULTING_PREFIX + occultingBodies.size()); - /** {@inheritDoc} */ - @Override - protected UmbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { - return new UmbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); - } + // create the spherical occulting body + final OneAxisEllipsoid sphericalOccultingBody = + new OneAxisEllipsoid(radius, SPHERICAL_BODY_FLATNESS, inertiallyOrientedBodyFrame); - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the central body apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public double g(final SpacecraftState s) { - final double[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - s.getPVCoordinates().getPosition()); - return angle[0] - angle[1] + angle[2] - ANGULAR_MARGIN; - } - - } - - /** This class defines the penumbra entry/exit detector. */ - private class PenumbraDetector extends AbstractDetector { - - /** Build a new instance. */ - PenumbraDetector() { - super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler() { - - /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final PenumbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - } - - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - * @since 6.1 - */ - private PenumbraDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); - } - - /** {@inheritDoc} */ - @Override - protected PenumbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { - return new PenumbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); - } - - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the sum of the central body and Sun's apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public double g(final SpacecraftState s) { - final double[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - s.getPVCoordinates().getPosition()); - return angle[0] - angle[1] - angle[2] + ANGULAR_MARGIN; - } + addOccultingBody(sphericalOccultingBody); } - /** This class defines the umbra entry/exit detector. */ - private class FieldUmbraDetector> - extends FieldAbstractDetector, T> { - - /** Build a new instance. - * @param field field to which elements belong - */ - FieldUmbraDetector(final Field field) { - super(field.getZero().add(60.0), field.getZero().add(1.0e-3), - DEFAULT_MAX_ITER, new FieldEventHandler, T>() { - - /** {@inheritDoc} */ - public Action eventOccurred(final FieldSpacecraftState s, - final FieldUmbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - } - - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - */ - private FieldUmbraDetector(final T maxCheck, final T threshold, final int maxIter, - final FieldEventHandler, T> handler) { - super(maxCheck, threshold, maxIter, handler); - } - - /** {@inheritDoc} */ - @Override - protected FieldUmbraDetector create(final T newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { - return new FieldUmbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); - } - - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the central body apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public T g(final FieldSpacecraftState s) { - final T[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - s.getPVCoordinates().getPosition()); - return angle[0].subtract(angle[1]).add(angle[2]).subtract(ANGULAR_MARGIN); - } - - } - - /** This class defines the penumbra entry/exit detector. */ - private class FieldPenumbraDetector> - extends FieldAbstractDetector, T> { - - /** Build a new instance. - * @param field field to which elements belong - */ - FieldPenumbraDetector(final Field field) { - super(field.getZero().add(60.0), field.getZero().add(1.0e-3), - DEFAULT_MAX_ITER, new FieldEventHandler, T>() { - - /** {@inheritDoc} */ - public Action eventOccurred(final FieldSpacecraftState s, - final FieldPenumbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - } - - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - */ - private FieldPenumbraDetector(final T maxCheck, final T threshold, final int maxIter, - final FieldEventHandler, T> handler) { - super(maxCheck, threshold, maxIter, handler); - } - - /** {@inheritDoc} */ - @Override - protected FieldPenumbraDetector create(final T newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { - return new FieldPenumbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); - } - - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the sum of the central body and Sun's apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public T g(final FieldSpacecraftState s) { - final T[] angle = getEclipseAngles(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - s.getPVCoordinates().getPosition()); - return angle[0].subtract(angle[1]).subtract(angle[2]).add(ANGULAR_MARGIN); - } - - } - - /** This class defines the umbra entry/exit detector. */ - private class GeneralUmbraDetector extends AbstractDetector { - - /** Occulting body PV provider. */ - private ExtendedPVCoordinatesProvider provider; - - /** Occulting body mean radius. */ - private double radius; - - /** Build a new instance. - * @param provider occulting body PV provider - * @param radius occulting body mean radius - */ - GeneralUmbraDetector(final ExtendedPVCoordinatesProvider provider, final double radius) { - super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler() { - - /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final GeneralUmbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - this.provider = provider; - this.radius = radius; - } - - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - * @since 6.1 - */ - private GeneralUmbraDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); - } - - /** {@inheritDoc} */ - @Override - protected GeneralUmbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { - return new GeneralUmbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); - } - - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the central body apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public double g(final SpacecraftState s) { - final double[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(), - provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - Constants.SUN_RADIUS); - return angle[0] - angle[1] + angle[2] - ANGULAR_MARGIN; - } - - } - - /** This class defines the umbra entry/exit detector. */ - private class GeneralPenumbraDetector extends AbstractDetector { - - /** Occulting body PV provider. */ - private ExtendedPVCoordinatesProvider provider; - - /** Occulting body mean radius. */ - private double radius; - - /** Build a new instance. - * @param provider occulting body PV provider - * @param radius occulting body mean radius - */ - GeneralPenumbraDetector(final ExtendedPVCoordinatesProvider provider, final double radius) { - super(60.0, 1.0e-3, DEFAULT_MAX_ITER, new EventHandler() { - - /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final GeneralPenumbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - this.provider = provider; - this.radius = radius; - } + /** + * Add a new occulting body. + *

          + * Central body is already considered, it shall not be added this way. + *

          + * @param occulting occulting body to add + * @see #addOccultingBody(ExtendedPVCoordinatesProvider, double) + * @since 12.0 + */ + public void addOccultingBody(final OneAxisEllipsoid occulting) { - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - * @since 6.1 - */ - private GeneralPenumbraDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler) { - super(maxCheck, threshold, maxIter, handler); - } + // retrieve Sun from the central occulting body engine + final OccultationEngine central = occultingBodies.get(0); - /** {@inheritDoc} */ - @Override - protected GeneralPenumbraDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { - return new GeneralPenumbraDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); - } + // create a new occultation engine for the new occulting body + final OccultationEngine additional = + new OccultationEngine(central.getOcculted(), central.getOccultedRadius(), occulting); - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the central body apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public double g(final SpacecraftState s) { - final double[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(), - provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - Constants.SUN_RADIUS); - return angle[0] - angle[1] - angle[2] + ANGULAR_MARGIN; - } + // add it to the list + occultingBodies.add(additional); } - /** This class defines the umbra entry/exit detector. */ - private class FieldGeneralUmbraDetector> - extends FieldAbstractDetector, T> { - - /** Occulting body PV provider. */ - private ExtendedPVCoordinatesProvider provider; - - /** Occulting body mean radius. */ - private T radius; - - /** Build a new instance. - * @param field field to which elements belong - * @param provider occulting body PV provider - * @param radius occulting body mean radius - */ - FieldGeneralUmbraDetector(final Field field, final ExtendedPVCoordinatesProvider provider, final T radius) { - super(field.getZero().add(60.0), field.getZero().add(1.0e-3), - DEFAULT_MAX_ITER, new FieldEventHandler, T>() { - - /** {@inheritDoc} */ - public Action eventOccurred(final FieldSpacecraftState s, - final FieldGeneralUmbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - this.provider = provider; - this.radius = radius; - } - - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - */ - private FieldGeneralUmbraDetector(final T maxCheck, final T threshold, - final int maxIter, - final FieldEventHandler, T> handler) { - super(maxCheck, threshold, maxIter, handler); - } - - /** {@inheritDoc} */ - @Override - protected FieldGeneralUmbraDetector create(final T newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { - return new FieldGeneralUmbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); - } - - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the central body apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public T g(final FieldSpacecraftState s) { - final T[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(), - provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - s.getA().getField().getZero().add(Constants.SUN_RADIUS)); - return angle[0].subtract(angle[1]).add(angle[2]).subtract(ANGULAR_MARGIN); - } - + /** + * Get all occulting bodies to consider. + *

          + * The list always contains at least one element: the central body + * which is always the first one in the list. + *

          + * @return immutable list of all occulting bodies to consider, including the central body + * @since 12.0 + */ + public List getOccultingBodies() { + return Collections.unmodifiableList(occultingBodies); } - /** This class defines the umbra entry/exit detector. */ - private class FieldGeneralPenumbraDetector> - extends FieldAbstractDetector, T> { - - /** Occulting body PV provider. */ - private ExtendedPVCoordinatesProvider provider; - - /** Occulting body mean radius. */ - private T radius; - - /** Build a new instance. - * @param field field to which elements belong - * @param provider occulting body PV provider - * @param radius occulting body mean radius - */ - FieldGeneralPenumbraDetector(final Field field, final ExtendedPVCoordinatesProvider provider, final T radius) { - super(field.getZero().add(60.0), field.getZero().add(1.0e-3), - DEFAULT_MAX_ITER, new FieldEventHandler, T>() { - - /** {@inheritDoc} */ - public Action eventOccurred(final FieldSpacecraftState s, - final FieldGeneralPenumbraDetector detector, - final boolean increasing) { - return Action.RESET_DERIVATIVES; - } - - }); - this.provider = provider; - this.radius = radius; - } - - /** Private constructor with full parameters. - *

          - * This constructor is private as users are expected to use the builder - * API with the various {@code withXxx()} methods to set up the instance - * in a readable manner without using a huge amount of parameters. - *

          - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - */ - private FieldGeneralPenumbraDetector(final T maxCheck, final T threshold, final int maxIter, - final FieldEventHandler, T> handler) { - super(maxCheck, threshold, maxIter, handler); - } - - /** {@inheritDoc} */ - @Override - protected FieldGeneralPenumbraDetector create(final T newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { - return new FieldGeneralPenumbraDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); - } - - /** The G-function is the difference between the Sun-Sat-Central-Body angle and - * the central body apparent radius. - * @param s the current state information : date, kinematics, attitude - * @return value of the g function - */ - public T g(final FieldSpacecraftState s) { - final T[] angle = getGeneralEclipseAngles(s.getPVCoordinates().getPosition(), - provider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - radius, sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(), - s.getA().getField().getZero().add(Constants.SUN_RADIUS)); - return angle[0].subtract(angle[1]).subtract(angle[2]).add(ANGULAR_MARGIN); - } - - } } diff --git a/src/main/java/org/orekit/forces/radiation/ECOM2.java b/src/main/java/org/orekit/forces/radiation/ECOM2.java index 875db914ac..9628dcd647 100644 --- a/src/main/java/org/orekit/forces/radiation/ECOM2.java +++ b/src/main/java/org/orekit/forces/radiation/ECOM2.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,6 +26,9 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.SinCos; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.frames.FramesFactory; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ExtendedPVCoordinatesProvider; @@ -114,9 +117,10 @@ public class ECOM2 extends AbstractRadiationForceModel { * @param sun provide for Sun parameter * @param equatorialRadius spherical shape model (for umbra/penumbra computation) */ + @DefaultDataContext public ECOM2(final int nD, final int nB, final double value, final ExtendedPVCoordinatesProvider sun, final double equatorialRadius) { - super(sun, equatorialRadius); + super(sun, new OneAxisEllipsoid(equatorialRadius, 0.0, FramesFactory.getGCRF())); this.nB = nB; this.nD = nD; this.coefficients = new ArrayList<>(2 * (nD + nB) + 3); @@ -150,8 +154,8 @@ public ECOM2(final int nD, final int nB, final double value, public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { // Spacecraft and Sun position vectors (expressed in the spacecraft's frame) - final Vector3D satPos = s.getPVCoordinates().getPosition(); - final Vector3D sunPos = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D satPos = s.getPosition(); + final Vector3D sunPos = sun.getPosition(s.getDate(), s.getFrame()); // Build the coordinate system final Vector3D Z = s.getPVCoordinates().getMomentum(); @@ -187,8 +191,8 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) public > FieldVector3D acceleration(final FieldSpacecraftState s, final T[] parameters) { // Spacecraft and Sun position vectors (expressed in the spacecraft's frame) - final FieldVector3D satPos = s.getPVCoordinates().getPosition(); - final FieldVector3D sunPos = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final FieldVector3D satPos = s.getPosition(); + final FieldVector3D sunPos = sun.getPosition(s.getDate(), s.getFrame()); // Build the coordinate system final FieldVector3D Z = s.getPVCoordinates().getMomentum(); diff --git a/src/main/java/org/orekit/forces/radiation/IsotropicRadiationCNES95Convention.java b/src/main/java/org/orekit/forces/radiation/IsotropicRadiationCNES95Convention.java index 231c1c3fc7..9de6e939ef 100644 --- a/src/main/java/org/orekit/forces/radiation/IsotropicRadiationCNES95Convention.java +++ b/src/main/java/org/orekit/forces/radiation/IsotropicRadiationCNES95Convention.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,14 +21,11 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; /** This class represents the features of a simplified spacecraft. @@ -82,7 +79,8 @@ public class IsotropicRadiationCNES95Convention implements RadiationSensitive { * @param tau specular reflection coefficient τ between 0.0 an 1.0 */ public IsotropicRadiationCNES95Convention(final double crossSection, final double alpha, final double tau) { - this.parameterDrivers = new ArrayList<>(2); + this.parameterDrivers = new ArrayList<>(3); + parameterDrivers.add(new ParameterDriver(RadiationSensitive.GLOBAL_RADIATION_FACTOR, 1.0, SCALE, 0.0, Double.POSITIVE_INFINITY)); parameterDrivers.add(new ParameterDriver(RadiationSensitive.ABSORPTION_COEFFICIENT, alpha, SCALE, 0.0, 1.0)); parameterDrivers.add(new ParameterDriver(RadiationSensitive.REFLECTION_COEFFICIENT, tau, SCALE, 0.0, 1.0)); this.crossSection = crossSection; @@ -96,26 +94,24 @@ public List getRadiationParametersDrivers() { /** {@inheritDoc} */ @Override - public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position, - final Rotation rotation, final double mass, final Vector3D flux, + public Vector3D radiationPressureAcceleration(final SpacecraftState state, final Vector3D flux, final double[] parameters) { - final double alpha = parameters[0]; - final double tau = parameters[1]; - final double kP = crossSection * (1 + 4 * (1.0 - alpha) * (1.0 - tau) / 9.0); - return new Vector3D(kP / mass, flux); + final double alpha = parameters[1]; + final double tau = parameters[2]; + final double kP = parameters[0] * crossSection * (1 + 4 * (1.0 - alpha) * (1.0 - tau) / 9.0); + return new Vector3D(kP / state.getMass(), flux); } /** {@inheritDoc} */ @Override public > FieldVector3D - radiationPressureAcceleration(final FieldAbsoluteDate date, final Frame frame, - final FieldVector3D position, - final FieldRotation rotation, final T mass, + radiationPressureAcceleration(final FieldSpacecraftState state, final FieldVector3D flux, final T[] parameters) { - final T alpha = parameters[0]; - final T tau = parameters[1]; - final T kP = alpha.negate().add(1).multiply(tau.negate().add(1)).multiply(4.0 / 9.0).add(1).multiply(crossSection); - return new FieldVector3D<>(mass.reciprocal().multiply(kP), flux); + final T alpha = parameters[1]; + final T tau = parameters[2]; + final T kP = alpha.negate().add(1).multiply(tau.negate().add(1)).multiply(4.0 / 9.0).add(1). + multiply(parameters[0]).multiply(crossSection); + return new FieldVector3D<>(state.getMass().reciprocal().multiply(kP), flux); } } diff --git a/src/main/java/org/orekit/forces/radiation/IsotropicRadiationClassicalConvention.java b/src/main/java/org/orekit/forces/radiation/IsotropicRadiationClassicalConvention.java index 1f4177f428..262d94dc1f 100644 --- a/src/main/java/org/orekit/forces/radiation/IsotropicRadiationClassicalConvention.java +++ b/src/main/java/org/orekit/forces/radiation/IsotropicRadiationClassicalConvention.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,14 +21,11 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; /** This class represents the features of a simplified spacecraft. @@ -70,7 +67,8 @@ public class IsotropicRadiationClassicalConvention implements RadiationSensitive * @param cs specular reflection coefficient Cs between 0.0 an 1.0 */ public IsotropicRadiationClassicalConvention(final double crossSection, final double ca, final double cs) { - this.parameterDrivers = new ArrayList<>(2); + this.parameterDrivers = new ArrayList<>(3); + parameterDrivers.add(new ParameterDriver(RadiationSensitive.GLOBAL_RADIATION_FACTOR, 1.0, SCALE, 0.0, Double.POSITIVE_INFINITY)); parameterDrivers.add(new ParameterDriver(RadiationSensitive.ABSORPTION_COEFFICIENT, ca, SCALE, 0.0, 1.0)); parameterDrivers.add(new ParameterDriver(RadiationSensitive.REFLECTION_COEFFICIENT, cs, SCALE, 0.0, 1.0)); this.crossSection = crossSection; @@ -84,26 +82,24 @@ public List getRadiationParametersDrivers() { /** {@inheritDoc} */ @Override - public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position, - final Rotation rotation, final double mass, final Vector3D flux, + public Vector3D radiationPressureAcceleration(final SpacecraftState state, final Vector3D flux, final double[] parameters) { - final double ca = parameters[0]; - final double cs = parameters[1]; - final double kP = crossSection * (1 + 4 * (1.0 - ca - cs) / 9.0); - return new Vector3D(kP / mass, flux); + final double ca = parameters[1]; + final double cs = parameters[2]; + final double kP = parameters[0] * crossSection * (1 + 4 * (1.0 - ca - cs) / 9.0); + return new Vector3D(kP / state.getMass(), flux); } /** {@inheritDoc} */ @Override public > FieldVector3D - radiationPressureAcceleration(final FieldAbsoluteDate date, final Frame frame, - final FieldVector3D position, - final FieldRotation rotation, final T mass, + radiationPressureAcceleration(final FieldSpacecraftState state, final FieldVector3D flux, final T[] parameters) { - final T ca = parameters[0]; - final T cs = parameters[1]; - final T kP = ca.add(cs).negate().add(1).multiply(4.0 / 9.0).add(1).multiply(crossSection); - return new FieldVector3D<>(mass.reciprocal().multiply(kP), flux); + final T ca = parameters[1]; + final T cs = parameters[2]; + final T kP = ca.add(cs).negate().add(1).multiply(4.0 / 9.0).add(1). + multiply(parameters[0]).multiply(crossSection); + return new FieldVector3D<>(state.getMass().reciprocal().multiply(kP), flux); } } diff --git a/src/main/java/org/orekit/forces/radiation/IsotropicRadiationSingleCoefficient.java b/src/main/java/org/orekit/forces/radiation/IsotropicRadiationSingleCoefficient.java index d0a37c4485..98b116bb16 100644 --- a/src/main/java/org/orekit/forces/radiation/IsotropicRadiationSingleCoefficient.java +++ b/src/main/java/org/orekit/forces/radiation/IsotropicRadiationSingleCoefficient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,18 +16,16 @@ */ package org.orekit.forces.radiation; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; /** This class represents the features of a simplified spacecraft. @@ -51,8 +49,8 @@ public class IsotropicRadiationSingleCoefficient implements RadiationSensitive { */ private final double SCALE = FastMath.scalb(1.0, -3); - /** Driver for reflection coefficient. */ - private final ParameterDriver reflectionParameterDriver; + /** Drivers for radiation coefficient. */ + private final List radiationParametersDrivers; /** Cross section (m²). */ private final double crossSection; @@ -75,9 +73,17 @@ public IsotropicRadiationSingleCoefficient(final double crossSection, final doub final double crMin, final double crMax) { // in some corner cases (unknown spacecraft, fuel leaks, active piloting ...) // the single coefficient may be arbitrary, and even negative - reflectionParameterDriver = new ParameterDriver(RadiationSensitive.REFLECTION_COEFFICIENT, - cr, SCALE, - crMin, crMax); + // the REFLECTION_COEFFICIENT parameter should be sufficient, but GLOBAL_RADIATION_FACTOR + // was added as of 12.0 for consistency with BoxAndSolarArraySpacecraft + // that only has a global multiplicatof factor, hence allowing this name + // to be used for both models + this.radiationParametersDrivers = new ArrayList<>(2); + radiationParametersDrivers.add(new ParameterDriver(RadiationSensitive.GLOBAL_RADIATION_FACTOR, + 1.0, SCALE, + 0.0, Double.POSITIVE_INFINITY)); + radiationParametersDrivers.add(new ParameterDriver(RadiationSensitive.REFLECTION_COEFFICIENT, + cr, SCALE, + crMin, crMax)); this.crossSection = crossSection; @@ -86,28 +92,26 @@ public IsotropicRadiationSingleCoefficient(final double crossSection, final doub /** {@inheritDoc} */ @Override public List getRadiationParametersDrivers() { - return Collections.singletonList(reflectionParameterDriver); + return Collections.unmodifiableList(radiationParametersDrivers); } /** {@inheritDoc} */ @Override - public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position, - final Rotation rotation, final double mass, final Vector3D flux, + public Vector3D radiationPressureAcceleration(final SpacecraftState state, final Vector3D flux, final double[] parameters) { - final double cr = parameters[0]; - return new Vector3D(crossSection * cr / mass, flux); + final double cr = parameters[1]; + return new Vector3D(parameters[0] * crossSection * cr / state.getMass(), flux); } /** {@inheritDoc} */ @Override public > FieldVector3D - radiationPressureAcceleration(final FieldAbsoluteDate date, final Frame frame, - final FieldVector3D position, - final FieldRotation rotation, final T mass, + radiationPressureAcceleration(final FieldSpacecraftState state, final FieldVector3D flux, final T[] parameters) { - final T cr = parameters[0]; - return new FieldVector3D<>(mass.reciprocal().multiply(crossSection).multiply(cr), flux); + final T cr = parameters[1]; + return new FieldVector3D<>(state.getMass().reciprocal().multiply(parameters[0]).multiply(crossSection).multiply(cr), + flux); } } diff --git a/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java b/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java index 36fafccbf6..756cde4a7f 100644 --- a/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java +++ b/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,9 +17,7 @@ package org.orekit.forces.radiation; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.polynomials.PolynomialFunction; import org.hipparchus.analysis.polynomials.PolynomialsUtils; @@ -35,14 +33,12 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.data.DataContext; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.Transform; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; @@ -69,7 +65,7 @@ * @author Thomas Paulet * @since 10.3 */ -public class KnockeRediffusedForceModel extends AbstractForceModel { +public class KnockeRediffusedForceModel implements ForceModel { /** Earth rotation around Sun pulsation in rad/sec. */ private static final double EARTH_AROUND_SUN_PULSATION = MathUtils.TWO_PI / Constants.JULIAN_YEAR; @@ -165,18 +161,6 @@ public boolean dependsOnPositionOnly() { return false; } - /** {@inheritDoc} */ - @Override - public Stream getEventsDetectors() { - return Stream.of(); - } - - /** {@inheritDoc} */ - @Override - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.of(); - } - /** {@inheritDoc} */ @Override public Vector3D acceleration(final SpacecraftState s, @@ -189,10 +173,10 @@ public Vector3D acceleration(final SpacecraftState s, final Frame frame = s.getFrame(); // Get satellite position - final Vector3D satellitePosition = s.getPVCoordinates().getPosition(); + final Vector3D satellitePosition = s.getPosition(); // Get Sun position - final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition(); + final Vector3D sunPosition = sun.getPosition(date, frame); // Get spherical Earth model final OneAxisEllipsoid earth = new OneAxisEllipsoid(equatorialRadius, 0.0, frame); @@ -241,8 +225,8 @@ public Vector3D acceleration(final SpacecraftState s, rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, earth, sectorArea)); } } - return spacecraft.radiationPressureAcceleration(date, frame, satellitePosition, s.getAttitude().getRotation(), - s.getMass(), rediffusedFlux, parameters); + + return spacecraft.radiationPressureAcceleration(s, rediffusedFlux, parameters); } @@ -260,10 +244,10 @@ public > FieldVector3D acceleration(final F final T zero = date.getField().getZero(); // Get satellite position - final FieldVector3D satellitePosition = s.getPVCoordinates().getPosition(); + final FieldVector3D satellitePosition = s.getPosition(); // Get Sun position - final FieldVector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition(); + final FieldVector3D sunPosition = sun.getPosition(date, frame); // Get spherical Earth model final OneAxisEllipsoid earth = new OneAxisEllipsoid(equatorialRadius, 0.0, frame); @@ -316,8 +300,8 @@ public > FieldVector3D acceleration(final F rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, earth, sectorArea)); } } - return spacecraft.radiationPressureAcceleration(date, frame, satellitePosition, s.getAttitude().getRotation(), - s.getMass(), rediffusedFlux, parameters); + + return spacecraft.radiationPressureAcceleration(s, rediffusedFlux, parameters); } @@ -499,7 +483,7 @@ private Vector3D computeElementaryFlux(final SpacecraftState state, final double elementArea) { // Get satellite position - final Vector3D satellitePosition = state.getPVCoordinates().getPosition(); + final Vector3D satellitePosition = state.getPosition(); // Get current date final AbsoluteDate date = state.getDate(); @@ -573,7 +557,7 @@ private > FieldVector3D computeElementaryFl final T elementArea) { // Get satellite position - final FieldVector3D satellitePosition = state.getPVCoordinates().getPosition(); + final FieldVector3D satellitePosition = state.getPosition(); // Get current date final FieldAbsoluteDate date = state.getDate(); diff --git a/src/main/java/org/orekit/forces/radiation/RadiationSensitive.java b/src/main/java/org/orekit/forces/radiation/RadiationSensitive.java index 01c2b34824..a90d3c70d7 100644 --- a/src/main/java/org/orekit/forces/radiation/RadiationSensitive.java +++ b/src/main/java/org/orekit/forces/radiation/RadiationSensitive.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,13 +19,10 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.frames.Frame; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.utils.ParameterDriver; /** Interface for spacecraft that are sensitive to radiation pressure forces. @@ -36,6 +33,11 @@ */ public interface RadiationSensitive { + /** Parameter name for global multiplicative factor. + * @since 12.0 + */ + String GLOBAL_RADIATION_FACTOR = "global radiation factor"; + /** Parameter name for absorption coefficient. */ String ABSORPTION_COEFFICIENT = "absorption coefficient"; @@ -49,31 +51,23 @@ public interface RadiationSensitive { List getRadiationParametersDrivers(); /** Compute the acceleration due to radiation pressure. - * @param date current date - * @param frame inertial reference frame for state (both orbit and attitude) - * @param position position of spacecraft in reference frame - * @param rotation orientation (attitude) of the spacecraft with respect to reference frame - * @param mass current mass + * @param state current state * @param flux radiation flux in the same inertial frame as spacecraft orbit * @param parameters values of the force model parameters * @return spacecraft acceleration in the same inertial frame as spacecraft orbit (m/s²) + * @since 12.0 */ - Vector3D radiationPressureAcceleration(AbsoluteDate date, Frame frame, Vector3D position, - Rotation rotation, double mass, Vector3D flux, - double[] parameters); + Vector3D radiationPressureAcceleration(SpacecraftState state, Vector3D flux, double[] parameters); /** Compute the acceleration due to radiation pressure. - * @param date current date - * @param frame inertial reference frame for state (both orbit and attitude) - * @param position position of spacecraft in reference frame - * @param rotation orientation (attitude) of the spacecraft with respect to reference frame - * @param mass current mass + * @param state current state * @param flux radiation flux in the same inertial frame as spacecraft orbit * @param parameters values of the force model parameters * @param extends CalculusFieldElement * @return spacecraft acceleration in the same inertial frame as spacecraft orbit (m/s²) + * @since 12.0 */ - > FieldVector3D radiationPressureAcceleration(FieldAbsoluteDate date, Frame frame, FieldVector3D position, - FieldRotation rotation, T mass, FieldVector3D flux, - T[] parameters); + > FieldVector3D radiationPressureAcceleration(FieldSpacecraftState state, + FieldVector3D flux, + T[] parameters); } diff --git a/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java b/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java index 0e122f4c7c..40c1e50405 100644 --- a/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java +++ b/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,16 +16,15 @@ */ package org.orekit.forces.radiation; -import java.util.ArrayList; +import java.lang.reflect.Array; import java.util.List; -import java.util.Map; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; import org.hipparchus.util.Precision; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.frames.Frame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; @@ -33,6 +32,8 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ExtendedPVCoordinatesProvider; +import org.orekit.utils.FrameAdapter; +import org.orekit.utils.OccultationEngine; import org.orekit.utils.ParameterDriver; /** Solar radiation pressure force model. @@ -65,6 +66,9 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel { /** Margin to force recompute lighting ratio derivatives when we are really inside penumbra. */ private static final double ANGULAR_MARGIN = 1.0e-10; + /** Threshold to decide whether the S/C frame is Sun-centered. */ + private static final double SUN_CENTERED_FRAME_THRESHOLD = 2. * Constants.SUN_RADIUS; + /** Reference flux normalized for a 1m distance (N). */ private final double kRef; @@ -81,13 +85,14 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel { *
        • pref = 4.56 10-6 N/m²
        • *
        * @param sun Sun model - * @param equatorialRadius spherical shape model (for umbra/penumbra computation) + * @param centralBody central body shape model (for umbra/penumbra computation) * @param spacecraft the object physical and geometrical information - * @since 9.2 + * @since 12.0 */ - public SolarRadiationPressure(final ExtendedPVCoordinatesProvider sun, final double equatorialRadius, + public SolarRadiationPressure(final ExtendedPVCoordinatesProvider sun, + final OneAxisEllipsoid centralBody, final RadiationSensitive spacecraft) { - this(D_REF, P_REF, sun, equatorialRadius, spacecraft); + this(D_REF, P_REF, sun, centralBody, spacecraft); } /** Complete constructor. @@ -99,15 +104,15 @@ public SolarRadiationPressure(final ExtendedPVCoordinatesProvider sun, final dou * @param dRef reference distance for the solar radiation pressure (m) * @param pRef reference solar radiation pressure at dRef (N/m²) * @param sun Sun model - * @param equatorialRadius spherical shape model (for umbra/penumbra computation) + * @param centralBody central body shape model (for umbra/penumbra computation) * @param spacecraft the object physical and geometrical information - * @since 9.2 + * @since 12.0 */ public SolarRadiationPressure(final double dRef, final double pRef, final ExtendedPVCoordinatesProvider sun, - final double equatorialRadius, + final OneAxisEllipsoid centralBody, final RadiationSensitive spacecraft) { - super(sun, equatorialRadius); + super(sun, centralBody); this.kRef = pRef * dRef * dRef; this.sun = sun; this.spacecraft = spacecraft; @@ -119,143 +124,156 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final AbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); - final Vector3D position = s.getPVCoordinates().getPosition(); - final Vector3D sunSatVector = position.subtract(sun.getPVCoordinates(date, frame).getPosition()); + final Vector3D position = s.getPosition(); + final Vector3D sunPosition = sun.getPosition(date, frame); + final Vector3D sunSatVector = position.subtract(sunPosition); final double r2 = sunSatVector.getNormSq(); // compute flux - final double ratio = getTotalLightingRatio(position, frame, date); + final double ratio = getLightingRatio(s, sunPosition); final double rawP = ratio * kRef / r2; final Vector3D flux = new Vector3D(rawP / FastMath.sqrt(r2), sunSatVector); - return spacecraft.radiationPressureAcceleration(date, frame, position, s.getAttitude().getRotation(), - s.getMass(), flux, parameters); + return spacecraft.radiationPressureAcceleration(s, flux, parameters); } /** {@inheritDoc} */ @Override public > FieldVector3D acceleration(final FieldSpacecraftState s, - final T[] parameters) { + final T[] parameters) { final FieldAbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); - final FieldVector3D position = s.getPVCoordinates().getPosition(); - final FieldVector3D sunSatVector = position.subtract(sun.getPVCoordinates(date, frame).getPosition()); + final FieldVector3D position = s.getPosition(); + final FieldVector3D sunPosition = sun.getPosition(date, frame); + final FieldVector3D sunSatVector = position.subtract(sunPosition); final T r2 = sunSatVector.getNormSq(); // compute flux - final T ratio = getTotalLightingRatio(position, frame, date); + final T ratio = getLightingRatio(s, sunPosition); final T rawP = ratio.multiply(kRef).divide(r2); final FieldVector3D flux = new FieldVector3D<>(rawP.divide(r2.sqrt()), sunSatVector); - return spacecraft.radiationPressureAcceleration(date, frame, position, s.getAttitude().getRotation(), - s.getMass(), flux, parameters); + return spacecraft.radiationPressureAcceleration(s, flux, parameters); } + /** Check whether the frame is considerer Sun-centered. + * + * @param sunPositionInFrame Sun position in frame to test + * @return true if frame is considered Sun-centered + * @since 12.0 + */ + private boolean isSunCenteredFrame(final Vector3D sunPositionInFrame) { + // Frame is considered Sun-centered if Sun (or Solar System barycenter) position + // in that frame is smaller than SUN_CENTERED_FRAME_THRESHOLD + return sunPositionInFrame.getNorm() < SUN_CENTERED_FRAME_THRESHOLD; + } + + /** Get the lighting ratio ([0-1]). - * Considers only central body as occulting body. - * @param position the satellite's position in the selected frame. - * @param frame in which is defined the position - * @param date the date + * @param state spacecraft state + * @param sunPosition Sun position in S/C frame at S/C date * @return lighting ratio - * @since 7.1 + * @since 12.0 added to avoid numerous call to sun.getPosition(...) */ - public double getLightingRatio(final Vector3D position, final Frame frame, final AbsoluteDate date) { + private double getLightingRatio(final SpacecraftState state, final Vector3D sunPosition) { - final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition(); - if (sunPosition.getNorm() < 2 * Constants.SUN_RADIUS) { - // we are in fact computing a trajectory around Sun (or solar system barycenter), - // not around a planet,we consider lighting ratio is always 1 + // Check if S/C frame is Sun-centered + if (isSunCenteredFrame(sunPosition)) { + // We are in fact computing a trajectory around Sun (or solar system barycenter), + // not around a planet, we consider lighting ratio will always be 1 return 1.0; } - // Compute useful angles - final double[] angle = getEclipseAngles(sunPosition, position); + final List occultingBodies = getOccultingBodies(); + final int n = occultingBodies.size(); - // Sat-Sun / Sat-CentralBody angle - final double sunSatCentralBodyAngle = angle[0]; - - // Central Body apparent radius - final double alphaCentral = angle[1]; + final OccultationEngine.OccultationAngles[] angles = new OccultationEngine.OccultationAngles[n]; + for (int i = 0; i < n; ++i) { + angles[i] = occultingBodies.get(i).angles(state); + } + final double alphaSunSq = angles[0].getOccultedApparentRadius() * angles[0].getOccultedApparentRadius(); - // Sun apparent radius - final double alphaSun = angle[2]; + double result = 0.0; + for (int i = 0; i < n; ++i) { + + // compute lighting ratio considering one occulting body only + final OccultationEngine oi = occultingBodies.get(i); + final double lightingRatioI = maskingRatio(angles[i]); + if (lightingRatioI == 0.0) { + // body totally occults Sun, total eclipse is occurring. + return 0.0; + } + result += lightingRatioI; - double result = 1.0; + // Mutual occulting body eclipse ratio computations between first and secondary bodies + for (int j = i + 1; j < n; ++j) { - // Is the satellite in complete umbra ? - if (sunSatCentralBodyAngle - alphaCentral + alphaSun <= ANGULAR_MARGIN) { - result = 0.0; - } else if (sunSatCentralBodyAngle - alphaCentral - alphaSun < -ANGULAR_MARGIN) { - // Compute a lighting ratio in penumbra - final double sEA2 = sunSatCentralBodyAngle * sunSatCentralBodyAngle; - final double oo2sEA = 1.0 / (2. * sunSatCentralBodyAngle); - final double aS2 = alphaSun * alphaSun; - final double aE2 = alphaCentral * alphaCentral; - final double aE2maS2 = aE2 - aS2; + final OccultationEngine oj = occultingBodies.get(j); + final double lightingRatioJ = maskingRatio(angles[j]); + if (lightingRatioJ == 0.0) { + // Secondary body totally occults Sun, no more computations are required, total eclipse is occurring. + return 0.0; + } else if (lightingRatioJ != 1) { + // Secondary body partially occults Sun - final double alpha1 = (sEA2 - aE2maS2) * oo2sEA; - final double alpha2 = (sEA2 + aE2maS2) * oo2sEA; + final OccultationEngine oij = new OccultationEngine(new FrameAdapter(oi.getOcculting().getBodyFrame()), + oi.getOcculting().getEquatorialRadius(), + oj.getOcculting()); + final OccultationEngine.OccultationAngles aij = oij.angles(state); + final double maskingRatioIJ = maskingRatio(aij); + final double alphaJSq = aij.getOccultedApparentRadius() * aij.getOccultedApparentRadius(); - // Protection against numerical inaccuracy at boundaries - final double almost0 = Precision.SAFE_MIN; - final double almost1 = FastMath.nextDown(1.0); - final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaSun)); - final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1); - final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaCentral)); - final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2); + final double mutualEclipseCorrection = (1 - maskingRatioIJ) * alphaJSq / alphaSunSq; + result -= mutualEclipseCorrection; - final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12); - final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22); + } - result = 1. - (P1 + P2) / (FastMath.PI * aS2); + } } - return result; + // Final term + result -= n - 1; + return result; } - /** Get eclipse ratio between to bodies seen from a specific object. - * Ratio is in [0-1]. - * @param position the satellite's position in the selected frame - * @param occultingPosition the position of the occulting object - * @param occultingRadius the mean radius of the occulting object - * @param occultedPosition the position of the occulted object - * @param occultedRadius the mean radius of the occulted object - * @return eclipse ratio + /** Get the lighting ratio ([0-1]). + * @param state spacecraft state + * @return lighting ratio + * @since 7.1 */ - public double getGeneralEclipseRatio(final Vector3D position, - final Vector3D occultingPosition, - final double occultingRadius, - final Vector3D occultedPosition, - final double occultedRadius) { - + public double getLightingRatio(final SpacecraftState state) { + return getLightingRatio(state, sun.getPosition(state.getDate(), state.getFrame())); + } - // Compute useful angles - final double[] angle = getGeneralEclipseAngles(position, occultingPosition, occultingRadius, occultedPosition, occultedRadius); + /** Get the masking ratio ([0-1]) considering one pair of bodies. + * @param angles occultation angles + * @return masking ratio: 0.0 body fully masked, 1.0 body fully visible + * @since 12.0 + */ + private double maskingRatio(final OccultationEngine.OccultationAngles angles) { // Sat-Occulted/ Sat-Occulting angle - final double occultedSatOcculting = angle[0]; + final double sunSatCentralBodyAngle = angles.getSeparation(); // Occulting apparent radius - final double alphaOcculting = angle[1]; + final double alphaCentral = angles.getLimbRadius(); // Occulted apparent radius - final double alphaOcculted = angle[2]; - - double result = 1.0; + final double alphaSun = angles.getOccultedApparentRadius(); // Is the satellite in complete umbra ? - if (occultedSatOcculting - alphaOcculting + alphaOcculted <= ANGULAR_MARGIN) { - result = 0.0; - } else if (occultedSatOcculting - alphaOcculting - alphaOcculted < -ANGULAR_MARGIN) { - // Compute an eclipse ratio in penumbra - final double sEA2 = occultedSatOcculting * occultedSatOcculting; - final double oo2sEA = 1.0 / (2. * occultedSatOcculting); - final double aS2 = alphaOcculted * alphaOcculted; - final double aE2 = alphaOcculting * alphaOcculting; + if (sunSatCentralBodyAngle - alphaCentral + alphaSun <= ANGULAR_MARGIN) { + return 0.0; + } else if (sunSatCentralBodyAngle - alphaCentral - alphaSun < -ANGULAR_MARGIN) { + // Compute a masking ratio in penumbra + final double sEA2 = sunSatCentralBodyAngle * sunSatCentralBodyAngle; + final double oo2sEA = 1.0 / (2. * sunSatCentralBodyAngle); + final double aS2 = alphaSun * alphaSun; + final double aE2 = alphaCentral * alphaCentral; final double aE2maS2 = aE2 - aS2; final double alpha1 = (sEA2 - aE2maS2) * oo2sEA; @@ -264,218 +282,125 @@ public double getGeneralEclipseRatio(final Vector3D position, // Protection against numerical inaccuracy at boundaries final double almost0 = Precision.SAFE_MIN; final double almost1 = FastMath.nextDown(1.0); - final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaOcculted)); + final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaSun)); final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1); - final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaOcculting)); + final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaCentral)); final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2); final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12); final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22); - result = 1. - (P1 + P2) / (FastMath.PI * aS2); + return 1. - (P1 + P2) / (FastMath.PI * aS2); + } else { + return 1.0; } - return result; } - /** Get the total lighting ratio ([0-1]). - * This method considers every occulting bodies. - * @param position the satellite's position in the selected frame. - * @param frame in which is defined the position - * @param date the date + /** Get the lighting ratio ([0-1]). + * @param state spacecraft state + * @param sunPosition Sun position in S/C frame at S/C date + * @param extends CalculusFieldElement * @return lighting ratio + * @since 12.0 added to avoid numerous call to sun.getPosition(...) */ - public double getTotalLightingRatio(final Vector3D position, final Frame frame, final AbsoluteDate date) { - - double result = 0.0; - final Map otherOccultingBodies = getOtherOccultingBodies(); - final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition(); - final int n = otherOccultingBodies.size() + 1; - - if (n > 1) { + private > T getLightingRatio(final FieldSpacecraftState state, final FieldVector3D sunPosition) { - final Vector3D[] occultingBodyPositions = new Vector3D[n]; - final double[] occultingBodyRadiuses = new double[n]; + final T one = state.getDate().getField().getOne(); + if (isSunCenteredFrame(sunPosition.toVector3D())) { + // We are in fact computing a trajectory around Sun (or solar system barycenter), + // not around a planet, we consider lighting ratio will always be 1 + return one; + } + final T zero = state.getDate().getField().getZero(); + final List occultingBodies = getOccultingBodies(); + final int n = occultingBodies.size(); + + @SuppressWarnings("unchecked") + final OccultationEngine.FieldOccultationAngles[] angles = + (OccultationEngine.FieldOccultationAngles[]) Array.newInstance(OccultationEngine.FieldOccultationAngles.class, n); + for (int i = 0; i < n; ++i) { + angles[i] = occultingBodies.get(i).angles(state); + } + final T alphaSunSq = angles[0].getOccultedApparentRadius().multiply(angles[0].getOccultedApparentRadius()); - // Central body - occultingBodyPositions[0] = new Vector3D(0.0, 0.0, 0.0); - occultingBodyRadiuses[0] = getEquatorialRadius(); + T result = state.getDate().getField().getZero(); + for (int i = 0; i < n; ++i) { - // Other occulting bodies - int k = 1; - for (Map.Entry entry : otherOccultingBodies.entrySet()) { - occultingBodyPositions[k] = entry.getKey().getPVCoordinates(date, frame).getPosition(); - occultingBodyRadiuses[k] = entry.getValue(); - ++k; + // compute lighting ratio considering one occulting body only + final OccultationEngine oi = occultingBodies.get(i); + final T lightingRatioI = maskingRatio(angles[i]); + if (lightingRatioI.isZero()) { + // body totally occults Sun, total eclipse is occurring. + return zero; } - for (int i = 0; i < n; ++i) { + result = result.add(lightingRatioI); - // Lighting ratio computations - final double eclipseRatioI = getGeneralEclipseRatio(position, - occultingBodyPositions[i], - occultingBodyRadiuses[i], - sunPosition, - Constants.SUN_RADIUS); + // Mutual occulting body eclipse ratio computations between first and secondary bodies + for (int j = i + 1; j < n; ++j) { - // First body totaly occults Sun, full eclipse is occuring. - if (eclipseRatioI == 0.0) { - return 0.0; - } - - - result += eclipseRatioI; - - - // Mutual occulting body eclipse ratio computations between first and secondary bodies - for (int j = i + 1; j < n; ++j) { - - final double eclipseRatioJ = getGeneralEclipseRatio(position, - occultingBodyPositions[j], - occultingBodyRadiuses[j], - sunPosition, - Constants.SUN_RADIUS); - final double eclipseRatioIJ = getGeneralEclipseRatio(position, - occultingBodyPositions[i], - occultingBodyRadiuses[i], - occultingBodyPositions[j], - occultingBodyRadiuses[j]); - - final double alphaJ = getGeneralEclipseAngles(position, - occultingBodyPositions[i], - occultingBodyRadiuses[i], - occultingBodyPositions[j], - occultingBodyRadiuses[j])[2]; + final OccultationEngine oj = occultingBodies.get(j); + final T lightingRatioJ = maskingRatio(angles[j]); + if (lightingRatioJ.isZero()) { + // Secondary body totally occults Sun, no more computations are required, total eclipse is occurring. + return zero; + } else if (lightingRatioJ.getReal() != 1) { + // Secondary body partially occults Sun - final double alphaSun = getEclipseAngles(sunPosition, position)[2]; - final double alphaJSq = alphaJ * alphaJ; - final double alphaSunSq = alphaSun * alphaSun; - final double mutualEclipseCorrection = (1 - eclipseRatioIJ) * alphaJSq / alphaSunSq; + final OccultationEngine oij = new OccultationEngine(new FrameAdapter(oi.getOcculting().getBodyFrame()), + oi.getOcculting().getEquatorialRadius(), + oj.getOcculting()); + final OccultationEngine.FieldOccultationAngles aij = oij.angles(state); + final T maskingRatioIJ = maskingRatio(aij); + final T alphaJSq = aij.getOccultedApparentRadius().multiply(aij.getOccultedApparentRadius()); - // Secondary body totally occults Sun, no more computations are required, full eclipse is occuring. - if (eclipseRatioJ == 0.0 ) { - return 0.0; - } + final T mutualEclipseCorrection = one.subtract(maskingRatioIJ).multiply(alphaJSq).divide(alphaSunSq); + result = result.subtract(mutualEclipseCorrection); - // Secondary body partially occults Sun - else if (eclipseRatioJ != 1) { - result -= mutualEclipseCorrection; - } } + } - // Final term - result -= n - 1; - } else { - // only central body is considered - result = getLightingRatio(position, frame, date); } + + // Final term + result = result.subtract(n - 1); + return result; } - /** Get the lighting ratio ([0-1]). - * Considers only central body as occulting body. - * @param position the satellite's position in the selected frame. - * @param frame in which is defined the position - * @param date the date + * @param state spacecraft state * @param extends CalculusFieldElement * @return lighting ratio - * @since 7.1 + * @since 7.1 */ - public > T getLightingRatio(final FieldVector3D position, - final Frame frame, - final FieldAbsoluteDate date) { - - final T one = date.getField().getOne(); - - final FieldVector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition(); - if (sunPosition.getNorm().getReal() < 2 * Constants.SUN_RADIUS) { - // we are in fact computing a trajectory around Sun (or solar system barycenter), - // not around a planet,we consider lighting ratio is always 1 - return one; - } - - // Compute useful angles - final T[] angle = getEclipseAngles(sunPosition, position); - - // Sat-Sun / Sat-CentralBody angle - final T sunsatCentralBodyAngle = angle[0]; - - // Central Body apparent radius - final T alphaCentral = angle[1]; - - // Sun apparent radius - final T alphaSun = angle[2]; - - T result = one; - // Is the satellite in complete umbra ? - if (sunsatCentralBodyAngle.getReal() - alphaCentral.getReal() + alphaSun.getReal() <= ANGULAR_MARGIN) { - result = date.getField().getZero(); - } else if (sunsatCentralBodyAngle.getReal() - alphaCentral.getReal() - alphaSun.getReal() < -ANGULAR_MARGIN) { - // Compute a lighting ratio in penumbra - final T sEA2 = sunsatCentralBodyAngle.multiply(sunsatCentralBodyAngle); - final T oo2sEA = sunsatCentralBodyAngle.multiply(2).reciprocal(); - final T aS2 = alphaSun.multiply(alphaSun); - final T aE2 = alphaCentral.multiply(alphaCentral); - final T aE2maS2 = aE2.subtract(aS2); - - final T alpha1 = sEA2.subtract(aE2maS2).multiply(oo2sEA); - final T alpha2 = sEA2.add(aE2maS2).multiply(oo2sEA); - - // Protection against numerical inaccuracy at boundaries - final double almost0 = Precision.SAFE_MIN; - final double almost1 = FastMath.nextDown(1.0); - final T a1oaS = min(almost1, max(-almost1, alpha1.divide(alphaSun))); - final T aS2ma12 = max(almost0, aS2.subtract(alpha1.multiply(alpha1))); - final T a2oaE = min(almost1, max(-almost1, alpha2.divide(alphaCentral))); - final T aE2ma22 = max(almost0, aE2.subtract(alpha2.multiply(alpha2))); - - final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt())); - final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt())); - - result = one.subtract(P1.add(P2).divide(aS2.multiply(one.getPi()))); - } - - return result; + public > T getLightingRatio(final FieldSpacecraftState state) { + return getLightingRatio(state, sun.getPosition(state.getDate(), state.getFrame())); } - /** Get eclipse ratio between to bodies seen from a specific object. - * Ratio is in [0-1]. - * @param position the satellite's position in the selected frame - * @param occultingPosition the position of the occulting object - * @param occultingRadius the mean radius of the occulting object - * @param occultedPosition the position of the occulted object - * @param occultedRadius the mean radius of the occulted object - * @param extends RealFieldElement - * @return eclipse ratio + /** Get the masking ratio ([0-1]) considering one pair of bodies. + * @param angles occultation angles + * @param type of the field elements + * @return masking ratio: 0.0 body fully masked, 1.0 body fully visible + * @since 12.0 */ - public > T getGeneralEclipseRatio(final FieldVector3D position, - final FieldVector3D occultingPosition, - final T occultingRadius, - final FieldVector3D occultedPosition, - final T occultedRadius) { + private > T maskingRatio(final OccultationEngine.FieldOccultationAngles angles) { - final T one = occultingRadius.getField().getOne(); - - // Compute useful angles - final T[] angle = getGeneralEclipseAngles(position, occultingPosition, occultingRadius, occultedPosition, occultedRadius); - // Sat-Occulted/ Sat-Occulting angle - final T occultedSatOcculting = angle[0]; + final T occultedSatOcculting = angles.getSeparation(); // Occulting apparent radius - final T alphaOcculting = angle[1]; + final T alphaOcculting = angles.getLimbRadius(); // Occulted apparent radius - final T alphaOcculted = angle[2]; - - T result = one; + final T alphaOcculted = angles.getOccultedApparentRadius(); // Is the satellite in complete umbra ? if (occultedSatOcculting.getReal() - alphaOcculting.getReal() + alphaOcculted.getReal() <= ANGULAR_MARGIN) { - result = occultingRadius.getField().getZero(); + return occultedSatOcculting.getField().getZero(); } else if (occultedSatOcculting.getReal() - alphaOcculting.getReal() - alphaOcculted.getReal() < -ANGULAR_MARGIN) { - // Compute a lighting ratio in penumbra + // Compute a masking ratio in penumbra final T sEA2 = occultedSatOcculting.multiply(occultedSatOcculting); final T oo2sEA = occultedSatOcculting.multiply(2).reciprocal(); final T aS2 = alphaOcculted.multiply(alphaOcculted); @@ -496,107 +421,12 @@ public > T getGeneralEclipseRatio(final FieldV final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt())); final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt())); - result = one.subtract(P1.add(P2).divide(aS2.multiply(one.getPi()))); - } - - return result; - } - - /** Get the total lighting ratio ([0-1]). - * This method considers every occulting bodies. - * @param position the satellite's position in the selected frame. - * @param frame in which is defined the position - * @param date the date - * @param extends RealFieldElement - * @return lighting rati - */ - public > T getTotalLightingRatio(final FieldVector3D position, final Frame frame, final FieldAbsoluteDate date) { - - final T zero = position.getAlpha().getField().getZero(); - T result = zero; - final Map otherOccultingBodies = getOtherOccultingBodies(); - final FieldVector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition(); - final int n = otherOccultingBodies.size() + 1; - - if (n > 1) { - - final List> occultingBodyPositions = new ArrayList>(n); - final T[] occultingBodyRadiuses = MathArrays.buildArray(zero.getField(), n); - - // Central body - occultingBodyPositions.add(0, new FieldVector3D<>(zero, zero, zero)); - occultingBodyRadiuses[0] = zero.add(getEquatorialRadius()); - - // Other occulting bodies - int k = 1; - for (Map.Entry entry: otherOccultingBodies.entrySet()) { - occultingBodyPositions.add(k, entry.getKey().getPVCoordinates(date, frame).getPosition()); - occultingBodyRadiuses[k] = zero.newInstance(entry.getValue()); - ++k; - } - for (int i = 0; i < n; ++i) { - - // Lighting ratio computations - final T eclipseRatioI = getGeneralEclipseRatio(position, - occultingBodyPositions.get(i), - occultingBodyRadiuses[i], - sunPosition, - zero.add(Constants.SUN_RADIUS)); - - // First body totally occults Sun, full eclipse is occuring. - if (eclipseRatioI.getReal() == 0.0) { - return zero; - } - - - result = result.add(eclipseRatioI); - - - // Mutual occulting body eclipse ratio computations between first and secondary bodies - for (int j = i + 1; j < n; ++j) { - - final T eclipseRatioJ = getGeneralEclipseRatio(position, - occultingBodyPositions.get(i), - occultingBodyRadiuses[j], - sunPosition, - zero.add(Constants.SUN_RADIUS)); - final T eclipseRatioIJ = getGeneralEclipseRatio(position, - occultingBodyPositions.get(i), - occultingBodyRadiuses[i], - occultingBodyPositions.get(j), - occultingBodyRadiuses[j]); - - final T alphaJ = getGeneralEclipseAngles(position, - occultingBodyPositions.get(i), - occultingBodyRadiuses[i], - occultingBodyPositions.get(j), - occultingBodyRadiuses[j])[2]; - - final T alphaSun = getEclipseAngles(sunPosition, position)[2]; - final T alphaJSq = alphaJ.multiply(alphaJ); - final T alphaSunSq = alphaSun.multiply(alphaSun); - final T mutualEclipseCorrection = eclipseRatioIJ.negate().add(1).multiply(alphaJSq).divide(alphaSunSq); - - // Secondary body totaly occults Sun, no more computations are required, full eclipse is occuring. - if (eclipseRatioJ.getReal() == 0.0 ) { - return zero; - } - - // Secondary body partially occults Sun - else if (eclipseRatioJ.getReal() != 1) { - result = result.subtract(mutualEclipseCorrection); - } - } - } - // Final term - result = result.subtract(n - 1); + return occultedSatOcculting.getField().getOne().subtract(P1.add(P2).divide(aS2.multiply(occultedSatOcculting.getPi()))); } else { - // only central body is considered - result = getLightingRatio(position, frame, date); + return occultedSatOcculting.getField().getOne(); } - return result; - } + } /** {@inheritDoc} */ @Override @@ -607,7 +437,7 @@ public List getParametersDrivers() { /** Compute min of two values, one double and one field element. * @param d double value * @param f field element - * @param type fo the field elements + * @param type of the field elements * @return min value */ private > T min(final double d, final T f) { @@ -617,7 +447,7 @@ private > T min(final double d, final T f) { /** Compute max of two values, one double and one field element. * @param d double value * @param f field element - * @param type fo the field elements + * @param type of the field elements * @return max value */ private > T max(final double d, final T f) { diff --git a/src/main/java/org/orekit/forces/radiation/package-info.java b/src/main/java/org/orekit/forces/radiation/package-info.java index 0115e7856a..c80749d478 100644 --- a/src/main/java/org/orekit/forces/radiation/package-info.java +++ b/src/main/java/org/orekit/forces/radiation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/AbstractEopParser.java b/src/main/java/org/orekit/frames/AbstractEopParser.java index d1a5b498fc..40a763ad8a 100644 --- a/src/main/java/org/orekit/frames/AbstractEopParser.java +++ b/src/main/java/org/orekit/frames/AbstractEopParser.java @@ -16,7 +16,7 @@ */ package org.orekit.frames; -import org.orekit.frames.EOPHistoryLoader.Parser; +import org.orekit.frames.EopHistoryLoader.Parser; import org.orekit.time.TimeScale; import org.orekit.utils.IERSConventions; import org.orekit.utils.IERSConventions.NutationCorrectionConverter; diff --git a/src/main/java/org/orekit/frames/AbstractFrames.java b/src/main/java/org/orekit/frames/AbstractFrames.java index ddd22f5587..5e930c4c60 100644 --- a/src/main/java/org/orekit/frames/AbstractFrames.java +++ b/src/main/java/org/orekit/frames/AbstractFrames.java @@ -25,6 +25,7 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScales; +import org.orekit.time.UT1Scale; import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.Constants; @@ -288,11 +289,6 @@ public FactoryManagedFrame getITRF(final IERSConventions conventions, } } - @Override - public FactoryManagedFrame getTIRF(final IERSConventions conventions) { - return getTIRF(conventions, true); - } - @Override public VersionedITRF getITRF(final ITRFVersion version, final IERSConventions conventions, @@ -319,6 +315,37 @@ public VersionedITRF getITRF(final ITRFVersion version, } } + @Override + public Frame buildUncachedITRF(final UT1Scale ut1) { + + // extract EOP history + final EOPHistory eopHistory = ut1.getEOPHistory(); + + // build CIRF + final TransformProvider shifting = + new ShiftingTransformProvider(new CIRFProvider(eopHistory), + CartesianDerivativesFilter.USE_PVA, + AngularDerivativesFilter.USE_R, + 6, Constants.JULIAN_DAY / 24, + OrekitConfiguration.getCacheSlotsNumber(), + Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY); + final Frame cirf = new Frame(getGCRF(), shifting, "CIRF (uncached)", true); + + // build TIRF + final Frame tirf = new Frame(cirf, new TIRFProvider(eopHistory, ut1), + "TIRF (uncached)", false); + + // build ITRF + return new Frame(tirf, new ITRFProvider(eopHistory), + "ITRF (uncached)", false); + + } + + @Override + public FactoryManagedFrame getTIRF(final IERSConventions conventions) { + return getTIRF(conventions, true); + } + @Override public FactoryManagedFrame getTIRF(final IERSConventions conventions, final boolean simpleEOP) { diff --git a/src/main/java/org/orekit/frames/BulletinAFilesLoader.java b/src/main/java/org/orekit/frames/BulletinAFilesLoader.java index ed4f5c4659..6ec549feb7 100644 --- a/src/main/java/org/orekit/frames/BulletinAFilesLoader.java +++ b/src/main/java/org/orekit/frames/BulletinAFilesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -114,12 +114,19 @@ * where x stands for a roman numeral character and # stands for a digit * character.

        *

        + * Bulletin A in csv format must be read using {@link EopCsvFilesLoader} rather + * than using this loader. Bulletin A in xml format must be read using {@link EopXmlLoader} + * rather than using this loader. + *

        + *

        * This class is immutable and hence thread-safe *

        * @author Luc Maisonobe * @since 7.0 + * @see EopCsvFilesLoader + * @see EopXmlLoader */ -class BulletinAFilesLoader extends AbstractEopLoader implements EOPHistoryLoader { +class BulletinAFilesLoader extends AbstractEopLoader implements EopHistoryLoader { /** Regular expression matching blanks at start of line. */ private static final String LINE_START_REGEXP = "^\\p{Blank}+"; @@ -485,7 +492,6 @@ public void fill(final SortedSet history) { final AbsoluteDate mjdDate = AbsoluteDate.createMJDDate(mjd, 0, getUtc()); final double[] currentPole = poleOffsetsFieldsMap.get(mjd); - final double[] previousEOP = currentEOP; currentEOP = nextEOP; nextEOP = eopFieldsMap.get(mjd + 1); @@ -497,7 +503,7 @@ public void fill(final SortedSet history) { configuration = itrfVersionProvider.getConfiguration(fileName, mjd); } history.add(new EOPEntry(mjd, - 0.0, 0.0, 0.0, 0.0, + 0.0, Double.NaN, 0.0, 0.0, Double.NaN, Double.NaN, UnitsConverter.MILLI_ARC_SECONDS_TO_RADIANS.convert(currentPole[1]), UnitsConverter.MILLI_ARC_SECONDS_TO_RADIANS.convert(currentPole[2]), UnitsConverter.MILLI_ARC_SECONDS_TO_RADIANS.convert(currentPole[3]), @@ -507,26 +513,6 @@ public void fill(final SortedSet history) { } } else { - // compute LOD as the opposite of the time derivative of UT1-UTC - final double lod; - if (previousEOP == null) { - if (nextEOP == null) { - // isolated point - lod = 0; - } else { - // first entry, we use a forward difference - lod = currentEOP[3] - nextEOP[3]; - } - } else { - if (nextEOP == null) { - // last entry, we use a backward difference - lod = previousEOP[3] - currentEOP[3]; - } else { - // regular entry, we use a centered difference - lod = 0.5 * (previousEOP[3] - nextEOP[3]); - } - } - if (configuration == null || !configuration.isValid(mjd)) { // get a configuration for current name and date range configuration = itrfVersionProvider.getConfiguration(fileName, mjd); @@ -534,18 +520,20 @@ public void fill(final SortedSet history) { if (currentPole == null) { // we have only EOP for this date history.add(new EOPEntry(mjd, - currentEOP[3], lod, + currentEOP[3], Double.NaN, UnitsConverter.ARC_SECONDS_TO_RADIANS.convert(currentEOP[1]), UnitsConverter.ARC_SECONDS_TO_RADIANS.convert(currentEOP[2]), + Double.NaN, Double.NaN, 0.0, 0.0, 0.0, 0.0, configuration.getVersion(), mjdDate)); } else { // we have complete data history.add(new EOPEntry(mjd, - currentEOP[3], lod, + currentEOP[3], Double.NaN, UnitsConverter.ARC_SECONDS_TO_RADIANS.convert(currentEOP[1] ), UnitsConverter.ARC_SECONDS_TO_RADIANS.convert(currentEOP[2] ), + Double.NaN, Double.NaN, UnitsConverter.MILLI_ARC_SECONDS_TO_RADIANS.convert(currentPole[1]), UnitsConverter.MILLI_ARC_SECONDS_TO_RADIANS.convert(currentPole[2]), UnitsConverter.MILLI_ARC_SECONDS_TO_RADIANS.convert(currentPole[3]), diff --git a/src/main/java/org/orekit/frames/BulletinBFilesLoader.java b/src/main/java/org/orekit/frames/BulletinBFilesLoader.java index 000192adbe..78904bcd27 100644 --- a/src/main/java/org/orekit/frames/BulletinBFilesLoader.java +++ b/src/main/java/org/orekit/frames/BulletinBFilesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -74,14 +74,21 @@ * files with different name was published each month between March 2003 and January 2010). *

        *

        + * Bulletin B in csv format must be read using {@link EopCsvFilesLoader} rather + * than using this loader. Bulletin B in xml format must be read using {@link EopXmlLoader} + * rather than using this loader. + *

        + *

        * This class handles both the old and the new format. *

        *

        * This class is immutable and hence thread-safe *

        * @author Luc Maisonobe + * @see EopCsvFilesLoader + * @see EopXmlLoader */ -class BulletinBFilesLoader extends AbstractEopLoader implements EOPHistoryLoader { +class BulletinBFilesLoader extends AbstractEopLoader implements EopHistoryLoader { /** Section 1 header pattern. */ private static final Pattern SECTION_1_HEADER; @@ -337,6 +344,7 @@ public Collection parse(final InputStream input, final String name) configuration = getItrfVersionProvider().getConfiguration(name, mjd); } history.add(new EOPEntry(mjd, array[0], array[1], array[2], array[3], + Double.NaN, Double.NaN, equinox[0], equinox[1], array[4], array[5], configuration.getVersion(), mjdDate)); } @@ -454,7 +462,8 @@ private void loadEOPOldFormat(final boolean isNonRotatingOrigin, // get a configuration for current name and date range configuration = getItrfVersionProvider().getConfiguration(name, mjd); } - history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1], + history.add(new EOPEntry(mjd, dtu1, lod, x, y, Double.NaN, Double.NaN, + equinox[0], equinox[1], nro[0], nro[1], configuration.getVersion(), mjdDate)); line = mjd < mjdMax ? reader.readLine() : null; } else { diff --git a/src/main/java/org/orekit/frames/CIRFProvider.java b/src/main/java/org/orekit/frames/CIRFProvider.java index 62be895135..4442ab8c4a 100644 --- a/src/main/java/org/orekit/frames/CIRFProvider.java +++ b/src/main/java/org/orekit/frames/CIRFProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -78,7 +78,7 @@ public EOPHistory getEOPHistory() { /** {@inheritDoc} */ @Override public CIRFProvider getNonInterpolatingProvider() { - return new CIRFProvider(eopHistory.getNonInterpolatingEOPHistory()); + return new CIRFProvider(eopHistory.getEOPHistoryWithoutCachedTidalCorrection()); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/frames/CR3BPRotatingFrame.java b/src/main/java/org/orekit/frames/CR3BPRotatingFrame.java index 41161752ba..64592e87ab 100644 --- a/src/main/java/org/orekit/frames/CR3BPRotatingFrame.java +++ b/src/main/java/org/orekit/frames/CR3BPRotatingFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/CR3BPRotatingTransformProvider.java b/src/main/java/org/orekit/frames/CR3BPRotatingTransformProvider.java index ce6a2e54e1..8c7eb809bf 100644 --- a/src/main/java/org/orekit/frames/CR3BPRotatingTransformProvider.java +++ b/src/main/java/org/orekit/frames/CR3BPRotatingTransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,8 @@ */ package org.orekit.frames; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.geometry.euclidean.threed.FieldRotation; @@ -30,6 +30,7 @@ import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; /** Transform provider for the rotating frame of the CR3BP System. @@ -128,4 +129,22 @@ public > FieldTransform getTransform(final final FieldTransform transform2 = new FieldTransform<>(date, rotationField, rotationRate, rotationAcc); return new FieldTransform<>(date, transform2, transform1); } + + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + final Field field = date.getField(); + final TimeStampedFieldPVCoordinates pv = secondaryBody.getPVCoordinates(date, frame); + final FieldVector3D translation = FieldVector3D.getPlusI(field). + scalarMultiply(pv.getPosition().getNorm().multiply(mu)).negate(); + + final FieldRotation rotation = new FieldRotation<>( + pv.getPosition(), pv.getMomentum(), + FieldVector3D.getPlusI(field), FieldVector3D.getMinusK(field)); + + final FieldStaticTransform transform1 = FieldStaticTransform.of(date, translation); + final FieldStaticTransform transform2 = FieldStaticTransform.of(date, rotation); + return FieldStaticTransform.compose(date, transform2, transform1); + } + } diff --git a/src/main/java/org/orekit/frames/EME2000Provider.java b/src/main/java/org/orekit/frames/EME2000Provider.java index 378c6a2ae7..673a04e7f9 100644 --- a/src/main/java/org/orekit/frames/EME2000Provider.java +++ b/src/main/java/org/orekit/frames/EME2000Provider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/EOPBasedTransformProvider.java b/src/main/java/org/orekit/frames/EOPBasedTransformProvider.java index 34aa06a233..784e9d583d 100644 --- a/src/main/java/org/orekit/frames/EOPBasedTransformProvider.java +++ b/src/main/java/org/orekit/frames/EOPBasedTransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/EOPC04FilesLoader.java b/src/main/java/org/orekit/frames/EOPC04FilesLoader.java deleted file mode 100644 index 4cc7798e27..0000000000 --- a/src/main/java/org/orekit/frames/EOPC04FilesLoader.java +++ /dev/null @@ -1,279 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.frames; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.SortedSet; -import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.orekit.data.DataProvidersManager; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.DateComponents; -import org.orekit.time.TimeScale; -import org.orekit.utils.Constants; -import org.orekit.utils.IERSConventions; -import org.orekit.utils.IERSConventions.NutationCorrectionConverter; - -/** Loader for EOP xx C04 files. - *

        EOP xx C04 files contain {@link EOPEntry - * Earth Orientation Parameters} consistent with ITRF20xx for one year periods, with various - * xx (05, 08, 14) depending on the data source.

        - *
        - *

        - * Beware that files retrieved from the web site - * http://hpiers.obspm.fr/eoppc/eop/ do - * not follow the same naming convention. They lack the _05, _08 or _14 - * marker (and for EOP 14 C04 even the directory does not have this marker anymore...), - * so all the files in this site have the same name and can easily be confused. This is the reason - * why the default regular expression in {@link FramesFactory} does not recognize them and - * enforces the year marker presence. - *

        - *

        Between 2002 and 2007, another series of Earth Orientation Parameters was - * in use: EOPC04 (without the _##). These parameters were consistent with the - * previous ITRS realization: ITRF2000.

        - *

        Since 2008, several series of Earth Orientation Parameters have been available: - * EOP 05 C04 consistent with ITRF 2005, EOP 08 C04 consistent with ITRF 2008, and - * EOP 14 C04 consistent with ITRF 2014. As of mid 2017, only the EOP 08 C04 and - * EOP 14 C04 are updated for current years.

        - *

        Files are available at IERS FTP site: ftp://ftp.iers.org/products/eop/long-term/.

        - *

        - * This class is immutable and hence thread-safe - *

        - * @author Luc Maisonobe - */ -class EOPC04FilesLoader extends AbstractEopLoader implements EOPHistoryLoader { - - /** Pattern for delimiting regular expressions. */ - private static final Pattern SEPARATOR; - - /** Pattern to match the columns header. */ - private static final Pattern COLUMNS_HEADER_PATTERN; - - /** Pattern for data lines. */ - private static final Pattern DATA_LINE_PATTERN; - - /** Year field. */ - private static final int YEAR_FIELD; - - /** Month field. */ - private static final int MONTH_FIELD; - - /** Day field. */ - private static final int DAY_FIELD; - - /** MJD field. */ - private static final int MJD_FIELD; - - /** X component of pole motion field. */ - private static final int POLE_X_FIELD; - - /** Y component of pole motion field. */ - private static final int POLE_Y_FIELD; - - /** UT1-UTC field. */ - private static final int UT1_UTC_FIELD; - - /** LoD field. */ - private static final int LOD_FIELD; - - /** Correction for nutation first field (either dX or dPsi). */ - private static final int NUT_0_FIELD; - - /** Correction for nutation second field (either dY or dEps). */ - private static final int NUT_1_FIELD; - - static { - - SEPARATOR = Pattern.compile(" +"); - - // Header have either the following form: - // Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err - // " " s s " " " " s s " " - // (0h UTC) - // or the following form: - // Date MJD x y UT1-UTC LOD dX dY x Err y Err UT1-UTC Err LOD Err dX Err dY Err - // " " s s " " " " s s " " - // (0h UTC) - // - COLUMNS_HEADER_PATTERN = Pattern.compile("^ *Date +MJD +x +y +UT1-UTC +LOD +((?:dPsi +dEps)|(?:dX +dY)) .*"); - - // The data lines in the EOP C04 yearly data files have the following fixed form: - // year month day MJD ...12 floating values fields in decimal format... - // 2000 1 1 51544 0.043242 0.377915 0.3554777 ... - // 2000 1 2 51545 0.043515 0.377753 0.3546065 ... - // 2000 1 3 51546 0.043623 0.377452 0.3538444 ... - // the corresponding fortran format is: - // 3(I4),I7,2(F11.6),2(F12.7),2(F12.6),2(F11.6),2(F12.7),2F12.6 - DATA_LINE_PATTERN = Pattern.compile("^\\d+ +\\d+ +\\d+ +\\d+(?: +-?\\d+\\.\\d+){12}$"); - - YEAR_FIELD = 0; - MONTH_FIELD = 1; - DAY_FIELD = 2; - MJD_FIELD = 3; - POLE_X_FIELD = 4; - POLE_Y_FIELD = 5; - UT1_UTC_FIELD = 6; - LOD_FIELD = 7; - NUT_0_FIELD = 8; - NUT_1_FIELD = 9; - - } - - /** Build a loader for IERS EOP C04 files. - * @param supportedNames regular expression for supported files names - * @param manager provides access to the EOP C04 files. - * @param utcSupplier UTC time scale. - */ - EOPC04FilesLoader(final String supportedNames, - final DataProvidersManager manager, - final Supplier utcSupplier) { - super(supportedNames, manager, utcSupplier); - } - - /** {@inheritDoc} */ - public void fillHistory(final IERSConventions.NutationCorrectionConverter converter, - final SortedSet history) { - final ItrfVersionProvider itrfVersionProvider = new ITRFVersionLoader( - ITRFVersionLoader.SUPPORTED_NAMES, - getDataProvidersManager()); - final Parser parser = new Parser(converter, itrfVersionProvider, getUtc()); - final EopParserLoader loader = new EopParserLoader(parser); - this.feed(loader); - history.addAll(loader.getEop()); - } - - /** Internal class performing the parsing. */ - static class Parser extends AbstractEopParser { - - /** - * Simple constructor. - * - * @param converter converter to use - * @param itrfVersionProvider to use for determining the ITRF version of the EOP. - * @param utc time scale for parsing dates. - */ - Parser(final NutationCorrectionConverter converter, - final ItrfVersionProvider itrfVersionProvider, - final TimeScale utc) { - super(converter, itrfVersionProvider, utc); - } - - /** {@inheritDoc} */ - public Collection parse(final InputStream input, final String name) - throws IOException, OrekitException { - - final List history = new ArrayList<>(); - ITRFVersionLoader.ITRFVersionConfiguration configuration = null; - - // set up a reader for line-oriented bulletin B files - try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { - // reset parse info to start new file (do not clear history!) - int lineNumber = 0; - boolean inHeader = true; - boolean isNonRotatingOrigin = false; - - // read all file - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - ++lineNumber; - boolean parsed = false; - - if (inHeader) { - final Matcher matcher = COLUMNS_HEADER_PATTERN.matcher(line); - if (matcher.matches()) { - if (matcher.group(1).startsWith("dX")) { - isNonRotatingOrigin = true; - } - } - } - - if (DATA_LINE_PATTERN.matcher(line).matches()) { - inHeader = false; - // this is a data line, build an entry from the extracted fields - final String[] fields = SEPARATOR.split(line); - final DateComponents dc = new DateComponents(Integer.parseInt(fields[YEAR_FIELD]), - Integer.parseInt(fields[MONTH_FIELD]), - Integer.parseInt(fields[DAY_FIELD])); - final int mjd = Integer.parseInt(fields[MJD_FIELD]); - if (dc.getMJD() != mjd) { - throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE, - name, dc.getYear(), dc.getMonth(), dc.getDay(), mjd); - } - final AbsoluteDate date = new AbsoluteDate(dc, getUtc()); - - // the first six fields are consistent with the expected format - final double x = Double.parseDouble(fields[POLE_X_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS; - final double y = Double.parseDouble(fields[POLE_Y_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS; - final double dtu1 = Double.parseDouble(fields[UT1_UTC_FIELD]); - final double lod = Double.parseDouble(fields[LOD_FIELD]); - final double[] equinox; - final double[] nro; - if (isNonRotatingOrigin) { - nro = new double[] { - Double.parseDouble(fields[NUT_0_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS, - Double.parseDouble(fields[NUT_1_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS - }; - equinox = getConverter().toEquinox(date, nro[0], nro[1]); - } else { - equinox = new double[] { - Double.parseDouble(fields[NUT_0_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS, - Double.parseDouble(fields[NUT_1_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS - }; - nro = getConverter().toNonRotating(date, equinox[0], equinox[1]); - } - if (configuration == null || !configuration.isValid(mjd)) { - // get a configuration for current name and date range - configuration = getItrfVersionProvider().getConfiguration(name, mjd); - } - history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1], - configuration.getVersion(), date)); - parsed = true; - - } - if (!(inHeader || parsed)) { - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - } - - // check if we have read something - if (inHeader) { - throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, name); - } - } - - return history; - } - - } - -} diff --git a/src/main/java/org/orekit/frames/EOPEntry.java b/src/main/java/org/orekit/frames/EOPEntry.java index 744677a474..5388b13707 100644 --- a/src/main/java/org/orekit/frames/EOPEntry.java +++ b/src/main/java/org/orekit/frames/EOPEntry.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,7 +27,7 @@ public class EOPEntry implements TimeStamped, Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 20180330L; + private static final long serialVersionUID = 20231017L; /** Entry date (modified julian day, 00h00 UTC scale). */ private final int mjd; @@ -47,6 +47,16 @@ public class EOPEntry implements TimeStamped, Serializable { /** Y component of pole motion. */ private final double y; + /** X component of pole motion rate. + * @since 12.0 + */ + private final double xRate; + + /** Y component of pole motion rate. + * @since 12.0 + */ + private final double yRate; + /** Correction for nutation in longitude. */ private final double ddPsi; @@ -68,16 +78,18 @@ public class EOPEntry implements TimeStamped, Serializable { * @param lod length of day * @param x X component of pole motion * @param y Y component of pole motion + * @param xRate X component of pole motion rate (NaN if absent) + * @param yRate Y component of pole motion rate (NaN if absent) * @param ddPsi correction for nutation in longitude δΔΨ * @param ddEps correction for nutation in obliquity δΔε * @param dx correction for Celestial Intermediate Pole (CIP) coordinates * @param dy correction for Celestial Intermediate Pole (CIP) coordinates * @param itrfType ITRF version this entry defines * @param date corresponding to {@code mjd}. - * @since 10.1 + * @since 12.0 */ public EOPEntry(final int mjd, final double dt, final double lod, - final double x, final double y, + final double x, final double y, final double xRate, final double yRate, final double ddPsi, final double ddEps, final double dx, final double dy, final ITRFVersion itrfType, final AbsoluteDate date) { @@ -88,6 +100,8 @@ public EOPEntry(final int mjd, final double dt, final double lod, this.lod = lod; this.x = x; this.y = y; + this.xRate = xRate; + this.yRate = yRate; this.ddPsi = ddPsi; this.ddEps = ddEps; this.dx = dx; @@ -137,6 +151,22 @@ public double getY() { return y; } + /** Get the X component of the pole motion rate. + * @return X component of pole motion rate + * @since 12.0 + */ + public double getXRate() { + return xRate; + } + + /** Get the Y component of the pole motion rate. + * @return Y component of pole motion rate + * @since 12.0 + */ + public double getYRate() { + return yRate; + } + /** Get the correction for nutation in longitude δΔΨ. * @return correction for nutation in longitude δΔΨ */ diff --git a/src/main/java/org/orekit/frames/EOPFittedModel.java b/src/main/java/org/orekit/frames/EOPFittedModel.java new file mode 100644 index 0000000000..c86c2f0975 --- /dev/null +++ b/src/main/java/org/orekit/frames/EOPFittedModel.java @@ -0,0 +1,99 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import org.orekit.utils.SecularAndHarmonic; + +/** Container for fitted model for Earth Orientation Parameters. + * @see PredictedEOPHistory + * @see EOPFitter + * @since 12.0 + * @author Luc Maisonobe + */ +public class EOPFittedModel { + + /** Fitted model for dut1 and LOD. */ + private final SecularAndHarmonic dut1; + + /** Fitted model for pole x component. */ + private final SecularAndHarmonic xP; + + /** Fitted model for pole y component. */ + private final SecularAndHarmonic yP; + + /** Fitted model for nutation x component. */ + private final SecularAndHarmonic dx; + + /** Fitted model for nutation y component. */ + private final SecularAndHarmonic dy; + + /** Simple constructor. + * @param dut1 fitted model for dut1 and LOD + * @param xP fitted model for pole x component + * @param yP fitted model for pole y component + * @param dx fitted model for nutation x component + * @param dy fitted model for nutation y component + */ + public EOPFittedModel(final SecularAndHarmonic dut1, + final SecularAndHarmonic xP, final SecularAndHarmonic yP, + final SecularAndHarmonic dx, final SecularAndHarmonic dy) { + this.dut1 = dut1; + this.xP = xP; + this.yP = yP; + this.dx = dx; + this.dy = dy; + } + + /** Get the fitted secular and harmonics model for DUT1/LOD. + *

        + * LOD can be computed from DUT1 as {@code -Constants.JULIAN_DAY * getDUT1().osculatingDerivative(date)} + *

        + * @return fitted secular and harmonics model for DUT1/LOD + */ + public SecularAndHarmonic getDUT1() { + return dut1; + } + + /** Get the fitted secular and harmonics model for pole x component. + * @return fitted secular and harmonics model for pole x component + */ + public SecularAndHarmonic getXp() { + return xP; + } + + /** Get the fitted secular and harmonics model for pole y component. + * @return fitted secular and harmonics model for pole y component + */ + public SecularAndHarmonic getYp() { + return yP; + } + + /** Get the fitted secular and harmonics model for nutation x component. + * @return fitted secular and harmonics model for nutation x component + */ + public SecularAndHarmonic getDx() { + return dx; + } + + /** Get the fitted secular and harmonics model for nutation y component. + * @return fitted secular and harmonics model for nutation y component + */ + public SecularAndHarmonic getDy() { + return dy; + } + +} diff --git a/src/main/java/org/orekit/frames/EOPFitter.java b/src/main/java/org/orekit/frames/EOPFitter.java new file mode 100644 index 0000000000..6bf1f22bcf --- /dev/null +++ b/src/main/java/org/orekit/frames/EOPFitter.java @@ -0,0 +1,76 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import java.io.Serializable; + +/** Earth Orientation Parameters fitter for {@link PredictedEOPHistory EOP prediction}. + * @see PredictedEOPHistory + * @see SingleParameterFitter + * @since 12.0 + * @author Luc Maisonobe + */ +public class EOPFitter implements Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = 20230309L; + + /** Fitter for dut1 and LOD. */ + private final SingleParameterFitter dut1Fitter; + + /** Fitter for pole x component. */ + private final SingleParameterFitter xPFitter; + + /** Fitter for pole y component. */ + private final SingleParameterFitter yPFitter; + + /** Fitter for nutation x component. */ + private final SingleParameterFitter dxFitter; + + /** Fitter for nutation y component. */ + private final SingleParameterFitter dyFitter; + + /** Simple constructor. + * @param dut1Fitter fitter for dut1 and LOD + * @param xPFitter fitter for pole x component + * @param yPFitter fitter for pole y component + * @param dxFitter fitter for nutation x component + * @param dyFitter fitter for nutation y component + */ + public EOPFitter(final SingleParameterFitter dut1Fitter, + final SingleParameterFitter xPFitter, final SingleParameterFitter yPFitter, + final SingleParameterFitter dxFitter, final SingleParameterFitter dyFitter) { + this.dut1Fitter = dut1Fitter; + this.xPFitter = xPFitter; + this.yPFitter = yPFitter; + this.dxFitter = dxFitter; + this.dyFitter = dyFitter; + } + + /** Fit raw history. + * @param rawHistory raw EOP history to fit. + * @return fitted model + */ + public EOPFittedModel fit(final EOPHistory rawHistory) { + return new EOPFittedModel(dut1Fitter.fit(rawHistory, entry -> entry.getUT1MinusUTC()), + xPFitter.fit(rawHistory, entry -> entry.getX()), + yPFitter.fit(rawHistory, entry -> entry.getY()), + dxFitter.fit(rawHistory, entry -> entry.getDx()), + dyFitter.fit(rawHistory, entry -> entry.getDy())); + } + +} diff --git a/src/main/java/org/orekit/frames/EOPHistory.java b/src/main/java/org/orekit/frames/EOPHistory.java index 0e8de87a6c..cf787b1630 100644 --- a/src/main/java/org/orekit/frames/EOPHistory.java +++ b/src/main/java/org/orekit/frames/EOPHistory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Stream; @@ -55,11 +56,18 @@ */ public class EOPHistory implements Serializable { + /** Default interpolation degree. + * @since 12.0 + */ + public static final int DEFAULT_INTERPOLATION_DEGREE = 3; + /** Serializable UID. */ - private static final long serialVersionUID = 20191119L; + private static final long serialVersionUID = 20231022L; - /** Number of points to use in interpolation. */ - private static final int INTERPOLATION_POINTS = 4; + /** Interpolation degree. + * @since 12.0 + */ + private final int interpolationDegree; /** * If this history has any EOP data. @@ -85,55 +93,79 @@ public class EOPHistory implements Serializable { *

        This method uses the {@link DataContext#getDefault() default data context}. * * @param conventions IERS conventions to which EOP refers + * @param interpolationDegree interpolation degree (must be of the form 4k-1) * @param data the EOP data to use * @param simpleEOP if true, tidal effects are ignored when interpolating EOP - * @see #EOPHistory(IERSConventions, Collection, boolean, TimeScales) + * @see #EOPHistory(IERSConventions, int, Collection, boolean, TimeScales) */ @DefaultDataContext protected EOPHistory(final IERSConventions conventions, + final int interpolationDegree, final Collection data, final boolean simpleEOP) { - this(conventions, data, simpleEOP, DataContext.getDefault().getTimeScales()); + this(conventions, interpolationDegree, data, simpleEOP, DataContext.getDefault().getTimeScales()); } /** Simple constructor. * @param conventions IERS conventions to which EOP refers + * @param interpolationDegree interpolation degree (must be of the form 4k-1) * @param data the EOP data to use * @param simpleEOP if true, tidal effects are ignored when interpolating EOP * @param timeScales to use when computing EOP corrections. * @since 10.1 */ public EOPHistory(final IERSConventions conventions, + final int interpolationDegree, final Collection data, final boolean simpleEOP, final TimeScales timeScales) { - this(conventions, - data, - simpleEOP ? null : new CachedCorrection(conventions.getEOPTidalCorrection(timeScales)), - timeScales); + this(conventions, interpolationDegree, data, + simpleEOP ? null : new CachedCorrection(conventions.getEOPTidalCorrection(timeScales)), + timeScales); } /** Simple constructor. * @param conventions IERS conventions to which EOP refers + * @param interpolationDegree interpolation degree (must be of the form 4k-1) * @param data the EOP data to use * @param tidalCorrection correction to apply to EOP - * @param timeScales to use when computing EOP corrections. - * @since 10.1 + * @param timeScales to use when computing EOP corrections + * @since 12 */ private EOPHistory(final IERSConventions conventions, + final int interpolationDegree, final Collection data, final TimeVectorFunction tidalCorrection, final TimeScales timeScales) { - this.conventions = conventions; - this.tidalCorrection = tidalCorrection; - this.timeScales = timeScales; + + // check interpolation degree is 4k-1 + final int k = (interpolationDegree + 1) / 4; + if (interpolationDegree != 4 * k - 1) { + throw new OrekitException(OrekitMessages.WRONG_EOP_INTERPOLATION_DEGREE, interpolationDegree); + } + + this.conventions = conventions; + this.interpolationDegree = interpolationDegree; + this.tidalCorrection = tidalCorrection; + this.timeScales = timeScales; if (data.size() >= 1) { // enough data to interpolate - cache = new ImmutableTimeStampedCache(FastMath.min(INTERPOLATION_POINTS, data.size()), data); + if (missSomeDerivatives(data)) { + // we need to estimate the missing derivatives + final ImmutableTimeStampedCache rawCache = + new ImmutableTimeStampedCache<>(FastMath.min(interpolationDegree + 1, data.size()), data); + final ListfixedData = new ArrayList<>(); + for (final EOPEntry entry : rawCache.getAll()) { + fixedData.add(fixDerivatives(entry, rawCache)); + } + cache = new ImmutableTimeStampedCache<>(FastMath.min(interpolationDegree + 1, fixedData.size()), fixedData); + } else { + cache = new ImmutableTimeStampedCache<>(FastMath.min(interpolationDegree + 1, data.size()), data); + } hasData = true; } else { // not enough data to interpolate -> always use null correction - cache = ImmutableTimeStampedCache.emptyCache(); + cache = ImmutableTimeStampedCache.emptyCache(); hasData = false; } } @@ -147,6 +179,14 @@ public boolean isSimpleEop() { return tidalCorrection == null; } + /** Get interpolation degree. + * @return interpolation degree + * @since 12.0 + */ + public int getInterpolationDegree() { + return interpolationDegree; + } + /** * Get the time scales used in computing EOP corrections. * @@ -157,18 +197,21 @@ public TimeScales getTimeScales() { return timeScales; } - /** Get non-interpolating version of the instance. - * @return non-interpolatig version of the instance + /** Get version of the instance that does not cache tidal correction. + * @return version of the instance that does not cache tidal correction + * @since 12.0 */ - public EOPHistory getNonInterpolatingEOPHistory() { - return new EOPHistory(conventions, getEntries(), - conventions.getEOPTidalCorrection(timeScales), timeScales); + public EOPHistory getEOPHistoryWithoutCachedTidalCorrection() { + return new EOPHistory(conventions, interpolationDegree, getEntries(), + conventions.getEOPTidalCorrection(timeScales), + timeScales); } - /** Check if the instance uses interpolation on tidal corrections. - * @return true if the instance uses interpolation on tidal corrections + /** Check if the instance caches tidal corrections. + * @return true if the instance caches tidal corrections + * @since 12.0 */ - public boolean usesInterpolation() { + public boolean cachesTidalCorrection() { return tidalCorrection instanceof CachedCorrection; } @@ -209,7 +252,7 @@ public double getUT1MinusUTC(final AbsoluteDate date) { // we have EOP data -> interpolate offset try { final DUT1Interpolator interpolator = new DUT1Interpolator(date); - getNeighbors(date).forEach(interpolator); + getNeighbors(date, interpolationDegree + 1).forEach(interpolator); double interpolated = interpolator.getInterpolated(); if (tidalCorrection != null) { interpolated += tidalCorrection.value(date)[2]; @@ -241,7 +284,7 @@ public > T getUT1MinusUTC(final FieldAbsoluteD // we have EOP data -> interpolate offset try { final FieldDUT1Interpolator interpolator = new FieldDUT1Interpolator<>(date, absDate); - getNeighbors(absDate).forEach(interpolator); + getNeighbors(absDate, interpolationDegree + 1).forEach(interpolator); T interpolated = interpolator.getInterpolated(); if (tidalCorrection != null) { interpolated = interpolated.add(tidalCorrection.value(date)[2]); @@ -384,10 +427,12 @@ public T getInterpolated() { * for {@code central} without throwing an exception. * * @param central central date + * @param n number of neighbors * @return array of cached entries surrounding specified date + * @since 12.0 */ - protected Stream getNeighbors(final AbsoluteDate central) { - return cache.getNeighbors(central); + protected Stream getNeighbors(final AbsoluteDate central, final int n) { + return cache.getNeighbors(central, n); } /** Get the LoD (Length of Day) value. @@ -459,7 +504,9 @@ public PoleCorrection getPoleCorrection(final AbsoluteDate date) { } // we have EOP data for date -> interpolate correction - final double[] interpolated = interpolate(date, entry -> entry.getX(), entry -> entry.getY()); + final double[] interpolated = interpolate(date, + entry -> entry.getX(), entry -> entry.getY(), + entry -> entry.getXRate(), entry -> entry.getYRate()); if (tidalCorrection != null) { final double[] correction = tidalCorrection.value(date); interpolated[0] += correction[0]; @@ -546,7 +593,7 @@ public > T[] getEquinoxNutationCorrection(fina /** Get the correction to the nutation parameters for Non-Rotating Origin paradigm. *

        The data provided comes from the IERS files. It is smoothed data.

        * @param date date at which the correction is desired - * @return nutation correction in Celestial Intermediat Pole coordinates + * @return nutation correction in Celestial Intermediate Pole coordinates * δX and δY (zero if date is outside covered range) */ public double[] getNonRotatinOriginNutationCorrection(final AbsoluteDate date) { @@ -566,7 +613,7 @@ public double[] getNonRotatinOriginNutationCorrection(final AbsoluteDate date) { *

        The data provided comes from the IERS files. It is smoothed data.

        * @param date date at which the correction is desired * @param type of the filed elements - * @return nutation correction in Celestial Intermediat Pole coordinates + * @return nutation correction in Celestial Intermediate Pole coordinates * δX and δY (zero if date is outside covered range) */ public > T[] getNonRotatinOriginNutationCorrection(final FieldAbsoluteDate date) { @@ -599,7 +646,7 @@ public ITRFVersion getITRFVersion(final AbsoluteDate date) { try { // we have EOP data for date - final Optional first = getNeighbors(date).findFirst(); + final Optional first = getNeighbors(date, 1).findFirst(); return first.isPresent() ? first.get().getITRFType() : ITRFVersion.ITRF_2014; } catch (TimeStampedCacheException tce) { @@ -664,11 +711,11 @@ public List getEntries() { private double interpolate(final AbsoluteDate date, final Function selector) { try { final HermiteInterpolator interpolator = new HermiteInterpolator(); - getNeighbors(date).forEach(entry -> - interpolator.addSamplePoint(entry.getDate().durationFrom(date), - new double[] { - selector.apply(entry) - })); + getNeighbors(date, interpolationDegree + 1). + forEach(entry -> interpolator.addSamplePoint(entry.getDate().durationFrom(date), + new double[] { + selector.apply(entry) + })); return interpolator.value(0)[0]; } catch (TimeStampedCacheException tce) { // this should not happen because of date check performed by caller @@ -687,8 +734,8 @@ private double interpolate(final AbsoluteDate date, final Function> T interpolate(final FieldAbsoluteDate date, - final AbsoluteDate aDate, - final Function selector) { + final AbsoluteDate aDate, + final Function selector) { try { final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); final T[] y = MathArrays.buildArray(date.getField(), 1); @@ -696,10 +743,11 @@ private > T interpolate(final FieldAbsoluteDat final FieldAbsoluteDate central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date, // for example removing derivatives // if T was DerivativeStructure - getNeighbors(aDate).forEach(entry -> { - y[0] = zero.add(selector.apply(entry)); - interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); - }); + getNeighbors(aDate, interpolationDegree + 1). + forEach(entry -> { + y[0] = zero.add(selector.apply(entry)); + interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); + }); return interpolator.value(date.durationFrom(central))[0]; // here, we introduce derivatives again (in DerivativeStructure case) } catch (TimeStampedCacheException tce) { // this should not happen because of date check performed by caller @@ -721,12 +769,48 @@ private double[] interpolate(final AbsoluteDate date, final Function selector2) { try { final HermiteInterpolator interpolator = new HermiteInterpolator(); - getNeighbors(date).forEach(entry -> - interpolator.addSamplePoint(entry.getDate().durationFrom(date), - new double[] { - selector1.apply(entry), - selector2.apply(entry) - })); + getNeighbors(date, interpolationDegree + 1). + forEach(entry -> interpolator.addSamplePoint(entry.getDate().durationFrom(date), + new double[] { + selector1.apply(entry), + selector2.apply(entry) + })); + return interpolator.value(0); + } catch (TimeStampedCacheException tce) { + // this should not happen because of date check performed by caller + throw new OrekitInternalError(tce); + } + } + + /** Interpolate two EOP components. + *

        + * This method should be called only when {@link #hasDataFor(AbsoluteDate)} returns true. + *

        + * @param date interpolation date + * @param selector1 selector for first EOP entry component + * @param selector2 selector for second EOP entry component + * @param selector1Rate selector for first EOP entry component rate + * @param selector2Rate selector for second EOP entry component rate + * @return interpolated value + * @since 12.0 + */ + private double[] interpolate(final AbsoluteDate date, + final Function selector1, + final Function selector2, + final Function selector1Rate, + final Function selector2Rate) { + try { + final HermiteInterpolator interpolator = new HermiteInterpolator(); + getNeighbors(date, (interpolationDegree + 1) / 2). + forEach(entry -> interpolator.addSamplePoint(entry.getDate().durationFrom(date), + new double[] { + selector1.apply(entry), + selector2.apply(entry) + }, + new double[] { + selector1Rate.apply(entry), + selector2Rate.apply(entry) + })); return interpolator.value(0); } catch (TimeStampedCacheException tce) { // this should not happen because of date check performed by caller @@ -746,9 +830,9 @@ private double[] interpolate(final AbsoluteDate date, * @return interpolated value */ private > T[] interpolate(final FieldAbsoluteDate date, - final AbsoluteDate aDate, - final Function selector1, - final Function selector2) { + final AbsoluteDate aDate, + final Function selector1, + final Function selector2) { try { final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); final T[] y = MathArrays.buildArray(date.getField(), 2); @@ -756,11 +840,12 @@ private > T[] interpolate(final FieldAbsoluteD final FieldAbsoluteDate central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date, // for example removing derivatives // if T was DerivativeStructure - getNeighbors(aDate).forEach(entry -> { - y[0] = zero.add(selector1.apply(entry)); - y[1] = zero.add(selector2.apply(entry)); - interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); - }); + getNeighbors(aDate, interpolationDegree + 1). + forEach(entry -> { + y[0] = zero.add(selector1.apply(entry)); + y[1] = zero.add(selector2.apply(entry)); + interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); + }); return interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case) } catch (TimeStampedCacheException tce) { // this should not happen because of date check performed by caller @@ -768,15 +853,68 @@ private > T[] interpolate(final FieldAbsoluteD } } + /** Check if some derivatives are missing. + * @param raw raw EOP history + * @return true if some derivatives are missing + * @since 12.0 + */ + private boolean missSomeDerivatives(final Collection raw) { + for (final EOPEntry entry : raw) { + if (Double.isNaN(entry.getLOD() + entry.getXRate() + entry.getYRate())) { + return true; + } + } + return false; + } + + /** Fix missing derivatives. + * @param entry EOP entry to fix + * @param rawCache raw EOP history cache + * @return fixed entry + * @since 12.0 + */ + private EOPEntry fixDerivatives(final EOPEntry entry, final ImmutableTimeStampedCache rawCache) { + + // helper function to differentiate some EOP parameters + final BiFunction, Double> differentiator = + (e, selector) -> { + final HermiteInterpolator interpolator = new HermiteInterpolator(); + rawCache.getNeighbors(e.getDate()). + forEach(f -> interpolator.addSamplePoint(f.getDate().durationFrom(e.getDate()), + new double[] { + selector.apply(f) + })); + return interpolator.derivatives(0.0, 1)[1][0]; + }; + + if (Double.isNaN(entry.getLOD() + entry.getXRate() + entry.getYRate())) { + final double lod = Double.isNaN(entry.getLOD()) ? + -differentiator.apply(entry, e -> e.getUT1MinusUTC()) : + entry.getLOD(); + final double xRate = Double.isNaN(entry.getXRate()) ? + differentiator.apply(entry, e -> e.getX()) : + entry.getXRate(); + final double yRate = Double.isNaN(entry.getYRate()) ? + differentiator.apply(entry, e -> e.getY()) : + entry.getYRate(); + return new EOPEntry(entry.getMjd(), + entry.getUT1MinusUTC(), lod, + entry.getX(), entry.getY(), xRate, yRate, + entry.getDdPsi(), entry.getDdEps(), + entry.getDx(), entry.getDy(), + entry.getITRFType(), entry.getDate()); + } else { + // the entry already has all derivatives + return entry; + } + } + /** Replace the instance with a data transfer object for serialization. - *

        - * This intermediate class serializes only the frame key. - *

        * @return data transfer object that will be serialized */ @DefaultDataContext private Object writeReplace() { - return new DataTransferObject(conventions, getEntries(), tidalCorrection == null); + return new DataTransferObject(conventions, interpolationDegree, getEntries(), tidalCorrection == null); } /** Internal class used only for serialization. */ @@ -784,11 +922,16 @@ private Object writeReplace() { private static class DataTransferObject implements Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 20131010L; + private static final long serialVersionUID = 20231122L; /** IERS conventions. */ private final IERSConventions conventions; + /** Interpolation degree. + * @since 12.0 + */ + private final int interpolationDegree; + /** EOP entries. */ private final List entries; @@ -797,15 +940,19 @@ private static class DataTransferObject implements Serializable { /** Simple constructor. * @param conventions IERS conventions to which EOP refers + * @param interpolationDegree interpolation degree (must be of the form 4k-1) * @param entries the EOP data to use * @param simpleEOP if true, tidal effects are ignored when interpolating EOP + * @since 12.0 */ DataTransferObject(final IERSConventions conventions, - final List entries, - final boolean simpleEOP) { - this.conventions = conventions; - this.entries = entries; - this.simpleEOP = simpleEOP; + final int interpolationDegree, + final List entries, + final boolean simpleEOP) { + this.conventions = conventions; + this.interpolationDegree = interpolationDegree; + this.entries = entries; + this.simpleEOP = simpleEOP; } /** Replace the deserialized data transfer object with a {@link EOPHistory}. @@ -813,8 +960,7 @@ private static class DataTransferObject implements Serializable { */ private Object readResolve() { try { - // retrieve a managed frame - return new EOPHistory(conventions, entries, simpleEOP); + return new EOPHistory(conventions, interpolationDegree, entries, simpleEOP); } catch (OrekitException oe) { throw new OrekitInternalError(oe); } @@ -929,7 +1075,7 @@ public List generate(final AbsoluteDate existingDate, fina if (existingDate == null) { // no prior existing entries, just generate a first set - for (int i = -cache.getNeighborsSize() / 2; generated.size() < cache.getNeighborsSize(); ++i) { + for (int i = -cache.getMaxNeighborsSize() / 2; generated.size() < cache.getMaxNeighborsSize(); ++i) { final AbsoluteDate t = date.shiftedBy(i * step); generated.add(new TidalCorrectionEntry(t, tidalCorrection.value(t))); } diff --git a/src/main/java/org/orekit/frames/EopC04FilesLoader.java b/src/main/java/org/orekit/frames/EopC04FilesLoader.java new file mode 100644 index 0000000000..fade522ee8 --- /dev/null +++ b/src/main/java/org/orekit/frames/EopC04FilesLoader.java @@ -0,0 +1,552 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.SortedSet; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.orekit.data.DataProvidersManager; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateComponents; +import org.orekit.time.TimeComponents; +import org.orekit.time.TimeScale; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.IERSConventions.NutationCorrectionConverter; + +/** Loader for EOP C04 files. + *

        EOP C04 files contain {@link EOPEntry + * Earth Orientation Parameters} consistent with ITRF20xx for one year periods, with various + * xx (05, 08, 14, 20) depending on the data source.

        + *

        The EOP C04 files retrieved from the old ftp site + * ftp://ftp.iers.org/products/eop/long-term/ + * were recognized thanks to their base names, which must match one of the patterns + * {@code eopc04_##_IAU2000.##} or {@code eopc04_##.##} (or the same ending with .gz for + * gzip-compressed files) where # stands for a digit character. As of early 2023, this ftp site + * seems not to be accessible anymore.

        + *

        + * The official source for these files is now the web site + * https://hpiers.obspm.fr/eoppc/eop/. These + * files do not follow the old naming convention that was used in the older ftp site. + * They lack the _05, _08 or _14 markers in the file names. The ITRF year appears only in the URL + * (with directories eopc04_05, eop04_c08…). The directory for the current data is named eopc04 + * without any suffix. So before 2023-02-14 the eopc04 directory would contain files compatible with + * ITRF2014 and after 2023-02-14 it would contain files compatible with ITRF2020. In each directory, + * the files don't have any marker, hence users downloading eopc04.99 file from eopc04_05 would get + * a file compatible with ITRF2005 whereas users downloading a file with the exact same name eopc04.99 + * but from eop04_c08 would get a file compatible with ITRF2008. + *

        + *

        + * Starting with Orekit version 12.0, the ITRF year is retrieved by analyzing the file header, it is + * not linked to file name anymore, hence it is compatible with any IERS site layout. + *

        + *

        + * This class is immutable and hence thread-safe + *

        + * @author Luc Maisonobe + */ +class EopC04FilesLoader extends AbstractEopLoader implements EopHistoryLoader { + + /** Build a loader for IERS EOP C04 files. + * @param supportedNames regular expression for supported files names + * @param manager provides access to the EOP C04 files. + * @param utcSupplier UTC time scale. + */ + EopC04FilesLoader(final String supportedNames, + final DataProvidersManager manager, + final Supplier utcSupplier) { + super(supportedNames, manager, utcSupplier); + } + + /** {@inheritDoc} */ + public void fillHistory(final IERSConventions.NutationCorrectionConverter converter, + final SortedSet history) { + final Parser parser = new Parser(converter, getUtc()); + final EopParserLoader loader = new EopParserLoader(parser); + this.feed(loader); + history.addAll(loader.getEop()); + } + + /** Internal class performing the parsing. */ + static class Parser extends AbstractEopParser { + + /** Simple constructor. + * @param converter converter to use + * @param utc time scale for parsing dates. + */ + Parser(final NutationCorrectionConverter converter, + final TimeScale utc) { + super(converter, null, utc); + } + + /** {@inheritDoc} */ + public Collection parse(final InputStream input, final String name) + throws IOException, OrekitException { + + final List history = new ArrayList<>(); + + // set up a reader for line-oriented EOP C04 files + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { + // reset parse info to start new file (do not clear history!) + int lineNumber = 0; + boolean inHeader = true; + final LineParser[] tentativeParsers = new LineParser[] { + new LineWithoutRatesParser(name), + new LineWithRatesParser(name) + }; + LineParser selectedParser = null; + + // read all file + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + ++lineNumber; + boolean parsed = false; + + if (inHeader) { + // maybe it's an header line + for (final LineParser parser : tentativeParsers) { + if (parser.parseHeaderLine(line)) { + // we recognized one EOP C04 format + selectedParser = parser; + break; + } + } + } + + if (selectedParser != null) { + // maybe it's a data line + final EOPEntry entry = selectedParser.parseDataLine(line); + if (entry != null) { + + // this is a data line, build an entry from the extracted fields + history.add(entry); + parsed = true; + + // we know we have already finished header + inHeader = false; + + } + } + + if (!(inHeader || parsed)) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + } + + // check if we have read something + if (inHeader) { + throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, name); + } + } + + return history; + } + + /** Base parser for EOP C04 lines. + * @since 12.0 + */ + private abstract class LineParser { + + /** Pattern for ITRF version. */ + private final Pattern itrfVersionPattern; + + /** Pattern for columns header. */ + private final Pattern columnHeaderPattern; + + /** Pattern for data lines. */ + private final Pattern dataPattern; + + /** Year group. */ + private final int yearGroup; + + /** Month group. */ + private final int monthGroup; + + /** Day group. */ + private final int dayGroup; + + /** MJD group. */ + private final int mjdGroup; + + /** Name of the stream for error messages. */ + private final String name; + + /** ITRF version. */ + private ITRFVersion itrfVersion; + + /** Simple constructor. + * @param itrfVersionRegexp regular expression for ITRF version + * @param columnsHeaderRegexp regular expression for columns header + * @param dataRegexp regular expression for data lines + * @param yearGroup year group + * @param monthGroup month group + * @param dayGroup day group + * @param mjdGroup MJD group + * @param name of the stream for error messages. + */ + protected LineParser(final String itrfVersionRegexp, final String columnsHeaderRegexp, + final String dataRegexp, + final int yearGroup, final int monthGroup, final int dayGroup, + final int mjdGroup, final String name) { + this.itrfVersionPattern = Pattern.compile(itrfVersionRegexp); + this.columnHeaderPattern = Pattern.compile(columnsHeaderRegexp); + this.dataPattern = Pattern.compile(dataRegexp); + this.yearGroup = yearGroup; + this.monthGroup = monthGroup; + this.dayGroup = dayGroup; + this.mjdGroup = mjdGroup; + this.name = name; + } + + /** Get the ITRF version for this EOP C04 file. + * @return ITRF version + */ + protected ITRFVersion getItrfVersion() { + return itrfVersion; + } + + /** Parse a header line. + * @param line line to parse + * @return true if line was recognized (either ITRF version or columns header) + */ + public boolean parseHeaderLine(final String line) { + final Matcher itrfVersionMatcher = itrfVersionPattern.matcher(line); + if (itrfVersionMatcher.matches()) { + switch (Integer.parseInt(itrfVersionMatcher.group(1))) { + case 5 : + itrfVersion = ITRFVersion.ITRF_2005; + break; + case 8 : + itrfVersion = ITRFVersion.ITRF_2008; + break; + case 14 : + itrfVersion = ITRFVersion.ITRF_2014; + break; + case 20 : + itrfVersion = ITRFVersion.ITRF_2020; + break; + default : + throw new OrekitException(OrekitMessages.NO_SUCH_ITRF_FRAME, itrfVersionMatcher.group(1)); + } + return true; + } else { + final Matcher columnHeaderMatcher = columnHeaderPattern.matcher(line); + if (columnHeaderMatcher.matches()) { + parseColumnsHeaderLine(columnHeaderMatcher); + return true; + } + return false; + } + } + + /** Parse a data line. + * @param line line to parse + * @return EOP entry for the line, or null if line does not match expected regular expression + */ + public EOPEntry parseDataLine(final String line) { + + final Matcher matcher = dataPattern.matcher(line); + if (!matcher.matches()) { + // this is not a data line + return null; + } + + // check date + final DateComponents dc = new DateComponents(Integer.parseInt(matcher.group(yearGroup)), + Integer.parseInt(matcher.group(monthGroup)), + Integer.parseInt(matcher.group(dayGroup))); + final int mjd = Integer.parseInt(matcher.group(mjdGroup)); + if (dc.getMJD() != mjd) { + throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE, + name, dc.getYear(), dc.getMonth(), dc.getDay(), mjd); + } + + return parseDataLine(matcher, dc); + + } + + /** Parse a columns header line. + * @param matcher matcher for line + */ + protected abstract void parseColumnsHeaderLine(Matcher matcher); + + /** Parse a data line. + * @param matcher matcher for line + * @param dc date components already extracted from the line + * @return EOP entry for the line + */ + protected abstract EOPEntry parseDataLine(Matcher matcher, DateComponents dc); + + } + + /** Parser for data lines without pole rates. + *

        + * ITRF markers have either the following form: + *

        + *
        +         *                           EOP (IERS) 05 C04
        +         * 
        + *

        + * or the following form: + *

        + *
        +         *                           EOP (IERS) 14 C04 TIME SERIES
        +         * 
        + *

        + * Header have either the following form: + *

        + *
        +         *       Date      MJD      x          y        UT1-UTC       LOD         dPsi      dEps       x Err     y Err   UT1-UTC Err  LOD Err    dPsi Err   dEpsilon Err
        +         *                          "          "           s           s            "         "        "          "          s           s            "         "
        +         *      (0h UTC)
        +         * 
        + *

        + * or the following form: + *

        + *
        +         *       Date      MJD      x          y        UT1-UTC       LOD         dX        dY        x Err     y Err   UT1-UTC Err  LOD Err     dX Err       dY Err
        +         *                          "          "           s           s          "         "           "          "          s         s            "           "
        +         *      (0h UTC)
        +         * 
        + *

        + * The data lines in the EOP C04 yearly data files have either the following fixed form: + *

        + *
        +         * year month day MJD …12 floating values fields in decimal format...
        +         * 2000   1   1  51544   0.043242   0.377915   0.3554777   …
        +         * 2000   1   2  51545   0.043515   0.377753   0.3546065   …
        +         * 2000   1   3  51546   0.043623   0.377452   0.3538444   …
        +         * 
        + * @since 12.0 + */ + private class LineWithoutRatesParser extends LineParser { + + /** Nutation header group. */ + private static final int NUTATION_HEADER_GROUP = 1; + + /** Year group. */ + private static final int YEAR_GROUP = 1; + + /** Month group. */ + private static final int MONTH_GROUP = 2; + + /** Day group. */ + private static final int DAY_GROUP = 3; + + /** MJD group. */ + private static final int MJD_GROUP = 4; + + /** X component of pole motion group. */ + private static final int POLE_X_GROUP = 5; + + /** Y component of pole motion group. */ + private static final int POLE_Y_GROUP = 6; + + /** UT1-UTC group. */ + private static final int UT1_UTC_GROUP = 7; + + /** LoD group. */ + private static final int LOD_GROUP = 8; + + /** Correction for nutation first field (either dX or dPsi). */ + private static final int NUT_0_GROUP = 9; + + /** Correction for nutation second field (either dY or dEps). */ + private static final int NUT_1_GROUP = 10; + + /** Indicator for non-rotating origin. */ + private boolean isNonRotatingOrigin; + + /** Simple constructor. + * @param name of the stream for error messages. + */ + LineWithoutRatesParser(final String name) { + super("^ +EOP +\\(IERS\\) +([0-9][0-9]) +C04.*", + "^ *Date +MJD +x +y +UT1-UTC +LOD +((?:dPsi +dEps)|(?:dX +dY)) .*", + "^(\\d+) +(\\d+) +(\\d+) +(\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+)(?: +(-?\\d+\\.\\d+)){6}$", + YEAR_GROUP, MONTH_GROUP, DAY_GROUP, MJD_GROUP, + name); + } + + /** {@inheritDoc} */ + @Override + protected void parseColumnsHeaderLine(final Matcher matcher) { + isNonRotatingOrigin = matcher.group(NUTATION_HEADER_GROUP).startsWith("dX"); + } + + /** {@inheritDoc} */ + @Override + protected EOPEntry parseDataLine(final Matcher matcher, final DateComponents dc) { + + final AbsoluteDate date = new AbsoluteDate(dc, getUtc()); + + final double x = Double.parseDouble(matcher.group(POLE_X_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS; + final double y = Double.parseDouble(matcher.group(POLE_Y_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS; + final double dtu1 = Double.parseDouble(matcher.group(UT1_UTC_GROUP)); + final double lod = Double.parseDouble(matcher.group(LOD_GROUP)); + final double[] equinox; + final double[] nro; + if (isNonRotatingOrigin) { + nro = new double[] { + Double.parseDouble(matcher.group(NUT_0_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS, + Double.parseDouble(matcher.group(NUT_1_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS + }; + equinox = getConverter().toEquinox(date, nro[0], nro[1]); + } else { + equinox = new double[] { + Double.parseDouble(matcher.group(NUT_0_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS, + Double.parseDouble(matcher.group(NUT_1_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS + }; + nro = getConverter().toNonRotating(date, equinox[0], equinox[1]); + } + + return new EOPEntry(dc.getMJD(), dtu1, lod, x, y, Double.NaN, Double.NaN, + equinox[0], equinox[1], nro[0], nro[1], + getItrfVersion(), date); + + } + } + + /** Parser for data lines with pole rates. + *

        + * ITRF markers have either the following form: + *

        + *
        +         * # EOP (IERS) 20 C04 TIME SERIES  consistent with ITRF 2020 - sampled at 0h UTC
        +         * 
        + *

        + * Header have either the following form: + *

        + *
        +         * # YR  MM  DD  HH       MJD        x(")        y(")  UT1-UTC(s)       dX(")      dY(")       xrt(")      yrt(")      LOD(s)        x Er        y Er  UT1-UTC Er      dX Er       dY Er       xrt Er      yrt Er      LOD Er
        +         * 
        + *

        + * The data lines in the EOP C04 yearly data files have either the following fixed form: + *

        + *
        +         * year month day hour MJD (in floating format) …16 floating values fields in decimal format...
        +         * 2015   1   1  12  57023.50    0.030148    0.281014   …
        +         * 2015   1   2  12  57024.50    0.029219    0.281441   …
        +         * 2015   1   3  12  57025.50    0.028777    0.281824   …
        +         * 
        + * @since 12.0 + */ + private class LineWithRatesParser extends LineParser { + + /** Year group. */ + private static final int YEAR_GROUP = 1; + + /** Month group. */ + private static final int MONTH_GROUP = 2; + + /** Day group. */ + private static final int DAY_GROUP = 3; + + /** Hour group. */ + private static final int HOUR_GROUP = 4; + + /** MJD group. */ + private static final int MJD_GROUP = 5; + + /** X component of pole motion group. */ + private static final int POLE_X_GROUP = 6; + + /** Y component of pole motion group. */ + private static final int POLE_Y_GROUP = 7; + + /** UT1-UTC group. */ + private static final int UT1_UTC_GROUP = 8; + + /** Correction for nutation first field. */ + private static final int NUT_DX_GROUP = 9; + + /** Correction for nutation second field. */ + private static final int NUT_DY_GROUP = 10; + + /** X rate component of pole motion group. + * @since 12.0 + */ + private static final int POLE_X_RATE_GROUP = 11; + + /** Y rate component of pole motion group. + * @since 12.0 + */ + private static final int POLE_Y_RATE_GROUP = 12; + + /** LoD group. */ + private static final int LOD_GROUP = 13; + + /** Simple constructor. + * @param name of the stream for error messages. + */ + LineWithRatesParser(final String name) { + super("^# +EOP +\\(IERS\\) +([0-9][0-9]) +C04.*", + "^# +YR +MM +DD +H +MJD +x\\(\"\\) +y\\(\"\\) +UT1-UTC\\(s\\) +dX\\(\"\\) +dY\\(\"\\) +xrt\\(\"\\) +yrt\\'\"\\) +.*", + "^(\\d+) +(\\d+) +(\\d+) +(\\d+) +(\\d+)\\.\\d+ +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+) +(-?\\d+\\.\\d+)(?: +(-?\\d+\\.\\d+)){8}$", // we intentionally ignore MJD fractional part + YEAR_GROUP, MONTH_GROUP, DAY_GROUP, MJD_GROUP, + name); + } + + /** {@inheritDoc} */ + @Override + protected void parseColumnsHeaderLine(final Matcher matcher) { + // nothing to do here + } + + /** {@inheritDoc} */ + @Override + protected EOPEntry parseDataLine(final Matcher matcher, final DateComponents dc) { + + final TimeComponents tc = new TimeComponents(Integer.parseInt(matcher.group(HOUR_GROUP)), 0, 0.0); + final AbsoluteDate date = new AbsoluteDate(dc, tc, getUtc()); + + final double x = Double.parseDouble(matcher.group(POLE_X_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS; + final double y = Double.parseDouble(matcher.group(POLE_Y_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS; + final double xRate = Double.parseDouble(matcher.group(POLE_X_RATE_GROUP)) * + Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_DAY; + final double yRate = Double.parseDouble(matcher.group(POLE_Y_RATE_GROUP)) * + Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_DAY; + final double dtu1 = Double.parseDouble(matcher.group(UT1_UTC_GROUP)); + final double lod = Double.parseDouble(matcher.group(LOD_GROUP)); + final double[] nro = new double[] { + Double.parseDouble(matcher.group(NUT_DX_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS, + Double.parseDouble(matcher.group(NUT_DY_GROUP)) * Constants.ARC_SECONDS_TO_RADIANS + }; + final double[] equinox = getConverter().toEquinox(date, nro[0], nro[1]); + + return new EOPEntry(dc.getMJD(), dtu1, lod, x, y, xRate, yRate, + equinox[0], equinox[1], nro[0], nro[1], + getItrfVersion(), date); + + } + } + + } + +} diff --git a/src/main/java/org/orekit/frames/EopCsvFilesLoader.java b/src/main/java/org/orekit/frames/EopCsvFilesLoader.java new file mode 100644 index 0000000000..d573a92045 --- /dev/null +++ b/src/main/java/org/orekit/frames/EopCsvFilesLoader.java @@ -0,0 +1,373 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.SortedSet; +import java.util.function.Supplier; + +import org.orekit.data.DataProvidersManager; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateComponents; +import org.orekit.time.TimeScale; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.IERSConventions.NutationCorrectionConverter; +import org.orekit.utils.units.Unit; + +/** Loader for EOP csv files (can be bulletin A, bulletin B, EOP C04…). + *

        + * This class is immutable and hence thread-safe + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +class EopCsvFilesLoader extends AbstractEopLoader implements EopHistoryLoader { + + /** Separator. */ + private static final String SEPARATOR = ";"; + + /** Header for MJD. */ + private static final String MJD = "MJD"; + + /** Header for Year. */ + private static final String YEAR = "Year"; + + /** Header for Month. */ + private static final String MONTH = "Month"; + + /** Header for Day. */ + private static final String DAY = "Day"; + + /** Header for x_pole. */ + private static final String X_POLE = "x_pole"; + + /** Header for y_pole. */ + private static final String Y_POLE = "y_pole"; + + /** Header for x_rate. */ + private static final String X_RATE = "x_rate"; + + /** Header for y_rate. */ + private static final String Y_RATE = "y_rate"; + + /** Header for UT1-UTC. */ + private static final String UT1_UTC = "UT1-UTC"; + + /** Header for LOD. */ + private static final String LOD = "LOD"; + + /** Header for dPsi. */ + private static final String DPSI = "dPsi"; + + /** Header for dEpsilon. */ + private static final String DEPSILON = "dEpsilon"; + + /** Header for dX. */ + private static final String DX = "dX"; + + /** Header for dY. */ + private static final String DY = "dY"; + + /** Converter for milliarcseconds. */ + private static final Unit MAS = Unit.parse("mas"); + + /** Converter for milliarcseconds per day. */ + private static final Unit MAS_D = Unit.parse("mas/day"); + + /** Converter for milliseconds. */ + private static final Unit MS = Unit.parse("ms"); + + /** Build a loader for IERS EOP csv files. + * @param supportedNames regular expression for supported files names + * @param manager provides access to the EOP C04 files. + * @param utcSupplier UTC time scale. + */ + EopCsvFilesLoader(final String supportedNames, + final DataProvidersManager manager, + final Supplier utcSupplier) { + super(supportedNames, manager, utcSupplier); + } + + /** {@inheritDoc} */ + public void fillHistory(final IERSConventions.NutationCorrectionConverter converter, + final SortedSet history) { + final Parser parser = new Parser(converter, getUtc()); + final EopParserLoader loader = new EopParserLoader(parser); + this.feed(loader); + history.addAll(loader.getEop()); + } + + /** Internal class performing the parsing. */ + class Parser extends AbstractEopParser { + + /** Configuration for ITRF versions. */ + private final ItrfVersionProvider itrfVersionProvider; + + /** Column number for MJD field. */ + private int mjdColumn; + + /** Column number for year field. */ + private int yearColumn; + + /** Column number for month field. */ + private int monthColumn; + + /** Column number for day field. */ + private int dayColumn; + + /** Column number for X pole field. */ + private int xPoleColumn; + + /** Column number for Y pole field. */ + private int yPoleColumn; + + /** Column number for X rate pole field. */ + private int xRatePoleColumn; + + /** Column number for Y rate pole field. */ + private int yRatePoleColumn; + + /** Column number for UT1-UTC field. */ + private int ut1Column; + + /** Column number for LOD field. */ + private int lodColumn; + + /** Column number for dX field. */ + private int dxColumn; + + /** Column number for dY field. */ + private int dyColumn; + + /** Column number for dPsi field. */ + private int dPsiColumn; + + /** Column number for dEpsilon field. */ + private int dEpsilonColumn; + + /** ITRF version configuration. */ + private ITRFVersionLoader.ITRFVersionConfiguration configuration; + + /** Simple constructor. + * @param converter converter to use + * @param utc time scale for parsing dates. + */ + Parser(final NutationCorrectionConverter converter, + final TimeScale utc) { + super(converter, null, utc); + this.itrfVersionProvider = new ITRFVersionLoader(ITRFVersionLoader.SUPPORTED_NAMES, + getDataProvidersManager()); + } + + /** {@inheritDoc} */ + public Collection parse(final InputStream input, final String name) + throws IOException, OrekitException { + + final List history = new ArrayList<>(); + + // set up a reader for line-oriented csv files + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { + // reset parse info to start new file (do not clear history!) + int lineNumber = 0; + configuration = null; + + // read all file + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + ++lineNumber; + + final boolean parsed; + if (lineNumber == 1) { + parsed = parseHeaderLine(line); + } else { + history.add(parseDataLine(line, name)); + parsed = true; + } + + if (!parsed) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + } + + // check if we have read something + if (lineNumber < 2) { + throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, name); + } + } + + return history; + } + + /** Parse the header line. + * @param headerLine header line + * @return true if line was parsed correctly + */ + private boolean parseHeaderLine(final String headerLine) { + + // reset columns numbers + mjdColumn = -1; + yearColumn = -1; + monthColumn = -1; + dayColumn = -1; + xPoleColumn = -1; + yPoleColumn = -1; + xRatePoleColumn = -1; + yRatePoleColumn = -1; + ut1Column = -1; + lodColumn = -1; + dxColumn = -1; + dyColumn = -1; + dPsiColumn = -1; + dEpsilonColumn = -1; + + // split header fields + final String[] fields = headerLine.split(SEPARATOR); + + // affect column numbers according to header fields + for (int column = 0; column < fields.length; ++column) { + switch (fields[column]) { + case MJD : + mjdColumn = column; + break; + case YEAR : + yearColumn = column; + break; + case MONTH : + monthColumn = column; + break; + case DAY : + dayColumn = column; + break; + case X_POLE : + xPoleColumn = column; + break; + case Y_POLE : + yPoleColumn = column; + break; + case X_RATE : + xRatePoleColumn = column; + break; + case Y_RATE : + yRatePoleColumn = column; + break; + case UT1_UTC : + ut1Column = column; + break; + case LOD : + lodColumn = column; + break; + case DX : + dxColumn = column; + break; + case DY : + dyColumn = column; + break; + case DPSI : + dPsiColumn = column; + break; + case DEPSILON : + dEpsilonColumn = column; + break; + default : + // ignored column + } + } + + // check all required files are present (we just allow pole rates to be missing) + return mjdColumn >= 0 && yearColumn >= 0 && monthColumn >= 0 && dayColumn >= 0 && + xPoleColumn >= 0 && yPoleColumn >= 0 && ut1Column >= 0 && lodColumn >= 0 && + (dxColumn >= 0 && dyColumn >= 0 || dPsiColumn >= 0 && dEpsilonColumn >= 0); + + } + + /** Parse a data line. + * @param line line to parse + * @param name file name (for error messages) + * @return parsed entry + */ + private EOPEntry parseDataLine(final String line, final String name) { + + final String[] fields = line.split(SEPARATOR); + + // check date + final DateComponents dc = new DateComponents(Integer.parseInt(fields[yearColumn]), + Integer.parseInt(fields[monthColumn]), + Integer.parseInt(fields[dayColumn])); + final int mjd = Integer.parseInt(fields[mjdColumn]); + if (dc.getMJD() != mjd) { + throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE, + name, dc.getYear(), dc.getMonth(), dc.getDay(), mjd); + } + final AbsoluteDate date = new AbsoluteDate(dc, getUtc()); + + if (configuration == null || !configuration.isValid(mjd)) { + // get a configuration for current name and date range + configuration = itrfVersionProvider.getConfiguration(name, mjd); + } + + final double x = parseField(fields, xPoleColumn, MAS); + final double y = parseField(fields, yPoleColumn, MAS); + final double xRate = parseField(fields, xRatePoleColumn, MAS_D); + final double yRate = parseField(fields, yRatePoleColumn, MAS_D); + final double dtu1 = parseField(fields, ut1Column, MS); + final double lod = parseField(fields, lodColumn, MS); + + if (dxColumn >= 0) { + // non-rotatin origin paradigm + final double dx = parseField(fields, dxColumn, MAS); + final double dy = parseField(fields, dyColumn, MAS); + final double[] equinox = getConverter().toEquinox(date, dx, dy); + return new EOPEntry(dc.getMJD(), dtu1, lod, x, y, xRate, yRate, + equinox[0], equinox[1], dx, dy, + configuration.getVersion(), date); + } else { + // equinox paradigm + final double ddPsi = parseField(fields, dPsiColumn, MAS); + final double dddEpsilon = parseField(fields, dEpsilonColumn, MAS); + final double[] nro = getConverter().toNonRotating(date, ddPsi, dddEpsilon); + return new EOPEntry(dc.getMJD(), dtu1, lod, x, y, xRate, yRate, + ddPsi, dddEpsilon, nro[0], nro[1], + configuration.getVersion(), date); + } + + + } + + /** Parse one field. + * @param fields fields array to parse + * @param index index in the field array (negative for ignored fields) + * @param unit field unit + * @return parsed and converted field + */ + private double parseField(final String[] fields, final int index, final Unit unit) { + return (index < 0 || index >= fields.length || fields[index].isEmpty()) ? + Double.NaN : + unit.toSI(Double.parseDouble(fields[index])); + } + + } + +} diff --git a/src/main/java/org/orekit/frames/EOPHistoryLoader.java b/src/main/java/org/orekit/frames/EopHistoryLoader.java similarity index 95% rename from src/main/java/org/orekit/frames/EOPHistoryLoader.java rename to src/main/java/org/orekit/frames/EopHistoryLoader.java index eabdb71a3c..38a8d26b7f 100644 --- a/src/main/java/org/orekit/frames/EOPHistoryLoader.java +++ b/src/main/java/org/orekit/frames/EopHistoryLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,7 @@ * @author Luc Maisonobe * @since 6.1 */ -public interface EOPHistoryLoader { +public interface EopHistoryLoader { /** Load celestial body. * @param converter converter to use for nutation corrections @@ -73,7 +73,7 @@ static Parser newFinalsXmlParser( final IERSConventions conventions, final ItrfVersionProvider itrfVersionProvider, final TimeScales timeScales) { - return new RapidDataAndPredictionXMLLoader.Parser( + return new EopXmlLoader.Parser( conventions.getNutationCorrectionConverter(timeScales), itrfVersionProvider, timeScales.getUTC()); @@ -129,10 +129,8 @@ static Parser newEopC04Parser( final IERSConventions conventions, final ItrfVersionProvider itrfVersionProvider, final TimeScales timeScales) { - return new EOPC04FilesLoader.Parser( - conventions.getNutationCorrectionConverter(timeScales), - itrfVersionProvider, - timeScales.getUTC()); + return new EopC04FilesLoader.Parser(conventions.getNutationCorrectionConverter(timeScales), + timeScales.getUTC()); } /** diff --git a/src/main/java/org/orekit/frames/EopParserLoader.java b/src/main/java/org/orekit/frames/EopParserLoader.java index afcefdadda..24c959d3db 100644 --- a/src/main/java/org/orekit/frames/EopParserLoader.java +++ b/src/main/java/org/orekit/frames/EopParserLoader.java @@ -26,7 +26,7 @@ import org.orekit.data.DataLoader; /** - * Implementation of {@link DataLoader} based on {@link EOPHistoryLoader.Parser} that + * Implementation of {@link DataLoader} based on {@link EopHistoryLoader.Parser} that * loads all files and compiles the results into one data structure. * * @author Evan Ward @@ -35,18 +35,18 @@ class EopParserLoader implements DataLoader { /** Parser for EOP data files. */ - private final EOPHistoryLoader.Parser parser; + private final EopHistoryLoader.Parser parser; /** History entries. */ private final List history; /** - * Create a {@link DataLoader} based on a {@link EOPHistoryLoader.Parser}. Loads + * Create a {@link DataLoader} based on a {@link EopHistoryLoader.Parser}. Loads * all EOP data into a single collection. * * @param parser for the EOP data files. */ - EopParserLoader(final EOPHistoryLoader.Parser parser) { + EopParserLoader(final EopHistoryLoader.Parser parser) { this.parser = parser; this.history = new ArrayList<>(); } diff --git a/src/main/java/org/orekit/frames/RapidDataAndPredictionXMLLoader.java b/src/main/java/org/orekit/frames/EopXmlLoader.java similarity index 82% rename from src/main/java/org/orekit/frames/RapidDataAndPredictionXMLLoader.java rename to src/main/java/org/orekit/frames/EopXmlLoader.java index fe2a090191..b0f87432ac 100644 --- a/src/main/java/org/orekit/frames/RapidDataAndPredictionXMLLoader.java +++ b/src/main/java/org/orekit/frames/EopXmlLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,24 +44,19 @@ import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -/** Loader for IERS rapid data and prediction file in XML format (finals file). - *

        Rapid data and prediction file contain {@link EOPEntry - * Earth Orientation Parameters} for several years periods, in one file - * only that is updated regularly.

        +/** Loader for IERS EOP data in XML format (finals and EOPC04 files). *

        The XML EOP files are recognized thanks to their base names, which - * must match one of the the patterns finals.2000A.*.xml or - * finals.*.xml (or the same ending with .gz for - * gzip-compressed files) where * stands for a word like "all", "daily", - * or "data".

        - *

        Files containing data (back to 1973) are available at IERS web site: Earth orientation data.

        + * must match one of the the patterns {@code finals.2000A.*.xml} or + * {@code finals.*.xml} or {@code eopc04_*.xml} (or the same ending with + * {@.gz} for gzip-compressed files) where * stands for any string of characters.

        + *

        Files containing data (back to 1962) are available at IERS web site: IERS https data download.

        *

        * This class is immutable and hence thread-safe *

        * @author Luc Maisonobe */ -class RapidDataAndPredictionXMLLoader extends AbstractEopLoader - implements EOPHistoryLoader { +class EopXmlLoader extends AbstractEopLoader implements EopHistoryLoader { /** Millisecond unit. */ private static final Unit MILLI_SECOND = Unit.parse("ms"); @@ -69,6 +64,11 @@ class RapidDataAndPredictionXMLLoader extends AbstractEopLoader /** Milli arcsecond unit. */ private static final Unit MILLI_ARC_SECOND = Unit.parse("mas"); + /**Arcsecond per day unit. + * @since 12.0 + */ + private static final Unit ARC_SECOND_PER_DAY = Unit.parse("as/day"); + /** * Build a loader for IERS XML EOP files. * @@ -76,9 +76,9 @@ class RapidDataAndPredictionXMLLoader extends AbstractEopLoader * @param manager provides access to the XML EOP files. * @param utcSupplier UTC time scale. */ - RapidDataAndPredictionXMLLoader(final String supportedNames, - final DataProvidersManager manager, - final Supplier utcSupplier) { + EopXmlLoader(final String supportedNames, + final DataProvidersManager manager, + final Supplier utcSupplier) { super(supportedNames, manager, utcSupplier); } @@ -143,11 +143,21 @@ private class EOPContentHandler extends DefaultHandler { private static final String LOD_ELT = "LOD"; private static final String X_ELT = "X"; private static final String Y_ELT = "Y"; + private static final String X_RATE_ELT = "x_rate"; + private static final String Y_RATE_ELT = "y_rate"; private static final String DPSI_ELT = "dPsi"; private static final String DEPSILON_ELT = "dEpsilon"; private static final String DX_ELT = "dX"; private static final String DY_ELT = "dY"; + // elements and attributes specific to bulletinA, bulletinB and EOP C04 files + private static final String DATA_ELT = "data"; + private static final String PRODUCT_ATTR = "product"; + private static final String BULLETIN_A_PROD = "BulletinA"; + private static final String BULLETIN_B_PROD = "BulletinB"; + private static final String EOP_C04_PROD_PREFIX = "EOP"; + private static final String EOP_C04_PROD_SUFFIX = "C04"; + // elements and attributes specific to daily data files private static final String DATA_EOP_ELT = "dataEOP"; private static final String TIME_SERIES_ELT = "timeSeries"; @@ -159,7 +169,6 @@ private class EOPContentHandler extends DefaultHandler { private static final String UT1_U_UTC_ELT = "UT1_UTC"; private static final String NUTATION_ELT = "nutation"; private static final String SOURCE_ATTR = "source"; - private static final String BULLETIN_A_SOURCE = "BulletinA"; // elements and attributes specific to finals data files private static final String FINALS_ELT = "Finals"; @@ -178,6 +187,8 @@ private class EOPContentHandler extends DefaultHandler { private double lod; private double x; private double y; + private double xRate; + private double yRate; private double dpsi; private double deps; private double dx; @@ -234,10 +245,26 @@ public void startElement(final String uri, final String localName, } else if (qName.equals(FINALS_ELT)) { // the file contains final data content = DataFileContent.FINAL; + } else if (qName.equals(DATA_ELT)) { + final String product = atts.getValue(PRODUCT_ATTR); + if (product != null) { + if (product.startsWith(BULLETIN_A_PROD)) { + // the file contains bulletinA + content = DataFileContent.BULLETIN_A; + inBulletinA = true; + } else if (product.startsWith(BULLETIN_B_PROD)) { + // the file contains bulletinB + content = DataFileContent.BULLETIN_B; + } else if (product.startsWith(EOP_C04_PROD_PREFIX) && product.endsWith(EOP_C04_PROD_SUFFIX)) { + // the file contains EOP C04 + content = DataFileContent.EOP_C04; + } + } } } - if (content == DataFileContent.DAILY) { + if (content == DataFileContent.DAILY || content == DataFileContent.BULLETIN_A || + content == DataFileContent.BULLETIN_B || content == DataFileContent.EOP_C04) { startDailyElement(qName, atts); } else if (content == DataFileContent.FINAL) { startFinalElement(qName); @@ -256,7 +283,7 @@ private void startDailyElement(final String qName, final Attributes atts) { } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) { final String source = atts.getValue(SOURCE_ATTR); if (source != null) { - inBulletinA = source.equals(BULLETIN_A_SOURCE); + inBulletinA = source.equals(BULLETIN_A_PROD); } } } @@ -286,6 +313,8 @@ private void resetEOPData() { lod = Double.NaN; x = Double.NaN; y = Double.NaN; + xRate = Double.NaN; + yRate = Double.NaN; dpsi = Double.NaN; deps = Double.NaN; dx = Double.NaN; @@ -295,7 +324,8 @@ private void resetEOPData() { /** {@inheritDoc} */ @Override public void endElement(final String uri, final String localName, final String qName) { - if (content == DataFileContent.DAILY) { + if (content == DataFileContent.DAILY || content == DataFileContent.BULLETIN_A || + content == DataFileContent.BULLETIN_B || content == DataFileContent.EOP_C04) { endDailyElement(qName); } else if (content == DataFileContent.FINAL) { endFinalElement(qName); @@ -324,6 +354,10 @@ private void endDailyElement(final String qName) { x = overwrite(x, Unit.ARC_SECOND); } else if (qName.equals(Y_ELT)) { y = overwrite(y, Unit.ARC_SECOND); + } else if (qName.equals(X_RATE_ELT)) { + xRate = overwrite(xRate, ARC_SECOND_PER_DAY); + } else if (qName.equals(Y_RATE_ELT)) { + yRate = overwrite(yRate, ARC_SECOND_PER_DAY); } else if (qName.equals(DPSI_ELT)) { dpsi = overwrite(dpsi, MILLI_ARC_SECOND); } else if (qName.equals(DEPSILON_ELT)) { @@ -336,7 +370,7 @@ private void endDailyElement(final String qName) { inBulletinA = false; } else if (qName.equals(DATA_EOP_ELT)) { checkDates(); - if (!Double.isNaN(dtu1) && !Double.isNaN(lod) && !Double.isNaN(x) && !Double.isNaN(y)) { + if (!Double.isNaN(dtu1) && !Double.isNaN(x) && !Double.isNaN(y)) { final double[] equinox; final double[] nro; if (Double.isNaN(dpsi)) { @@ -354,7 +388,8 @@ private void endDailyElement(final String qName) { // get a configuration for current name and date range configuration = getItrfVersionProvider().getConfiguration(name, mjd); } - history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1], + history.add(new EOPEntry(mjd, dtu1, lod, x, y, Double.NaN, Double.NaN, + equinox[0], equinox[1], nro[0], nro[1], configuration.getVersion(), mjdDate)); } } @@ -383,6 +418,10 @@ private void endFinalElement(final String qName) { x = overwrite(x, Unit.ARC_SECOND); } else if (qName.equals(Y_ELT)) { y = overwrite(y, Unit.ARC_SECOND); + } else if (qName.equals(X_RATE_ELT)) { + xRate = overwrite(xRate, ARC_SECOND_PER_DAY); + } else if (qName.equals(Y_RATE_ELT)) { + yRate = overwrite(yRate, ARC_SECOND_PER_DAY); } else if (qName.equals(DPSI_ELT)) { dpsi = overwrite(dpsi, MILLI_ARC_SECOND); } else if (qName.equals(DEPSILON_ELT)) { @@ -395,7 +434,7 @@ private void endFinalElement(final String qName) { inBulletinA = false; } else if (qName.equals(EOP_SET_ELT)) { checkDates(); - if (!Double.isNaN(dtu1) && !Double.isNaN(lod) && !Double.isNaN(x) && !Double.isNaN(y)) { + if (!Double.isNaN(dtu1) && !Double.isNaN(x) && !Double.isNaN(y)) { final double[] equinox; final double[] nro; if (Double.isNaN(dpsi)) { @@ -413,7 +452,8 @@ private void endFinalElement(final String qName) { // get a configuration for current name and date range configuration = getItrfVersionProvider().getConfiguration(name, mjd); } - history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1], + history.add(new EOPEntry(mjd, dtu1, lod, x, y, xRate, yRate, + equinox[0], equinox[1], nro[0], nro[1], configuration.getVersion(), mjdDate)); } } @@ -463,6 +503,21 @@ private enum DataFileContent { /** Unknown content. */ UNKNOWN, + /** Bulletin A data. + * @since 12.0 + */ + BULLETIN_A, + + /** Bulletin B data. + * @since 12.0 + */ + BULLETIN_B, + + /** EOP_C04 data. + * @since 12.0 + */ + EOP_C04, + /** Daily data. */ DAILY, diff --git a/src/main/java/org/orekit/frames/FactoryManagedFrame.java b/src/main/java/org/orekit/frames/FactoryManagedFrame.java index 8f8b530a60..2beab0ce3f 100644 --- a/src/main/java/org/orekit/frames/FactoryManagedFrame.java +++ b/src/main/java/org/orekit/frames/FactoryManagedFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/FieldPoleCorrection.java b/src/main/java/org/orekit/frames/FieldPoleCorrection.java index 9c04ae26a8..64598cb0e5 100644 --- a/src/main/java/org/orekit/frames/FieldPoleCorrection.java +++ b/src/main/java/org/orekit/frames/FieldPoleCorrection.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/FieldStaticTransform.java b/src/main/java/org/orekit/frames/FieldStaticTransform.java new file mode 100644 index 0000000000..c7fb4301ff --- /dev/null +++ b/src/main/java/org/orekit/frames/FieldStaticTransform.java @@ -0,0 +1,298 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldLine; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Line; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeStamped; + +/** + * A transform that only includes translation and rotation. It is static in the + * sense that no rates thereof are included. + * + * @param the type of the field elements + * @author Bryan Cazabonne + * @see FieldTransform + * @since 12.0 + */ +public interface FieldStaticTransform> extends TimeStamped { + + /** + * Get the identity static transform. + * + * @param type of the elements + * @param field field used by default + * @return identity transform. + */ + static > FieldStaticTransform getIdentity(final Field field) { + return FieldTransform.getIdentity(field); + } + + /** + * Transform a position vector (including translation effects). + * + * @param position vector to transform + * @return transformed position + */ + default FieldVector3D transformPosition(final Vector3D position) { + return getRotation().applyTo(getTranslation().add(position)); + } + + /** + * Transform a position vector (including translation effects). + * + * @param position vector to transform + * @return transformed position + */ + default FieldVector3D transformPosition(final FieldVector3D position) { + return getRotation().applyTo(position.add(getTranslation())); + } + + /** + * Transform a vector (ignoring translation effects). + * + * @param vector vector to transform + * @return transformed vector + */ + default FieldVector3D transformVector(final Vector3D vector) { + return getRotation().applyTo(vector); + } + + /** + * Transform a vector (ignoring translation effects). + * + * @param vector vector to transform + * @return transformed vector + */ + default FieldVector3D transformVector(final FieldVector3D vector) { + return getRotation().applyTo(vector); + } + + /** + * Transform a line. + * + * @param line to transform + * @return transformed line + */ + default FieldLine transformLine(final Line line) { + final FieldVector3D transformedP0 = transformPosition(line.getOrigin()); + final FieldVector3D transformedP1 = transformPosition(line.pointAt(1.0e6)); + return new FieldLine<>(transformedP0, transformedP1, line.getTolerance()); + } + + /** + * Transform a line. + * + * @param line to transform + * @return transformed line + */ + default FieldLine transformLine(final FieldLine line) { + final FieldVector3D transformedP0 = transformPosition(line.getOrigin()); + final FieldVector3D transformedP1 = transformPosition(line.pointAt(1.0e6)); + return new FieldLine<>(transformedP0, transformedP1, line.getTolerance()); + } + + /** + * Get the underlying elementary translation. + *

        A transform can be uniquely represented as an elementary + * translation followed by an elementary rotation. This method returns this + * unique elementary translation.

        + * + * @return underlying elementary translation + */ + FieldVector3D getTranslation(); + + /** + * Get the underlying elementary rotation. + *

        A transform can be uniquely represented as an elementary + * translation followed by an elementary rotation. This method returns this + * unique elementary rotation.

        + * + * @return underlying elementary rotation + */ + FieldRotation getRotation(); + + /** + * Get the inverse transform of the instance. + * + * @return inverse transform of the instance + */ + FieldStaticTransform getInverse(); + + /** + * Build a transform by combining two existing ones. + *

        + * Note that the dates of the two existing transformed are ignored, + * and the combined transform date is set to the date supplied in this + * constructor without any attempt to shift the raw transforms. This is a + * design choice allowing user full control of the combination. + *

        + * + * @param type of the elements + * @param date date of the transform + * @param first first transform applied + * @param second second transform applied + * @return the newly created static transform that has the same effect as + * applying {@code first}, then {@code second}. + * @see #of(FieldAbsoluteDate, FieldVector3D, FieldRotation) + */ + static > FieldStaticTransform compose(final FieldAbsoluteDate date, + final FieldStaticTransform first, + final FieldStaticTransform second) { + return of(date, + compositeTranslation(first, second), + compositeRotation(first, second)); + } + + /** + * Compute a composite translation. + * + * @param first first applied transform + * @param second second applied transform + * @param the type of the field elements + * @return translation part of the composite transform + */ + static > FieldVector3D compositeTranslation(final FieldStaticTransform first, + final FieldStaticTransform second) { + + final FieldVector3D p1 = first.getTranslation(); + final FieldRotation r1 = first.getRotation(); + final FieldVector3D p2 = second.getTranslation(); + + return p1.add(r1.applyInverseTo(p2)); + + } + + /** + * Compute a composite rotation. + * + * @param first first applied transform + * @param second second applied transform + * @param the type of the field elements + * @return rotation part of the composite transform + */ + static > FieldRotation compositeRotation(final FieldStaticTransform first, + final FieldStaticTransform second) { + final FieldRotation r1 = first.getRotation(); + final FieldRotation r2 = second.getRotation(); + return r1.compose(r2, RotationConvention.FRAME_TRANSFORM); + } + + /** + * Create a new static transform from a rotation and zero translation. + * + * @param type of the elements + * @param date of translation. + * @param rotation to apply after the translation. That is after translating + * applying this rotation produces positions expressed in + * the new frame. + * @return the newly created static transform. + * @see #of(FieldAbsoluteDate, FieldVector3D, FieldRotation) + */ + static > FieldStaticTransform of(final FieldAbsoluteDate date, + final FieldRotation rotation) { + return of(date, FieldVector3D.getZero(date.getField()), rotation); + } + + /** + * Create a new static transform from a translation and rotation. + * + * @param type of the elements + * @param date of translation. + * @param translation to apply, expressed in the old frame. That is, the + * opposite of the coordinates of the new origin in the + * old frame. + * @return the newly created static transform. + * @see #of(FieldAbsoluteDate, FieldVector3D, FieldRotation) + */ + static > FieldStaticTransform of(final FieldAbsoluteDate date, + final FieldVector3D translation) { + return of(date, translation, FieldRotation.getIdentity(date.getField())); + } + + /** + * Create a new static transform from an {@link FieldAbsoluteDate} and a {@link StaticTransform}. + * + * @param type of the elements + * @param date of translation. + * @param staticTransform to apply + * @return the newly created static transform. + * @see #of(FieldAbsoluteDate, FieldVector3D, FieldRotation) + */ + static > FieldStaticTransform of(final FieldAbsoluteDate date, + final StaticTransform staticTransform) { + return of(date, + new FieldVector3D<>(date.getField(), staticTransform.getTranslation()), + new FieldRotation<>(date.getField(), staticTransform.getRotation())); + } + + /** + * Create a new static transform from a translation and rotation. + * + * @param type of the elements + * @param date of translation. + * @param translation to apply, expressed in the old frame. That is, the + * opposite of the coordinates of the new origin in the + * old frame. + * @param rotation to apply after the translation. That is after + * translating applying this rotation produces positions + * expressed in the new frame. + * @return the newly created static transform. + * @see #compose(FieldAbsoluteDate, FieldStaticTransform, FieldStaticTransform) + * @see #of(FieldAbsoluteDate, FieldRotation) + * @see #of(FieldAbsoluteDate, FieldVector3D) + */ + static > FieldStaticTransform of(final FieldAbsoluteDate date, + final FieldVector3D translation, + final FieldRotation rotation) { + return new FieldStaticTransform() { + + @Override + public FieldStaticTransform getInverse() { + final FieldRotation r = getRotation(); + final FieldVector3D rp = r.applyTo(getTranslation()); + final FieldVector3D pInv = rp.negate(); + return FieldStaticTransform.of(date, pInv, rotation.revert()); + } + + @Override + public AbsoluteDate getDate() { + return date.toAbsoluteDate(); + } + + @Override + public FieldVector3D getTranslation() { + return translation; + } + + @Override + public FieldRotation getRotation() { + return rotation; + } + + }; + } + +} diff --git a/src/main/java/org/orekit/frames/FieldTransform.java b/src/main/java/org/orekit/frames/FieldTransform.java index 0b36483a88..bc27f87353 100644 --- a/src/main/java/org/orekit/frames/FieldTransform.java +++ b/src/main/java/org/orekit/frames/FieldTransform.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,29 +22,28 @@ import java.util.List; import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldLine; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Line; -import org.hipparchus.geometry.euclidean.threed.RotationConvention; -import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.time.TimeShiftable; -import org.orekit.time.TimeStamped; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.FieldTimeShiftable; import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.FieldAngularCoordinates; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedFieldAngularCoordinates; +import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedPVCoordinates; -/** Transformation class in three dimensional space. +/** Transformation class in three-dimensional space. * *

        This class represents the transformation engine between {@link Frame frames}. * It is used both to define the relationship between each frame and its @@ -102,7 +101,7 @@ * @since 9.0 */ public class FieldTransform> - implements TimeStamped, TimeShiftable> { + implements FieldTimeShiftable, T>, FieldStaticTransform { /** Date of the transform. */ private final FieldAbsoluteDate date; @@ -274,10 +273,10 @@ public FieldTransform(final FieldAbsoluteDate date, final FieldTransform first, final FieldTransform second) { this(date, date.toAbsoluteDate(), - new FieldPVCoordinates<>(compositeTranslation(first, second), + new FieldPVCoordinates<>(FieldStaticTransform.compositeTranslation(first, second), compositeVelocity(first, second), compositeAcceleration(first, second)), - new FieldAngularCoordinates<>(compositeRotation(first, second), + new FieldAngularCoordinates<>(FieldStaticTransform.compositeRotation(first, second), compositeRotationRate(first, second), compositeRotationAcceleration(first, second))); } @@ -291,22 +290,6 @@ public static > FieldTransform getIdentity( return new FieldIdentityTransform<>(field); } - /** Compute a composite translation. - * @param first first applied transform - * @param second second applied transform - * @param the type of the field elements - * @return translation part of the composite transform - */ - private static > FieldVector3D compositeTranslation(final FieldTransform first, final FieldTransform second) { - - final FieldVector3D p1 = first.cartesian.getPosition(); - final FieldRotation r1 = first.angular.getRotation(); - final FieldVector3D p2 = second.cartesian.getPosition(); - - return p1.add(r1.applyInverseTo(p2)); - - } - /** Compute a composite velocity. * @param first first applied transform * @param second second applied transform @@ -351,21 +334,6 @@ private static > FieldVector3D compositeAcc } - /** Compute a composite rotation. - * @param first first applied transform - * @param second second applied transform - * @param the type of the field elements - * @return rotation part of the composite transform - */ - private static > FieldRotation compositeRotation(final FieldTransform first, final FieldTransform second) { - - final FieldRotation r1 = first.angular.getRotation(); - final FieldRotation r2 = second.angular.getRotation(); - - return r1.compose(r2, RotationConvention.FRAME_TRANSFORM); - - } - /** Compute a composite rotation rate. * @param first first applied transform * @param second second applied transform @@ -403,6 +371,7 @@ private static > FieldVector3D compositeRot } /** {@inheritDoc} */ + @Override public AbsoluteDate getDate() { return aDate; } @@ -415,6 +384,7 @@ public FieldAbsoluteDate getFieldDate() { } /** {@inheritDoc} */ + @Override public FieldTransform shiftedBy(final double dt) { return new FieldTransform<>(date.shiftedBy(dt), aDate.shiftedBy(dt), cartesian.shiftedBy(dt), angular.shiftedBy(dt)); @@ -429,6 +399,34 @@ public FieldTransform shiftedBy(final T dt) { cartesian.shiftedBy(dt), angular.shiftedBy(dt)); } + /** + * Shift the transform in time considering all rates, then return only the + * translation and rotation portion of the transform. + * + * @param dt time shift in seconds. + * @return shifted transform as a static transform. It is static in the + * sense that it can only be used to transform directions and positions, but + * not velocities or accelerations. + * @see #shiftedBy(double) + */ + public FieldStaticTransform staticShiftedBy(final T dt) { + return FieldStaticTransform.of(date.shiftedBy(dt), + cartesian.positionShiftedBy(dt), + angular.rotationShiftedBy(dt)); + } + + /** + * Create a so-called static transform from the instance. + * + * @return static part of the transform. It is static in the + * sense that it can only be used to transform directions and positions, but + * not velocities or accelerations. + * @see FieldStaticTransform + */ + public FieldStaticTransform toStaticTransform() { + return FieldStaticTransform.of(date, cartesian.getPosition(), angular.getRotation()); + } + /** Interpolate a transform from a sample set of existing transforms. *

        * Calling this method is equivalent to call {@link #interpolate(FieldAbsoluteDate, @@ -508,20 +506,33 @@ public static > FieldTransform interpolate( final CartesianDerivativesFilter cFilter, final AngularDerivativesFilter aFilter, final Stream> sample) { + + // Create samples final List> datedPV = new ArrayList<>(); final List> datedAC = new ArrayList<>(); sample.forEach(t -> { datedPV.add(new TimeStampedFieldPVCoordinates<>(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration())); datedAC.add(new TimeStampedFieldAngularCoordinates<>(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration())); }); - final TimeStampedFieldPVCoordinates interpolatedPV = TimeStampedFieldPVCoordinates.interpolate(date, cFilter, datedPV); - final TimeStampedFieldAngularCoordinates interpolatedAC = TimeStampedFieldAngularCoordinates.interpolate(date, aFilter, datedAC); + + // Create interpolators + final FieldTimeInterpolator, T> pvInterpolator = + new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(datedPV.size(), cFilter); + + final FieldTimeInterpolator, T> angularInterpolator = + new TimeStampedFieldAngularCoordinatesHermiteInterpolator<>(datedPV.size(), aFilter); + + // Interpolate + final TimeStampedFieldPVCoordinates interpolatedPV = pvInterpolator.interpolate(date, datedPV); + final TimeStampedFieldAngularCoordinates interpolatedAC = angularInterpolator.interpolate(date, datedAC); + return new FieldTransform<>(date, date.toAbsoluteDate(), interpolatedPV, interpolatedAC); } /** Get the inverse transform of the instance. * @return inverse transform of the instance */ + @Override public FieldTransform getInverse() { final FieldRotation r = angular.getRotation(); @@ -563,58 +574,6 @@ public FieldTransform freeze() { FieldVector3D.getZero(date.getField()))); } - /** Transform a position vector (including translation effects). - * @param position vector to transform - * @return transformed position - */ - public FieldVector3D transformPosition(final Vector3D position) { - return angular.getRotation().applyTo(cartesian.getPosition().add(position)); - } - - /** Transform a position vector (including translation effects). - * @param position vector to transform - * @return transformed position - */ - public FieldVector3D transformPosition(final FieldVector3D position) { - return angular.getRotation().applyTo(position.add(cartesian.getPosition())); - } - - /** Transform a vector (ignoring translation effects). - * @param vector vector to transform - * @return transformed vector - */ - public FieldVector3D transformVector(final Vector3D vector) { - return angular.getRotation().applyTo(vector); - } - - /** Transform a vector (ignoring translation effects). - * @param vector vector to transform - * @return transformed vector - */ - public FieldVector3D transformVector(final FieldVector3D vector) { - return angular.getRotation().applyTo(vector); - } - - /** Transform a line. - * @param line to transform - * @return transformed line - */ - public FieldLine transformLine(final Line line) { - final FieldVector3D transformedP0 = transformPosition(line.getOrigin()); - final FieldVector3D transformedP1 = transformPosition(line.pointAt(1.0e6)); - return new FieldLine<>(transformedP0, transformedP1, 1.0e-10); - } - - /** Transform a line. - * @param line to transform - * @return transformed line - */ - public FieldLine transformLine(final FieldLine line) { - final FieldVector3D transformedP0 = transformPosition(line.getOrigin()); - final FieldVector3D transformedP1 = transformPosition(line.pointAt(1.0e6)); - return new FieldLine<>(transformedP0, transformedP1, 1.0e-10); - } - /** Transform {@link TimeStampedPVCoordinates} including kinematic effects. *

        * In order to allow the user more flexibility, this method does not check for @@ -956,6 +915,12 @@ public FieldPVCoordinates transformPVCoordinates(final FieldPVCoordinates return pv; } + /** {@inheritDoc} */ + @Override + public FieldTransform freeze() { + return this; + } + /** {@inheritDoc} */ @Override public void getJacobian(final CartesianDerivativesFilter selector, final T[][] jacobian) { diff --git a/src/main/java/org/orekit/frames/FieldTransformGenerator.java b/src/main/java/org/orekit/frames/FieldTransformGenerator.java index 50a62d85ff..89d021c221 100644 --- a/src/main/java/org/orekit/frames/FieldTransformGenerator.java +++ b/src/main/java/org/orekit/frames/FieldTransformGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,6 +31,7 @@ * @see GenericTimeStampedCache * @since 9.0 * @author Luc Maisonobe + * @param type of the field elements */ public class FieldTransformGenerator> implements TimeStampedGenerator> { diff --git a/src/main/java/org/orekit/frames/FixedTransformProvider.java b/src/main/java/org/orekit/frames/FixedTransformProvider.java index 94f7c5d272..45465d4c91 100644 --- a/src/main/java/org/orekit/frames/FixedTransformProvider.java +++ b/src/main/java/org/orekit/frames/FixedTransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/Frame.java b/src/main/java/org/orekit/frames/Frame.java index f80bd75734..8e2139547b 100644 --- a/src/main/java/org/orekit/frames/Frame.java +++ b/src/main/java/org/orekit/frames/Frame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import java.util.function.Function; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; +import org.hipparchus.FieldElement; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; @@ -258,13 +258,12 @@ public Transform getTransformTo(final Frame destination, final AbsoluteDate date * @return transform from the instance to the destination frame */ public > FieldTransform getTransformTo(final Frame destination, final FieldAbsoluteDate date) { - final Field field = date.getField(); - return getTransformTo( - destination, - FieldTransform.getIdentity(field), - frame -> frame.getTransformProvider().getTransform(date), - (t1, t2) -> new FieldTransform<>(date, t1, t2), - FieldTransform::getInverse); + + return getTransformTo(destination, + FieldTransform.getIdentity(date.getField()), + frame -> frame.getTransformProvider().getTransform(date), + (t1, t2) -> new FieldTransform<>(date, t1, t2), + FieldTransform::getInverse); } /** @@ -292,6 +291,42 @@ public StaticTransform getStaticTransformTo(final Frame destination, StaticTransform::getInverse); } + /** + * Get the static portion of the transform from the instance to another + * frame. The returned transform is static in the sense that it includes + * translations and rotations, but not rates. + * + *

        This method is often more performant than {@link + * #getTransformTo(Frame, FieldAbsoluteDate)} when rates are not needed. + * + *

        A first check is made on the FieldAbsoluteDate because "fielded" transforms have low-performance.
        + * The date field is checked with {@link FieldElement#isZero()}.
        + * If true, the un-fielded version of the transform computation is used. + * + * @param type of the elements + * @param destination destination frame to which we want to transform + * vectors + * @param date the date (can be null if it is sure than no date + * dependent frame is used) + * @return static transform from the instance to the destination frame + * @since 12.0 + */ + public > FieldStaticTransform getStaticTransformTo(final Frame destination, + final FieldAbsoluteDate date) { + if (date.hasZeroField()) { + // If date field is Zero, then use the un-fielded version for performances + return FieldStaticTransform.of(date, getStaticTransformTo(destination, date.toAbsoluteDate())); + + } else { + // Use classic fielded function + return getTransformTo(destination, + FieldStaticTransform.getIdentity(date.getField()), + frame -> frame.getTransformProvider().getStaticTransform(date), + (t1, t2) -> FieldStaticTransform.compose(date, t1, t2), + FieldStaticTransform::getInverse); + } + } + /** * Generic get transform method that builds the transform from {@code this} * to {@code destination}. diff --git a/src/main/java/org/orekit/frames/Frames.java b/src/main/java/org/orekit/frames/Frames.java index c80faeb1d1..76c1e30977 100644 --- a/src/main/java/org/orekit/frames/Frames.java +++ b/src/main/java/org/orekit/frames/Frames.java @@ -22,6 +22,7 @@ import org.orekit.bodies.CelestialBodies; import org.orekit.time.TimeScales; +import org.orekit.time.UT1Scale; import org.orekit.utils.IERSConventions; /** @@ -130,6 +131,20 @@ VersionedITRF getITRF(ITRFVersion version, IERSConventions conventions, boolean simpleEOP); + /** Build an uncached International Terrestrial Reference Frame with specific {@link EOPHistory EOP history}. + *

        + * This frame and its parent frames (TIRF and CIRF) will not be cached, they are + * rebuilt from scratch each time this method is called. This factory method is intended + * to be used when EOP history is changed at run time. For regular ITRF use, the + * {@link #getITRF(IERSConventions, boolean)} and {link {@link #getITRF(ITRFVersion, IERSConventions, boolean)} + * are more suitable. + *

        + * @param ut1 UT1 time scale (contains the {@link EOPHistory EOP history}) + * @return an ITRF frame with specified time scale and embedded EOP history + * @since 12.0 + */ + Frame buildUncachedITRF(UT1Scale ut1); + /** Get the TIRF reference frame. * @param conventions IERS conventions to apply * @param simpleEOP if true, tidal effects are ignored when interpolating EOP @@ -148,7 +163,7 @@ FactoryManagedFrame getCIRF(IERSConventions conventions, boolean simpleEOP); /** Get the VEIS 1950 reference frame. - *

        Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.

        + *

        Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.

        * @return the selected reference frame singleton. */ FactoryManagedFrame getVeis1950(); diff --git a/src/main/java/org/orekit/frames/FramesFactory.java b/src/main/java/org/orekit/frames/FramesFactory.java index 1beddc07ba..11b20d1ee4 100644 --- a/src/main/java/org/orekit/frames/FramesFactory.java +++ b/src/main/java/org/orekit/frames/FramesFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,9 +18,13 @@ import org.hipparchus.CalculusFieldElement; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.CelestialBodies; import org.orekit.data.DataContext; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScales; +import org.orekit.time.UT1Scale; +import org.orekit.time.UTCScale; import org.orekit.utils.IERSConventions; @@ -158,23 +162,23 @@ public class FramesFactory { /** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU1980 compatibles). */ public static final String RAPID_DATA_PREDICTION_COLUMNS_1980_FILENAME = "^finals\\.[^.]*$"; - /** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU1980 compatibles). */ - public static final String RAPID_DATA_PREDICTION_XML_1980_FILENAME = "^finals\\..*\\.xml$"; + /** Default regular expression for the EOP XML files (IAU1980 compatibles). */ + public static final String XML_1980_FILENAME = "^(:finals|eopc04_\\d\\d)\\..*\\.xml$"; /** Default regular expression for the EOPC04 files (IAU1980 compatibles). */ - public static final String EOPC04_1980_FILENAME = "^eopc04_\\d\\d\\.(\\d\\d)$"; + public static final String EOPC04_1980_FILENAME = "^eopc04(_\\d\\d)?\\.\\d\\d$"; /** Default regular expression for the BulletinB files (IAU1980 compatibles). */ public static final String BULLETINB_1980_FILENAME = "^bulletinb(_IAU1980)?((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$"; /** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU2000 compatibles). */ - public static final String RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME = "^finals2000A\\.[^.]*$"; + public static final String RAPID_DATA_PREDICTION_COLUMNS_2000_FILENAME = "^finals2000A\\.[^.]*$"; - /** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU2000 compatibles). */ - public static final String RAPID_DATA_PREDICITON_XML_2000_FILENAME = "^finals2000A\\..*\\.xml$"; + /** Default regular expression for the EOP XML files (IAU2000 compatibles). */ + public static final String XML_2000_FILENAME = "^(:finals2000A|eopc04_\\d\\d_IAU2000)\\..*\\.xml$"; /** Default regular expression for the EOPC04 files (IAU2000 compatibles). */ - public static final String EOPC04_2000_FILENAME = "^eopc04_\\d\\d_IAU2000\\.(\\d\\d)$"; + public static final String EOPC04_2000_FILENAME = "^eopc04(_\\d\\d_IAU2000)?\\.\\d\\d$"; /** Default regular expression for the BulletinB files (IAU2000 compatibles). */ public static final String BULLETINB_2000_FILENAME = "^bulletinb(_IAU2000)?((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$"; @@ -182,6 +186,11 @@ public class FramesFactory { /** Default regular expression for the BulletinA files (IAU1980 and IAU2000 compatibles). */ public static final String BULLETINA_FILENAME = "^bulletina-[ivxlcdm]+-\\d\\d\\d\\.txt$"; + /** Default regular expression for the csv files (IAU1980 and IAU2000 compatibles). + * @since 12.0 + */ + public static final String CSV_FILENAME = "^(?:eopc04|bulletina|bulletinb).*\\.csv$"; + /** Private constructor. *

        This class is a utility class, it should neither have a public * nor a default constructor. This private constructor prevents @@ -209,8 +218,7 @@ public static LazyLoadedFrames getFrames() { * @param rapidDataColumnsSupportedNames regular expression for supported * rapid data columns EOP files names * (may be null if the default IERS file names are used) - * @param rapidDataXMLSupportedNames regular expression for supported - * rapid data XML EOP files names + * @param rapidDataXMLSupportedNames regular expression for supported XML EOP files names * (may be null if the default IERS file names are used) * @param eopC04SupportedNames regular expression for supported EOP C04 files names * (may be null if the default IERS file names are used) @@ -218,23 +226,28 @@ public static LazyLoadedFrames getFrames() { * (may be null if the default IERS file names are used) * @param bulletinASupportedNames regular expression for supported bulletin A files names * (may be null if the default IERS file names are used) + * @param csvSupportedNames regular expression for supported csv files names + * (may be null if the default IERS file names are used) * @see IERS EOP C04 files - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) * @see #clearEOPHistoryLoaders() - * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String) + * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String, String) + * @since 12.0 */ @DefaultDataContext public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames, final String rapidDataXMLSupportedNames, final String eopC04SupportedNames, final String bulletinBSupportedNames, - final String bulletinASupportedNames) { + final String bulletinASupportedNames, + final String csvSupportedNames) { getFrames().addDefaultEOP1980HistoryLoaders( rapidDataColumnsSupportedNames, rapidDataXMLSupportedNames, eopC04SupportedNames, bulletinBSupportedNames, - bulletinASupportedNames); + bulletinASupportedNames, + csvSupportedNames); } /** Add the default loaders for EOP history (IAU 2000/2006 precession/nutation). @@ -246,8 +259,7 @@ public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumns * @param rapidDataColumnsSupportedNames regular expression for supported * rapid data columns EOP files names * (may be null if the default IERS file names are used) - * @param rapidDataXMLSupportedNames regular expression for supported - * rapid data XML EOP files names + * @param xmlSupportedNames regular expression for supported XML EOP files names * (may be null if the default IERS file names are used) * @param eopC04SupportedNames regular expression for supported EOP C04 files names * (may be null if the default IERS file names are used) @@ -255,39 +267,44 @@ public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumns * (may be null if the default IERS file names are used) * @param bulletinASupportedNames regular expression for supported bulletin A files names * (may be null if the default IERS file names are used) + * @param csvSupportedNames regular expression for supported csv files names + * (may be null if the default IERS file names are used) * @see IERS EOP C04 files - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) * @see #clearEOPHistoryLoaders() - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String) + * @since 12.0 */ @DefaultDataContext public static void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames, - final String rapidDataXMLSupportedNames, + final String xmlSupportedNames, final String eopC04SupportedNames, final String bulletinBSupportedNames, - final String bulletinASupportedNames) { + final String bulletinASupportedNames, + final String csvSupportedNames) { getFrames().addDefaultEOP2000HistoryLoaders( rapidDataColumnsSupportedNames, - rapidDataXMLSupportedNames, + xmlSupportedNames, eopC04SupportedNames, bulletinBSupportedNames, - bulletinASupportedNames); + bulletinASupportedNames, + csvSupportedNames); } /** Add a loader for Earth Orientation Parameters history. * @param conventions IERS conventions to which EOP history applies * @param loader custom loader to add for the EOP history - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String) * @see #clearEOPHistoryLoaders() */ @DefaultDataContext - public static void addEOPHistoryLoader(final IERSConventions conventions, final EOPHistoryLoader loader) { + public static void addEOPHistoryLoader(final IERSConventions conventions, final EopHistoryLoader loader) { getFrames().addEOPHistoryLoader(conventions, loader); } /** Clear loaders for Earth Orientation Parameters history. - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String) + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String) */ @DefaultDataContext public static void clearEOPHistoryLoaders() { @@ -317,12 +334,12 @@ public static void setEOPContinuityThreshold(final double threshold) { /** Get Earth Orientation Parameters history. *

        - * If no {@link EOPHistoryLoader} has been added by calling {@link - * #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) addEOPHistoryLoader} + * If no {@link EopHistoryLoader} has been added by calling {@link + * #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) addEOPHistoryLoader} * or if {@link #clearEOPHistoryLoaders() clearEOPHistoryLoaders} has been * called afterwards, the {@link #addDefaultEOP1980HistoryLoaders(String, String, - * String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String, - * String, String, String, String)} methods will be called automatically with + * String, String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String, + * String, String, String, String, String)} methods will be called automatically with * supported file names parameters all set to null, in order to get the default * loaders configuration. *

        @@ -425,7 +442,7 @@ public static FactoryManagedFrame getTIRF(final IERSConventions conventions) { return getFrames().getTIRF(conventions); } - /** Get an specific International Terrestrial Reference Frame. + /** Get a specific International Terrestrial Reference Frame. *

        * Note that if a specific version of ITRF is required, then {@code simpleEOP} * should most probably be set to {@code false}, as ignoring tidal effects @@ -445,6 +462,27 @@ public static VersionedITRF getITRF(final ITRFVersion version, return getFrames().getITRF(version, conventions, simpleEOP); } + /** Build an uncached International Terrestrial Reference Frame with specific {@link EOPHistory EOP history}. + *

        + * This frame and its parent frames (TIRF and CIRF) will not be cached, they are + * rebuilt from scratch each time this method is called. This factory method is intended + * to be used when EOP history is changed at run time. For regular ITRF use, the + * {@link #getITRF(IERSConventions, boolean)} and {link {@link #getITRF(ITRFVersion, IERSConventions, boolean)} + * are more suitable. + *

        + * @param eopHistory EOP history + * @param utc UTC time scale + * @return an ITRF frame with specified EOP history + * @since 12.0 + */ + public static Frame buildUncachedITRF(final EOPHistory eopHistory, final UTCScale utc) { + final TimeScales timeScales = TimeScales.of(utc.getBaseOffsets(), + (conventions, timescales) -> eopHistory.getEntries()); + final UT1Scale ut1 = timeScales.getUT1(eopHistory.getConventions(), eopHistory.isSimpleEop()); + final Frames frames = Frames.of(timeScales, (CelestialBodies) null); + return frames.buildUncachedITRF(ut1); + } + /** Get the TIRF reference frame. * @param conventions IERS conventions to apply * @param simpleEOP if true, tidal effects are ignored when interpolating EOP @@ -469,7 +507,7 @@ public static FactoryManagedFrame getCIRF(final IERSConventions conventions, } /** Get the VEIS 1950 reference frame. - *

        Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.

        + *

        Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.

        * @return the selected reference frame singleton. */ @DefaultDataContext @@ -481,7 +519,7 @@ public static FactoryManagedFrame getVeis1950() { * @param conventions IERS conventions to apply * @param simpleEOP if true, tidal effects are ignored when interpolating EOP * @return the selected reference frame singleton. - * @since 6.1 + * @since 6.1 */ @DefaultDataContext public static FactoryManagedFrame getITRFEquinox(final IERSConventions conventions, @@ -772,7 +810,7 @@ private static TransformProvider peel(final TransformProvider provider) { peeled = ((ShiftingTransformProvider) peeled).getRawProvider(); } else if (peeled instanceof EOPBasedTransformProvider && ((EOPBasedTransformProvider) peeled).getEOPHistory() != null && - ((EOPBasedTransformProvider) peeled).getEOPHistory().usesInterpolation()) { + ((EOPBasedTransformProvider) peeled).getEOPHistory().cachesTidalCorrection()) { peeled = ((EOPBasedTransformProvider) peeled).getNonInterpolatingProvider(); } else { peeling = false; diff --git a/src/main/java/org/orekit/frames/GTODProvider.java b/src/main/java/org/orekit/frames/GTODProvider.java index 8f4e712313..3370492f6c 100644 --- a/src/main/java/org/orekit/frames/GTODProvider.java +++ b/src/main/java/org/orekit/frames/GTODProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -102,7 +102,7 @@ public EOPHistory getEOPHistory() { /** {@inheritDoc} */ @Override public GTODProvider getNonInterpolatingProvider() { - return new GTODProvider(conventions, eopHistory.getNonInterpolatingEOPHistory(), + return new GTODProvider(conventions, eopHistory.getEOPHistoryWithoutCachedTidalCorrection(), gastFunction); } @@ -159,6 +159,20 @@ public > FieldTransform getTransform(final } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + + // compute Greenwich apparent sidereal time, in radians + final T gast = gastFunction.value(date); + + // set up the transform from parent TOD + return FieldStaticTransform.of( + date, + new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), gast, RotationConvention.FRAME_TRANSFORM)); + + } + /** Replace the instance with a data transfer object for serialization. *

        * This intermediate class serializes only the frame key. diff --git a/src/main/java/org/orekit/frames/HelmertTransformation.java b/src/main/java/org/orekit/frames/HelmertTransformation.java index 41d2403e0c..7427284e26 100644 --- a/src/main/java/org/orekit/frames/HelmertTransformation.java +++ b/src/main/java/org/orekit/frames/HelmertTransformation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; @@ -688,4 +689,29 @@ public > FieldTransform getTransform(final } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + + // field + final Field field = date.getField(); + + // compute parameters evolution since reference epoch + final T dt = date.durationFrom(epoch); + final FieldVector3D dR = new FieldVector3D<>(field.getOne(), rotationVector, dt, rotationRate); + + // build translation part + final FieldVector3D translation = new FieldPVCoordinates<>(date.getField(), cartesian).shiftedBy(dt).getPosition(); + + // build rotation part + final T angle = dR.getNorm(); + final FieldRotation rotation = (angle.getReal() < Precision.SAFE_MIN) ? + FieldRotation.getIdentity(field) : + new FieldRotation<>(dR, angle, RotationConvention.VECTOR_OPERATOR); + + // combine both parts + return FieldStaticTransform.of(date, translation, rotation); + + } + } diff --git a/src/main/java/org/orekit/frames/ITRFProvider.java b/src/main/java/org/orekit/frames/ITRFProvider.java index 3d153152f1..7583b55385 100644 --- a/src/main/java/org/orekit/frames/ITRFProvider.java +++ b/src/main/java/org/orekit/frames/ITRFProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -62,7 +62,7 @@ public EOPHistory getEOPHistory() { /** {@inheritDoc} */ @Override public ITRFProvider getNonInterpolatingProvider() { - return new ITRFProvider(eopHistory.getNonInterpolatingEOPHistory()); + return new ITRFProvider(eopHistory.getEOPHistoryWithoutCachedTidalCorrection()); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/frames/ITRFVersion.java b/src/main/java/org/orekit/frames/ITRFVersion.java index 8c0c2c67f9..20bf5d0d9b 100644 --- a/src/main/java/org/orekit/frames/ITRFVersion.java +++ b/src/main/java/org/orekit/frames/ITRFVersion.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -298,6 +298,12 @@ public > FieldTransform getTransform(final return provider.getTransform(date); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + return provider.getStaticTransform(date); + } + } } diff --git a/src/main/java/org/orekit/frames/ITRFVersionLoader.java b/src/main/java/org/orekit/frames/ITRFVersionLoader.java index ad44f0dafe..6531118dbe 100644 --- a/src/main/java/org/orekit/frames/ITRFVersionLoader.java +++ b/src/main/java/org/orekit/frames/ITRFVersionLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -41,12 +41,12 @@ * the {@link ITRFVersion ITRF versions} that each * type of Earth Orientation Parameter file contains * for each date. This configuration file is used to - * interpret {@link EOPC04FilesLoader EOP C04} files, + * interpret {@link EopC04FilesLoader EOP C04} files, * {@link BulletinAFilesLoader Bulletin A} files, * {@link BulletinBFilesLoader Bulletin B} files, * {@link RapidDataAndPredictionColumnsLoader rapid data * and prediction files in columns format} files, - * {@link RapidDataAndPredictionXMLLoader rapid data + * {@link EopXmlLoader rapid data * and prediction files in XML format} files... *

        *

        This file is an Orekit-specific configuration file. @@ -54,11 +54,11 @@ *

        * This class is immutable and hence thread-safe *

        - * @see EOPC04FilesLoader + * @see EopC04FilesLoader * @see BulletinAFilesLoader * @see BulletinBFilesLoader * @see RapidDataAndPredictionColumnsLoader - * @see RapidDataAndPredictionXMLLoader + * @see EopXmlLoader * @author Luc Maisonobe * @since 9.2 */ diff --git a/src/main/java/org/orekit/frames/InterpolatingTransformProvider.java b/src/main/java/org/orekit/frames/InterpolatingTransformProvider.java index bf6e0eaa41..a51bbb993b 100644 --- a/src/main/java/org/orekit/frames/InterpolatingTransformProvider.java +++ b/src/main/java/org/orekit/frames/InterpolatingTransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -111,7 +111,7 @@ public TransformProvider getRawProvider() { * @return number of interpolation grid points */ public int getGridPoints() { - return cache.getNeighborsSize(); + return cache.getMaxNeighborsSize(); } /** Get the grid points time step. @@ -139,12 +139,12 @@ public > FieldTransform getTransform(final (GenericTimeStampedCache>) fieldCaches.get(date.getField()); if (fieldCache == null) { fieldCache = - new GenericTimeStampedCache>(cache.getNeighborsSize(), + new GenericTimeStampedCache>(cache.getMaxNeighborsSize(), cache.getMaxSlots(), cache.getMaxSpan(), cache.getNewSlotQuantumGap(), new FieldTransformGenerator<>(date.getField(), - cache.getNeighborsSize(), + cache.getMaxNeighborsSize(), rawProvider, step)); fieldCaches.put(date.getField(), fieldCache); @@ -167,7 +167,7 @@ public > FieldTransform getTransform(final */ private Object writeReplace() { return new DTO(rawProvider, cFilter.getMaxOrder(), aFilter.getMaxOrder(), - cache.getNeighborsSize(), step, + cache.getMaxNeighborsSize(), step, cache.getMaxSlots(), cache.getMaxSpan(), cache.getNewSlotQuantumGap()); } diff --git a/src/main/java/org/orekit/frames/L1Frame.java b/src/main/java/org/orekit/frames/L1Frame.java index 912c8eab4c..d0b41790e3 100644 --- a/src/main/java/org/orekit/frames/L1Frame.java +++ b/src/main/java/org/orekit/frames/L1Frame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/L1TransformProvider.java b/src/main/java/org/orekit/frames/L1TransformProvider.java index fe7cc03d8a..1e6710772c 100644 --- a/src/main/java/org/orekit/frames/L1TransformProvider.java +++ b/src/main/java/org/orekit/frames/L1TransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -116,6 +116,19 @@ public > FieldTransform getTransform(final new FieldTransform<>(date, rotation)); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + final FieldPVCoordinates pv21 = secondaryBody.getPVCoordinates(date, frame); + final FieldVector3D translation = getL1(pv21.getPosition()).negate(); + final FieldRotation rotation = new FieldRotation<>(pv21.getPosition(), pv21.getVelocity(), + FieldVector3D.getPlusI(date.getField()), FieldVector3D.getPlusJ(date.getField())); + return FieldStaticTransform.compose( + date, + FieldStaticTransform.of(date, translation), + FieldStaticTransform.of(date, rotation)); + } + /** Compute the coordinates of the L1 point. * @param primaryToSecondary relative position of secondary body with respect to primary body * @return coordinates of the L1 point given in frame: primaryBody.getInertiallyOrientedFrame() diff --git a/src/main/java/org/orekit/frames/L2Frame.java b/src/main/java/org/orekit/frames/L2Frame.java index a89fa0f9a5..1ed30fc1ff 100644 --- a/src/main/java/org/orekit/frames/L2Frame.java +++ b/src/main/java/org/orekit/frames/L2Frame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/L2TransformProvider.java b/src/main/java/org/orekit/frames/L2TransformProvider.java index 6dff168853..f36b28141f 100644 --- a/src/main/java/org/orekit/frames/L2TransformProvider.java +++ b/src/main/java/org/orekit/frames/L2TransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -116,6 +116,19 @@ public > FieldTransform getTransform(final new FieldTransform<>(date, rotation)); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + final FieldPVCoordinates pv21 = secondaryBody.getPVCoordinates(date, frame); + final FieldVector3D translation = getL2(pv21.getPosition()).negate(); + final FieldRotation rotation = new FieldRotation<>(pv21.getPosition(), pv21.getVelocity(), + FieldVector3D.getPlusI(date.getField()), FieldVector3D.getPlusJ(date.getField())); + return FieldStaticTransform.compose( + date, + FieldStaticTransform.of(date, translation), + FieldStaticTransform.of(date, rotation)); + } + /** Compute the coordinates of the L2 point. * @param primaryToSecondary relative position of secondary body with respect to primary body * @return coordinates of the L2 point given in frame: primaryBody.getInertiallyOrientedFrame() diff --git a/src/main/java/org/orekit/frames/LOF.java b/src/main/java/org/orekit/frames/LOF.java new file mode 100644 index 0000000000..7376993569 --- /dev/null +++ b/src/main/java/org/orekit/frames/LOF.java @@ -0,0 +1,331 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * Interface for local orbital frame. + * + * @author Vincent Cucchietti + */ +public interface LOF { + + /** + * Get the rotation from input to output {@link LOF local orbital frame}. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. + * + * @param field field to which the elements belong + * @param in input commonly used local orbital frame + * @param out output commonly used local orbital frame + * @param date date of the rotation + * @param pv position-velocity of the spacecraft in some inertial frame + * @param type of the field elements + * + * @return rotation from input to output local orbital frame + * + * @since 11.3 + */ + static > FieldRotation rotationFromLOFInToLOFOut(final Field field, + final LOF in, final LOF out, + final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + return out.rotationFromLOF(field, in, date, pv); + } + + /** + * Get the transform from input to output {@link LOF local orbital frame}. + * + * @param in input commonly used local orbital frame + * @param out output commonly used local orbital frame + * @param date date of the transform + * @param pv position-velocity of the spacecraft in some inertial frame + * @param type of the field elements + * + * @return rotation from input to output local orbital frame. + * + * @since 11.3 + */ + static > FieldTransform transformFromLOFInToLOFOut(final LOF in, final LOF out, + final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + return out.transformFromLOF(in, date, pv); + } + + /** + * Get the rotation from input to output {@link LOF local orbital frame}. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. + * + * @param in input commonly used local orbital frame + * @param out output commonly used local orbital frame + * @param date date of the rotation + * @param pv position-velocity of the spacecraft in some inertial frame + * + * @return rotation from input to output local orbital frame. + * + * @since 11.3 + */ + static Rotation rotationFromLOFInToLOFOut(final LOF in, final LOF out, final AbsoluteDate date, final PVCoordinates pv) { + return out.rotationFromLOF(in, date, pv); + } + + /** + * Get the transform from input to output {@link LOF local orbital frame}. + * + * @param in input commonly used local orbital frame + * @param out output commonly used local orbital frame + * @param date date of the transform + * @param pv position-velocity of the spacecraft in some inertial frame + * + * @return rotation from input to output local orbital frame + * + * @since 11.3 + */ + static Transform transformFromLOFInToLOFOut(final LOF in, final LOF out, final AbsoluteDate date, + final PVCoordinates pv) { + return out.transformFromLOF(in, date, pv); + } + + /** + * Get the rotation from input {@link LOF local orbital frame} to the instance. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. + * + * @param field field to which the elements belong + * @param fromLOF input local orbital frame + * @param date date of the rotation + * @param pv position-velocity of the spacecraft in some inertial frame + * @param type of the field elements + * + * @return rotation from input local orbital frame to the instance + * + * @since 11.3 + */ + default > FieldRotation rotationFromLOF(final Field field, + final LOF fromLOF, + final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + + // First compute the rotation from the input LOF to the pivot inertial + final FieldRotation fromLOFToInertial = fromLOF.rotationFromInertial(field, date, pv).revert(); + + // Then compute the rotation from the pivot inertial to the output LOF + final FieldRotation inertialToThis = this.rotationFromInertial(field, date, pv); + + // Output composed rotation + return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM); + } + + /** + * Get the rotation from input {@link LOF commonly used local orbital frame} to the instance. + * + * @param fromLOF input local orbital frame + * @param date date of the transform + * @param pv position-velocity of the spacecraft in some inertial frame + * @param type of the field elements + * + * @return rotation from input local orbital frame to the instance + * + * @since 11.3 + */ + default > FieldTransform transformFromLOF(final LOF fromLOF, + final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + + // Get transform from input local orbital frame to inertial + final FieldTransform fromLOFToInertial = fromLOF.transformFromInertial(date, pv).getInverse(); + + // Get transform from inertial to output local orbital frame + final FieldTransform inertialToLOFOut = this.transformFromInertial(date, pv); + + // Output composition of both transforms + return new FieldTransform<>(date, fromLOFToInertial, inertialToLOFOut); + } + + /** + * Get the transform from an inertial frame defining position-velocity and the local orbital frame. + * + * @param date current date + * @param pv position-velocity of the spacecraft in some inertial frame + * @param type of the fields elements + * + * @return transform from the frame where position-velocity are defined to local orbital frame + * + * @since 9.0 + */ + default > FieldTransform transformFromInertial(final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + + // compute the translation part of the transform + final FieldTransform translation = new FieldTransform<>(date, pv.negate()); + + // compute the rotation part of the transform + final FieldRotation r = rotationFromInertial(date.getField(), date, pv); + final FieldVector3D p = pv.getPosition(); + final FieldVector3D momentum = pv.getMomentum(); + final FieldTransform rotation = new FieldTransform<>(date, r, + new FieldVector3D<>(p.getNormSq().reciprocal(), + r.applyTo(momentum))); + + final FieldTransform transform = new FieldTransform<>(date, translation, rotation); + + // If LOF is considered pseudo-inertial, freeze transform + return isQuasiInertial() ? transform.freeze() : transform; + + } + + /** + * Get the rotation from inertial frame to local orbital frame. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be + * called and the complete rotation transform must be extracted from it. + *

        + * + * @param field field to which the elements belong + * @param date date of the rotation + * @param pv position-velocity of the spacecraft in some inertial frame + * @param type of the field elements + * + * @return rotation from inertial frame to local orbital frame + * + * @since 9.0 + */ + > FieldRotation rotationFromInertial(Field field, FieldAbsoluteDate date, + FieldPVCoordinates pv); + + /** + * Get the rotation from input {@link LOF local orbital frame} to the instance. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. + * + * @param fromLOF input local orbital frame + * @param date date of the rotation + * @param pv position-velocity of the spacecraft in some inertial frame + * + * @return rotation from input local orbital frame to the instance + * + * @since 11.3 + */ + default Rotation rotationFromLOF(final LOF fromLOF, final AbsoluteDate date, final PVCoordinates pv) { + + // First compute the rotation from the input LOF to the pivot inertial + final Rotation fromLOFToInertial = fromLOF.rotationFromInertial(date, pv).revert(); + + // Then compute the rotation from the pivot inertial to the output LOF + final Rotation inertialToThis = this.rotationFromInertial(date, pv); + + // Output composed rotation + return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM); + } + + /** + * Get the rotation from input {@link LOF local orbital frame} to the instance. + * + * @param fromLOF input local orbital frame + * @param date date of the transform + * @param pv position-velocity of the spacecraft in some inertial frame + * + * @return rotation from input local orbital frame to the instance + * + * @since 11.3 + */ + default Transform transformFromLOF(final LOF fromLOF, final AbsoluteDate date, final PVCoordinates pv) { + + // First compute the rotation from the input LOF to the pivot inertial + final Transform fromLOFToInertial = fromLOF.transformFromInertial(date, pv).getInverse(); + + // Then compute the rotation from the pivot inertial to the output LOF + final Transform inertialToThis = this.transformFromInertial(date, pv); + + // Output composed rotation + return new Transform(date, fromLOFToInertial, inertialToThis); + } + + /** + * Get the transform from an inertial frame defining position-velocity and the local orbital frame. + * + * @param date current date + * @param pv position-velocity of the spacecraft in some inertial frame + * + * @return transform from the frame where position-velocity are defined to local orbital frame + */ + default Transform transformFromInertial(final AbsoluteDate date, final PVCoordinates pv) { + + // compute the translation part of the transform + final Transform translation = new Transform(date, pv.negate()); + + // compute the rotation part of the transform + final Rotation r = rotationFromInertial(date, pv); + final Vector3D p = pv.getPosition(); + final Vector3D momentum = pv.getMomentum(); + final Transform rotation = new Transform(date, r, new Vector3D(1.0 / p.getNormSq(), r.applyTo(momentum))); + + final Transform transform = new Transform(date, translation, rotation); + + // If LOF is considered pseudo-inertial, freeze transform + return isQuasiInertial() ? transform.freeze() : transform; + + } + + /** + * Get the rotation from inertial frame to local orbital frame. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be called and + * the complete rotation transform must be extracted from it. + * + * @param date date of the rotation + * @param pv position-velocity of the spacecraft in some inertial frame + * + * @return rotation from inertial frame to local orbital frame + */ + Rotation rotationFromInertial(AbsoluteDate date, PVCoordinates pv); + + /** Get flag that indicates if current local orbital frame shall be treated as pseudo-inertial. + * @return flag that indicates if current local orbital frame shall be treated as pseudo-inertial + */ + default boolean isQuasiInertial() { + return false; + } + + /** Get name of the local orbital frame. + * @return name of the local orbital frame + */ + String getName(); + +} diff --git a/src/main/java/org/orekit/frames/LOFType.java b/src/main/java/org/orekit/frames/LOFType.java index a4c77fb6d9..f82e60e6f1 100644 --- a/src/main/java/org/orekit/frames/LOFType.java +++ b/src/main/java/org/orekit/frames/LOFType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,6 +23,9 @@ import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.definitions.OrbitRelativeFrame; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; @@ -32,8 +35,10 @@ * Enumerate for different types of Local Orbital Frames. * * @author Luc Maisonobe + * @author Maxime Journot + * @author Vincent Cucchietti */ -public enum LOFType { +public enum LOFType implements LOF { /** Constant for TNW frame * (X axis aligned with velocity, Z axis aligned with orbital momentum). @@ -66,6 +71,53 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.PLUS_K)); } + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.TNW; + } + + }, + + /** + * Constant for TNW frame considered inertial (X axis aligned with velocity, Z axis aligned with orbital momentum). + *

        + * The axes of this frame are parallel to the axes of the {@link #VNC} and {@link #NTW} frames: + *

          + *
        • XTNW = XVNC = YNTW
        • + *
        • YTNW = -ZVNC = -XNTW
        • + *
        • ZTNW = YVNC = ZNTW
        • + *
        + * + * @see #VNC + * @see #NTW + */ + TNW_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return TNW.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return TNW.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.TNW_INERTIAL; + } + }, /** Constant for QSW frame @@ -101,6 +153,56 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.PLUS_K)); } + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.QSW; + } + + }, + + /** + * Constant for QSW frame considered inertial (X axis aligned with position, Z axis aligned with orbital momentum). + *

        + * This frame is also known as the {@link #LVLH} frame, both constants are equivalent. + *

        + *

        + * The axes of these frames are parallel to the axes of the {@link #VVLH} frame: + *

          + *
        • XQSW/LVLH = -ZVVLH
        • + *
        • YQSW/LVLH = XVVLH
        • + *
        • ZQSW/LVLH = -YVVLH
        • + *
        + * + * @see #LVLH + * @see #VVLH + */ + QSW_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return QSW.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return QSW.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.RSW_INERTIAL; + } + }, /** Constant for Local Vertical, Local Horizontal frame @@ -142,6 +244,62 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.PLUS_K)); } + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION); + } + + }, + + /** + * Constant for Local Vertical, Local Horizontal frame considered inertial (X axis aligned with position, Z axis + * aligned with orbital momentum). + *

        + * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for + * LVLH are used. This one is consistent with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and + * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}). + *

        + *

        + * This frame is also known as the {@link #QSW} frame, both constants are equivalent. + *

        + *

        + * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame: + *

          + *
        • XLVLH/QSW = -ZLVLH_CCSDS
        • + *
        • YLVLH/QSW = XLVLH_CCSDS
        • + *
        • ZLVLH/QSW = -YLVLH_CCSDS
        • + *
        + * + * @see #QSW + * @see #VVLH + */ + LVLH_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return LVLH.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return LVLH.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION); + } + }, /** Constant for Local Vertical, Local Horizontal frame as defined by CCSDS @@ -181,6 +339,60 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.MINUS_J)); } + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.LVLH; + } + + }, + + /** + * Constant for Local Vertical, Local Horizontal frame as defined by CCSDS considered inertial (Z axis aligned with + * opposite of position, Y axis aligned with opposite of orbital momentum). + *

        + * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for + * LVLH are used. This one is consistent with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However + * Vallado's book and with AGI's STK use another definition (see {@link #LVLH}). + *

        + *

        + * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames: + *

          + *
        • XLVLH_CCSDS/VVLH = YQSW/LVLH
        • + *
        • YLVLH_CCSDS/VVLH = -ZQSW/LVLH
        • + *
        • ZLVLH_CCSDS/VVLH = -XQSW/LVLH
        • + *
        + * + * @see #QSW + * @see #LVLH + * @since 11.0 + */ + LVLH_CCSDS_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return LVLH_CCSDS.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return LVLH_CCSDS.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.LVLH_INERTIAL; + } + }, /** Constant for Vehicle Velocity, Local Horizontal frame @@ -216,6 +428,60 @@ public > FieldRotation rotationFromInertial return LVLH_CCSDS.rotationFromInertial(field, pv); } + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.LVLH; + } + + }, + + /** + * Constant for Vehicle Velocity, Local Horizontal frame considered inertial (Z axis aligned with opposite of + * position, Y axis aligned with opposite of orbital momentum). + *

        + * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK. + *

        + *

        + * Beware that the name is misleading: in the general case (i.e. not perfectly circular), none of the axes is + * perfectly aligned with velocity! The preferred name for this should be {@link #LVLH_CCSDS}. + *

        + *

        + * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames: + *

          + *
        • XLVLH_CCSDS/VVLH = YQSW/LVLH
        • + *
        • YLVLH_CCSDS/VVLH = -ZQSW/LVLH
        • + *
        • ZLVLH_CCSDS/VVLH = -XQSW/LVLH
        • + *
        + * + * @see #LVLH_CCSDS + */ + VVLH_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return VVLH.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return VVLH.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.LVLH_INERTIAL; + } + }, /** Constant for Velocity - Normal - Co-normal frame @@ -249,10 +515,60 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.PLUS_J)); } + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.VNC_ROTATING; + } + }, - /** Constant for Equinoctial Coordinate System - * (X axis aligned with ascending node, Z axis aligned with orbital momentum). + /** + * Constant for Velocity - Normal - Co-normal frame considered inertial (X axis aligned with velocity, Y axis + * aligned with orbital momentum). + *

        + * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #NTW} frames: + *

          + *
        • XVNC = XTNW = YNTW
        • + *
        • YVNC = ZTNW = ZNTW
        • + *
        • ZVNC = -YTNW = XNTW
        • + *
        + * + * @see #TNW + * @see #NTW + */ + VNC_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return VNC.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return VNC.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.VNC_INERTIAL; + } + + }, + + /** + * Constant for Equinoctial Coordinate System (X axis aligned with ascending node, Z axis aligned with orbital + * momentum). + * * @since 11.0 */ EQW { @@ -275,6 +591,18 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.PLUS_J)); } + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.EQW_INERTIAL; + } + }, /** Constant for Transverse Velocity Normal coordinate system @@ -308,96 +636,114 @@ public > FieldRotation rotationFromInertial new FieldVector3D<>(field, Vector3D.PLUS_K)); } - }; + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.NTW_ROTATING; + } - /** - * Get the rotation from input to output {@link LOFType commonly used local orbital frame}. - *

        - * This rotation does not include any time derivatives. - *

        - * - * @param in input commonly used local orbital frame - * @param out output commonly used local orbital frame - * @param pv position-velocity of the spacecraft in some inertial frame - * @return rotation from input to output {@link LOFType commonly used local orbital frame}. - * @since 11.3 - */ - public static Rotation rotationFromLOFInToLOFOut(final LOFType in, final LOFType out, final PVCoordinates pv) { - return out.rotationFromLOFType(in, pv); - } + }, /** - * Get the rotation from input to output {@link LOFType commonly used local orbital frame}. + * Constant for Transverse Velocity Normal coordinate system considered inertial (Y axis aligned with velocity, Z + * axis aligned with orbital momentum). *

        - * This rotation does not include any time derivatives. - *

        + * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #VNC} frames: + *
          + *
        • XNTW = -YTNW = ZVNC
        • + *
        • YNTW = XTNW = XVNC
        • + *
        • ZNTW = ZTNW = YVNC
        • + *
        * - * @param field field to which the elements belong - * @param in input commonly used local orbital frame - * @param out output commonly used local orbital frame - * @param pv position-velocity of the spacecraft in some inertial frame - * @param type of the field elements - * @return rotation from input to output {@link LOFType commonly used local orbital frame}. - * @since 11.3 + * @see #TNW + * @see #VNC + * @since 11.0 */ - public static > FieldRotation rotationFromLOFInToLOFOut(final Field field, - final LOFType in, - final LOFType out, - final FieldPVCoordinates pv) { - return out.rotationFromLOFType(field, in, pv); - } + NTW_INERTIAL { + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates pv) { + return NTW.rotationFromInertial(pv); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates pv) { + return NTW.rotationFromInertial(field, pv); + } + + /** {@inheritDoc} */ + @Override + public boolean isQuasiInertial() { + return true; + } + + /** {@inheritDoc} */ + @Override + public OrbitRelativeFrame toOrbitRelativeFrame() { + return OrbitRelativeFrame.NTW_INERTIAL; + } + + }; + + /** {@inheritDoc} */ + public String getName() { + return this.name(); + }; /** - * Get the transform from input to output {@link LOFType commonly used local orbital frame}. + * Get the rotation from input to output {@link LOFType local orbital frame}. *

        - * This method simply builds the transform using the rotation obtained from - * {@link LOFType#rotationFromLOFInToLOFOut(LOFType, LOFType, PVCoordinates)} - *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. * * @param in input commonly used local orbital frame * @param out output commonly used local orbital frame - * @param date current date * @param pv position-velocity of the spacecraft in some inertial frame - * @return rotation from input to output {@link LOFType commonly used local orbital frame}. - * @since 11.3 + * + * @return rotation from input to output local orbital frame */ - public static Transform transformFromLOFInToLOFOut(final LOFType in, final LOFType out, final AbsoluteDate date, - final PVCoordinates pv) { - return new Transform(date, rotationFromLOFInToLOFOut(in, out, pv)); + static Rotation rotationFromLOFInToLOFOut(final LOFType in, final LOFType out, final PVCoordinates pv) { + return out.rotationFromLOF(in, pv); } /** - * Get the transform from input to output {@link LOFType commonly used local orbital frame}. + * Get the rotation from input to output {@link LOFType local orbital frame}. *

        - * This method simply builds the transform using the rotation obtained from - * {@link LOFType#rotationFromLOFInToLOFOut(LOFType, LOFType, PVCoordinates)} - *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. * * @param field field to which the elements belong * @param in input commonly used local orbital frame * @param out output commonly used local orbital frame - * @param date current date * @param pv position-velocity of the spacecraft in some inertial frame * @param type of the field elements - * @return rotation from input to output {@link LOFType commonly used local orbital frame}. - * @since 11.3 + * + * @return rotation from input to output local orbital frame */ - public static > FieldTransform transformFromLOFInToLOFOut(final Field field, - final LOFType in, - final LOFType out, - final FieldAbsoluteDate date, - final FieldPVCoordinates pv) { - return new FieldTransform(date, rotationFromLOFInToLOFOut(field, in, out, pv)); + static > FieldRotation rotationFromLOFInToLOFOut(final Field field, + final LOFType in, + final LOFType out, + final FieldPVCoordinates pv) { + return out.rotationFromLOF(field, in, pv); } /** - * Get the rotation from input {@link LOFType commonly used local orbital frame} to the instance. + * Get the rotation from input {@link LOF local orbital frame} to the instance. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)} method must be called and the complete rotation + * transform must be extracted from it. + * * @param fromLOF input local orbital frame * @param pv position-velocity of the spacecraft in some inertial frame + * * @return rotation from input local orbital frame to the instance - * @since 11.3 */ - public Rotation rotationFromLOFType(final LOFType fromLOF, final PVCoordinates pv) { + public Rotation rotationFromLOF(final LOFType fromLOF, final PVCoordinates pv) { // First compute the rotation from the input LOF to the pivot inertial final Rotation fromLOFToInertial = fromLOF.rotationFromInertial(pv).revert(); @@ -410,17 +756,22 @@ public Rotation rotationFromLOFType(final LOFType fromLOF, final PVCoordinates p } /** - * Get the rotation from input {@link LOFType commonly used local orbital frame} to the instance. + * Get the rotation from input {@link LOFType local orbital frame} to the instance. + *

        + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and the complete + * rotation transform must be extracted from it. + * * @param field field to which the elements belong * @param fromLOF input local orbital frame * @param pv position-velocity of the spacecraft in some inertial frame * @param type of the field elements + * * @return rotation from input local orbital frame to the instance - * @since 11.3 */ - public > FieldRotation rotationFromLOFType(final Field field, - final LOFType fromLOF, - final FieldPVCoordinates pv) { + public > FieldRotation rotationFromLOF(final Field field, + final LOFType fromLOF, + final FieldPVCoordinates pv) { // First compute the rotation from the input LOF to the pivot inertial final FieldRotation fromLOFToInertial = fromLOF.rotationFromInertial(field, pv).revert(); @@ -433,82 +784,63 @@ public > FieldRotation rotationFromLOFType( } /** - * Get the transform from an inertial frame defining position-velocity and the local orbital frame. - * - * @param date current date - * @param pv position-velocity of the spacecraft in some inertial frame - * @return transform from the frame where position-velocity are defined to local orbital frame - */ - public Transform transformFromInertial(final AbsoluteDate date, final PVCoordinates pv) { - - // compute the translation part of the transform - final Transform translation = new Transform(date, pv.negate()); - - // compute the rotation part of the transform - final Rotation r = rotationFromInertial(pv); - final Vector3D p = pv.getPosition(); - final Vector3D momentum = pv.getMomentum(); - final Transform rotation = - new Transform(date, r, new Vector3D(1.0 / p.getNormSq(), r.applyTo(momentum))); - - return new Transform(date, translation, rotation); - - } - - /** - * Get the transform from an inertial frame defining position-velocity and the local orbital frame. - * - * @param date current date - * @param pv position-velocity of the spacecraft in some inertial frame - * @param type of the fields elements - * @return transform from the frame where position-velocity are defined to local orbital frame - * @since 9.0 + * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use + * {@link #rotationFromInertial(PVCoordinates)} instead. */ - public > FieldTransform transformFromInertial(final FieldAbsoluteDate date, - final FieldPVCoordinates pv) { - - // compute the translation part of the transform - final FieldTransform translation = new FieldTransform<>(date, pv.negate()); - - // compute the rotation part of the transform - final FieldRotation r = rotationFromInertial(date.getField(), pv); - final FieldVector3D p = pv.getPosition(); - final FieldVector3D momentum = pv.getMomentum(); - final FieldTransform rotation = - new FieldTransform(date, r, new FieldVector3D<>(p.getNormSq().reciprocal(), r.applyTo(momentum))); - - return new FieldTransform<>(date, translation, rotation); - + @Override + public Rotation rotationFromInertial(final AbsoluteDate date, final PVCoordinates pv) { + return rotationFromInertial(pv); } /** * Get the rotation from inertial frame to local orbital frame. *

        - * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as - * well, the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be - * called and the complete rotation transform must be extracted from it. + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates)} method must be called and + * the complete rotation transform must be extracted from it. *

        * * @param pv position-velocity of the spacecraft in some inertial frame + * * @return rotation from inertial frame to local orbital frame */ public abstract Rotation rotationFromInertial(PVCoordinates pv); - /** Get the rotation from inertial frame to local orbital frame. + /** + * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use + * {@link #rotationFromInertial(Field, FieldPVCoordinates)} instead. + */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + return rotationFromInertial(field, pv); + } + + /** + * Get the rotation from inertial frame to local orbital frame. *

        - * This rotation does not include any time derivatives. If first - * time derivatives (i.e. rotation rate) is needed as well, the full - * {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates) transformFromInertial} - * method must be called and the complete rotation transform must be extracted - * from it. + * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well, + * the full {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be + * called and the complete rotation transform must be extracted from it. *

        + * * @param field field to which the elements belong * @param pv position-velocity of the spacecraft in some inertial frame * @param type of the field elements + * * @return rotation from inertial frame to local orbital frame - * @since 9.0 */ public abstract > FieldRotation rotationFromInertial(Field field, - FieldPVCoordinates pv); + FieldPVCoordinates pv); + + /** + * Convert current local orbital frame to CCSDS equivalent orbit relative frame when possible, null otherwise. + * + * @return CCSDS equivalent orbit relative frame when possible, null otherwise + * + * @see OrbitRelativeFrame + */ + public abstract OrbitRelativeFrame toOrbitRelativeFrame(); } diff --git a/src/main/java/org/orekit/frames/LazyLoadedEop.java b/src/main/java/org/orekit/frames/LazyLoadedEop.java index 51bedd0ef3..14850b6f3c 100644 --- a/src/main/java/org/orekit/frames/LazyLoadedEop.java +++ b/src/main/java/org/orekit/frames/LazyLoadedEop.java @@ -34,7 +34,7 @@ /** * Loads Earth Orientation Parameters (EOP) from a configured set of {@link - * EOPHistoryLoader}s on demand. Methods are synchronized so it is safe for access from + * EopHistoryLoader}s on demand. Methods are synchronized so it is safe for access from * multiple threads. * * @author Guylaine Prat @@ -50,20 +50,25 @@ public class LazyLoadedEop { /** Provides access to the EOP data files. */ private final DataProvidersManager dataProvidersManager; /** Loaders for Earth Orientation parameters. */ - private final Map> eopHistoryLoaders; + private final Map> eopHistoryLoaders; /** Threshold for EOP continuity. */ private double eopContinuityThreshold; + /** Degree for EOP interpolation. + * @since 12.0 + */ + private int interpolationDegree; /** * Create a new instance for loading EOP data from multiple {@link - * EOPHistoryLoader}s. + * EopHistoryLoader}s. * * @param dataProvidersManager provides access to the needed EOP data files. */ public LazyLoadedEop(final DataProvidersManager dataProvidersManager) { - this.dataProvidersManager = dataProvidersManager; - this.eopHistoryLoaders = new HashMap<>(); + this.dataProvidersManager = dataProvidersManager; + this.eopHistoryLoaders = new HashMap<>(); this.eopContinuityThreshold = 5 * Constants.JULIAN_DAY; + this.interpolationDegree = EOPHistory.DEFAULT_INTERPOLATION_DEGREE; } /** @@ -85,8 +90,8 @@ public DataProvidersManager getDataProvidersManager() { * @param rapidDataColumnsSupportedNames regular expression for supported rapid data * columns EOP files names (may be null if the * default IERS file names are used) - * @param rapidDataXMLSupportedNames regular expression for supported rapid data - * XML EOP files names (may be null if the + * @param xmlSupportedNames regular expression for supported XML EOP + * files names (may be null if the * default IERS file names are used) * @param eopC04SupportedNames regular expression for supported EOP C04 * files names (may be null if the default IERS @@ -97,18 +102,22 @@ public DataProvidersManager getDataProvidersManager() { * @param bulletinASupportedNames regular expression for supported bulletin A * files names (may be null if the default IERS * file names are used) + * @param csvSupportedNames regular expression for supported csv files names + * (may be null if the default IERS file names are used) * @param utcSupplier UTC time scale supplier. Value is not * accessed until attempting to load EOP. - * @see IERS EOP C04 files - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) + * @see IERS https data download + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) * @see #clearEOPHistoryLoaders() - * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String, Supplier) + * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String, String, Supplier) + * @since 12.0 */ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames, - final String rapidDataXMLSupportedNames, + final String xmlSupportedNames, final String eopC04SupportedNames, final String bulletinBSupportedNames, final String bulletinASupportedNames, + final String csvSupportedNames, final Supplier utcSupplier) { final String rapidColNames = (rapidDataColumnsSupportedNames == null) ? @@ -117,18 +126,16 @@ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupport addEOPHistoryLoader(IERSConventions.IERS_1996, new RapidDataAndPredictionColumnsLoader(false, rapidColNames, dataProvidersManager, utcSupplier)); - final String rapidXmlNames = - (rapidDataXMLSupportedNames == null) ? - FramesFactory.RAPID_DATA_PREDICTION_XML_1980_FILENAME : - rapidDataXMLSupportedNames; + final String xmlNames = (xmlSupportedNames == null) ? + FramesFactory.XML_1980_FILENAME : + xmlSupportedNames; addEOPHistoryLoader(IERSConventions.IERS_1996, - new RapidDataAndPredictionXMLLoader(rapidXmlNames, dataProvidersManager, - utcSupplier)); + new EopXmlLoader(xmlNames, dataProvidersManager, utcSupplier)); final String eopcNames = (eopC04SupportedNames == null) ? FramesFactory.EOPC04_1980_FILENAME : eopC04SupportedNames; addEOPHistoryLoader(IERSConventions.IERS_1996, - new EOPC04FilesLoader(eopcNames, dataProvidersManager, utcSupplier)); + new EopC04FilesLoader(eopcNames, dataProvidersManager, utcSupplier)); final String bulBNames = (bulletinBSupportedNames == null) ? FramesFactory.BULLETINB_1980_FILENAME : bulletinBSupportedNames; @@ -139,6 +146,10 @@ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupport FramesFactory.BULLETINA_FILENAME : bulletinASupportedNames; addEOPHistoryLoader(IERSConventions.IERS_1996, new BulletinAFilesLoader(bulANames, dataProvidersManager, utcSupplier)); + final String csvNames = (csvSupportedNames == null) ? + FramesFactory.CSV_FILENAME : csvSupportedNames; + addEOPHistoryLoader(IERSConventions.IERS_1996, + new EopCsvFilesLoader(csvNames, dataProvidersManager, utcSupplier)); } /** @@ -152,8 +163,8 @@ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupport * @param rapidDataColumnsSupportedNames regular expression for supported rapid data * columns EOP files names (may be null if the * default IERS file names are used) - * @param rapidDataXMLSupportedNames regular expression for supported rapid data - * XML EOP files names (may be null if the + * @param xmlSupportedNames regular expression for supported XML EOP + * files names (may be null if the * default IERS file names are used) * @param eopC04SupportedNames regular expression for supported EOP C04 * files names (may be null if the default IERS @@ -164,60 +175,62 @@ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupport * @param bulletinASupportedNames regular expression for supported bulletin A * files names (may be null if the default IERS * file names are used) + * @param csvSupportedNames regular expression for supported csv files names + * (may be null if the default IERS file names are used) * @param utcSupplier UTC time scale supplier. Value is not * accessed until attempting to load EOP. - * @see IERS EOP C04 files - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) + * @see IERS https data download + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) * @see #clearEOPHistoryLoaders() - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, Supplier) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String, Supplier) + * @since 12.0 */ public void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames, - final String rapidDataXMLSupportedNames, + final String xmlSupportedNames, final String eopC04SupportedNames, final String bulletinBSupportedNames, final String bulletinASupportedNames, + final String csvSupportedNames, final Supplier utcSupplier) { final String rapidColNames = (rapidDataColumnsSupportedNames == null) ? - FramesFactory.RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME : + FramesFactory.RAPID_DATA_PREDICTION_COLUMNS_2000_FILENAME : rapidDataColumnsSupportedNames; addEOPHistoryLoader(IERSConventions.IERS_2003, - new RapidDataAndPredictionColumnsLoader( - true, rapidColNames, dataProvidersManager, utcSupplier)); + new RapidDataAndPredictionColumnsLoader(true, rapidColNames, dataProvidersManager, utcSupplier)); addEOPHistoryLoader(IERSConventions.IERS_2010, - new RapidDataAndPredictionColumnsLoader( - true, rapidColNames, dataProvidersManager, utcSupplier)); - final String rapidXmlNames = - (rapidDataXMLSupportedNames == null) ? - FramesFactory.RAPID_DATA_PREDICITON_XML_2000_FILENAME : - rapidDataXMLSupportedNames; + new RapidDataAndPredictionColumnsLoader(true, rapidColNames, dataProvidersManager, utcSupplier)); + final String xmlNames = (xmlSupportedNames == null) ? + FramesFactory.XML_2000_FILENAME : + xmlSupportedNames; addEOPHistoryLoader(IERSConventions.IERS_2003, - new RapidDataAndPredictionXMLLoader( - rapidXmlNames, dataProvidersManager, utcSupplier)); + new EopXmlLoader(xmlNames, dataProvidersManager, utcSupplier)); addEOPHistoryLoader(IERSConventions.IERS_2010, - new RapidDataAndPredictionXMLLoader( - rapidXmlNames, dataProvidersManager, utcSupplier)); - final String eopcNames = - (eopC04SupportedNames == null) ? - FramesFactory.EOPC04_2000_FILENAME : eopC04SupportedNames; + new EopXmlLoader(xmlNames, dataProvidersManager, utcSupplier)); + final String eopcNames = (eopC04SupportedNames == null) ? + FramesFactory.EOPC04_2000_FILENAME : eopC04SupportedNames; addEOPHistoryLoader(IERSConventions.IERS_2003, - new EOPC04FilesLoader(eopcNames, dataProvidersManager, utcSupplier)); + new EopC04FilesLoader(eopcNames, dataProvidersManager, utcSupplier)); addEOPHistoryLoader(IERSConventions.IERS_2010, - new EOPC04FilesLoader(eopcNames, dataProvidersManager, utcSupplier)); - final String bulBNames = - (bulletinBSupportedNames == null) ? - FramesFactory.BULLETINB_2000_FILENAME : bulletinBSupportedNames; + new EopC04FilesLoader(eopcNames, dataProvidersManager, utcSupplier)); + final String bulBNames = (bulletinBSupportedNames == null) ? + FramesFactory.BULLETINB_2000_FILENAME : bulletinBSupportedNames; addEOPHistoryLoader(IERSConventions.IERS_2003, new BulletinBFilesLoader(bulBNames, dataProvidersManager, utcSupplier)); addEOPHistoryLoader(IERSConventions.IERS_2010, new BulletinBFilesLoader(bulBNames, dataProvidersManager, utcSupplier)); - final String bulANames = - (bulletinASupportedNames == null) ? - FramesFactory.BULLETINA_FILENAME : bulletinASupportedNames; + final String bulANames = (bulletinASupportedNames == null) ? + FramesFactory.BULLETINA_FILENAME : bulletinASupportedNames; addEOPHistoryLoader(IERSConventions.IERS_2003, - new BulletinAFilesLoader(bulANames, dataProvidersManager, utcSupplier)); + new BulletinAFilesLoader(bulANames, dataProvidersManager, utcSupplier)); addEOPHistoryLoader(IERSConventions.IERS_2010, - new BulletinAFilesLoader(bulANames, dataProvidersManager, utcSupplier)); + new BulletinAFilesLoader(bulANames, dataProvidersManager, utcSupplier)); + final String csvNames = (csvSupportedNames == null) ? + FramesFactory.CSV_FILENAME : csvSupportedNames; + addEOPHistoryLoader(IERSConventions.IERS_2003, + new EopCsvFilesLoader(csvNames, dataProvidersManager, utcSupplier)); + addEOPHistoryLoader(IERSConventions.IERS_2010, + new EopCsvFilesLoader(csvNames, dataProvidersManager, utcSupplier)); } /** @@ -225,10 +238,10 @@ public void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupport * * @param conventions IERS conventions to which EOP history applies * @param loader custom loader to add for the EOP history - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, Supplier) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String, Supplier) * @see #clearEOPHistoryLoaders() */ - public void addEOPHistoryLoader(final IERSConventions conventions, final EOPHistoryLoader loader) { + public void addEOPHistoryLoader(final IERSConventions conventions, final EopHistoryLoader loader) { synchronized (eopHistoryLoaders) { if (!eopHistoryLoaders.containsKey(conventions)) { eopHistoryLoaders.put(conventions, new ArrayList<>()); @@ -240,8 +253,8 @@ public void addEOPHistoryLoader(final IERSConventions conventions, final EOPHist /** * Clear loaders for Earth Orientation Parameters history. * - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, Supplier) + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String, Supplier) */ public void clearEOPHistoryLoaders() { synchronized (eopHistoryLoaders) { @@ -269,15 +282,28 @@ public void setEOPContinuityThreshold(final double threshold) { eopContinuityThreshold = threshold; } + /** + * Set the degree for interpolation degree. + *

        + * The default threshold (used if this method is never called) is {@link EOPHistory#DEFAULT_INTERPOLATION_DEGREE}. + *

        + * + * @param interpolationDegree interpolation degree, must be of the form 4k-1 + * @since 12.0 + */ + public void setInterpolationDegree(final int interpolationDegree) { + this.interpolationDegree = interpolationDegree; + } + /** * Get Earth Orientation Parameters history. *

        - * If no {@link EOPHistoryLoader} has been added by calling {@link - * #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) addEOPHistoryLoader} or if + * If no {@link EopHistoryLoader} has been added by calling {@link + * #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) addEOPHistoryLoader} or if * {@link #clearEOPHistoryLoaders() clearEOPHistoryLoaders} has been called * afterwards, the {@link #addDefaultEOP1980HistoryLoaders(String, String, String, - * String, String, Supplier)} and {@link #addDefaultEOP2000HistoryLoaders(String, - * String, String, String, String, Supplier)} methods will be called automatically + * String, String, String, Supplier)} and {@link #addDefaultEOP2000HistoryLoaders(String, + * String, String, String, String, String, Supplier)} methods will be called automatically * with supported file names parameters all set to null, in order to get the default * loaders configuration. *

        @@ -296,8 +322,8 @@ public EOPHistory getEOPHistory(final IERSConventions conventions, if (eopHistoryLoaders.isEmpty()) { // set up using default loaders final Supplier utcSupplier = timeScales::getUTC; - addDefaultEOP2000HistoryLoaders(null, null, null, null, null, utcSupplier); - addDefaultEOP1980HistoryLoaders(null, null, null, null, null, utcSupplier); + addDefaultEOP2000HistoryLoaders(null, null, null, null, null, null, utcSupplier); + addDefaultEOP1980HistoryLoaders(null, null, null, null, null, null, utcSupplier); } // TimeStamped based set needed to remove duplicates @@ -306,11 +332,10 @@ public EOPHistory getEOPHistory(final IERSConventions conventions, // try to load canonical data if available if (eopHistoryLoaders.containsKey(conventions)) { - for (final EOPHistoryLoader loader : eopHistoryLoaders.get(conventions)) { + for (final EopHistoryLoader loader : eopHistoryLoaders.get(conventions)) { try { - loader.fillHistory( - conventions.getNutationCorrectionConverter(timeScales), - data); + loader.fillHistory(conventions.getNutationCorrectionConverter(timeScales), + data); } catch (OrekitException oe) { pendingException = oe; } @@ -321,8 +346,7 @@ public EOPHistory getEOPHistory(final IERSConventions conventions, throw pendingException; } - final EOPHistory history = - new EOPHistory(conventions, data, simpleEOP, timeScales); + final EOPHistory history = new EOPHistory(conventions, interpolationDegree, data, simpleEOP, timeScales); history.checkEOPContinuity(eopContinuityThreshold); return history; diff --git a/src/main/java/org/orekit/frames/LazyLoadedFrames.java b/src/main/java/org/orekit/frames/LazyLoadedFrames.java index 648b67b812..db818a59e9 100644 --- a/src/main/java/org/orekit/frames/LazyLoadedFrames.java +++ b/src/main/java/org/orekit/frames/LazyLoadedFrames.java @@ -62,8 +62,7 @@ public LazyLoadedFrames(final LazyLoadedEop lazyLoadedEop, * @param rapidDataColumnsSupportedNames regular expression for supported * rapid data columns EOP files names * (may be null if the default IERS file names are used) - * @param rapidDataXMLSupportedNames regular expression for supported - * rapid data XML EOP files names + * @param xmlSupportedNames regular expression for supported XML EOP files names * (may be null if the default IERS file names are used) * @param eopC04SupportedNames regular expression for supported EOP C04 files names * (may be null if the default IERS file names are used) @@ -71,22 +70,27 @@ public LazyLoadedFrames(final LazyLoadedEop lazyLoadedEop, * (may be null if the default IERS file names are used) * @param bulletinASupportedNames regular expression for supported bulletin A files names * (may be null if the default IERS file names are used) - * @see IERS EOP C04 files - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) + * @param csvSupportedNames regular expression for supported csv files names + * (may be null if the default IERS file names are used) + * @see IERS https data download + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) * @see #clearEOPHistoryLoaders() - * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String) + * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String, String) + * @since 12.0 */ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames, - final String rapidDataXMLSupportedNames, - final String eopC04SupportedNames, - final String bulletinBSupportedNames, - final String bulletinASupportedNames) { + final String xmlSupportedNames, + final String eopC04SupportedNames, + final String bulletinBSupportedNames, + final String bulletinASupportedNames, + final String csvSupportedNames) { lazyLoadedEop.addDefaultEOP1980HistoryLoaders( rapidDataColumnsSupportedNames, - rapidDataXMLSupportedNames, + xmlSupportedNames, eopC04SupportedNames, bulletinBSupportedNames, bulletinASupportedNames, + csvSupportedNames, () -> getTimeScales().getUTC()); } @@ -99,8 +103,7 @@ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupport * @param rapidDataColumnsSupportedNames regular expression for supported * rapid data columns EOP files names * (may be null if the default IERS file names are used) - * @param rapidDataXMLSupportedNames regular expression for supported - * rapid data XML EOP files names + * @param xmlSupportedNames regular expression for supported XML EOP files names * (may be null if the default IERS file names are used) * @param eopC04SupportedNames regular expression for supported EOP C04 files names * (may be null if the default IERS file names are used) @@ -108,38 +111,43 @@ public void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupport * (may be null if the default IERS file names are used) * @param bulletinASupportedNames regular expression for supported bulletin A files names * (may be null if the default IERS file names are used) - * @see IERS EOP C04 files - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) + * @param csvSupportedNames regular expression for supported csv files names + * (may be null if the default IERS file names are used) + * @see IERS https data download + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) * @see #clearEOPHistoryLoaders() - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String) + * @since 12.0 */ public void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames, - final String rapidDataXMLSupportedNames, - final String eopC04SupportedNames, - final String bulletinBSupportedNames, - final String bulletinASupportedNames) { + final String xmlSupportedNames, + final String eopC04SupportedNames, + final String bulletinBSupportedNames, + final String bulletinASupportedNames, + final String csvSupportedNames) { lazyLoadedEop.addDefaultEOP2000HistoryLoaders( rapidDataColumnsSupportedNames, - rapidDataXMLSupportedNames, + xmlSupportedNames, eopC04SupportedNames, bulletinBSupportedNames, bulletinASupportedNames, + csvSupportedNames, () -> getTimeScales().getUTC()); } /** Add a loader for Earth Orientation Parameters history. * @param conventions IERS conventions to which EOP history applies * @param loader custom loader to add for the EOP history - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String) * @see #clearEOPHistoryLoaders() */ - public void addEOPHistoryLoader(final IERSConventions conventions, final EOPHistoryLoader loader) { + public void addEOPHistoryLoader(final IERSConventions conventions, final EopHistoryLoader loader) { lazyLoadedEop.addEOPHistoryLoader(conventions, loader); } /** Clear loaders for Earth Orientation Parameters history. - * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) - * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String) + * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) + * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String) */ public void clearEOPHistoryLoaders() { lazyLoadedEop.clearEOPHistoryLoaders(); @@ -167,12 +175,12 @@ public void setEOPContinuityThreshold(final double threshold) { /** {@inheritDoc} *

        - * If no {@link EOPHistoryLoader} has been added by calling {@link - * #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) addEOPHistoryLoader} + * If no {@link EopHistoryLoader} has been added by calling {@link + * #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) addEOPHistoryLoader} * or if {@link #clearEOPHistoryLoaders() clearEOPHistoryLoaders} has been * called afterwards, the {@link #addDefaultEOP1980HistoryLoaders(String, String, - * String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String, - * String, String, String, String)} methods will be called automatically with + * String, String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String, + * String, String, String, String, String)} methods will be called automatically with * supported file names parameters all set to null, in order to get the default * loaders configuration. *

        diff --git a/src/main/java/org/orekit/frames/LocalMagneticFieldFrame.java b/src/main/java/org/orekit/frames/LocalMagneticFieldFrame.java new file mode 100644 index 0000000000..26a0b04b44 --- /dev/null +++ b/src/main/java/org/orekit/frames/LocalMagneticFieldFrame.java @@ -0,0 +1,239 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.models.earth.GeoMagneticField; +import org.orekit.models.earth.ReferenceEllipsoid; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * This class handles a magnetic field variation attitude provider. + *

        + * It was designed to be used as a Bdot attitude pointing law which align a specific body axis with Earth magnetic field + * vector. + *

        + * Attitude control thought the magnetic field is called Bdot as it follows the sinusoidal variation of the Earth magnetic + * field vector, along the orbit. Magnetorquers are used on board to align the instrument, as so the satellite, with the + * planet magnetic field, producing a sinusoidal torque along the orbit. + * + * @author Alberto Ferrero + * @author Vincent Cucchietti + */ +public class LocalMagneticFieldFrame implements LOF { + + /** Inertial frame in which position-velocity coordinates will be given when computing transform and rotation. */ + private final Frame inertialFrame; + + /** Vector used to define the local orbital frame. */ + private final LOFBuilderVector lofBuilderVector; + + /** Body shape. */ + private final OneAxisEllipsoid wgs84BodyShape; + + /** Earth's magnetic field. */ + private final GeoMagneticField magneticField; + + /** + * Constructor with default definition of the local orbital frame: + *

          + *
        • x: Magnetic field
        • + *
        • y: Completes orthonormal frame
        • + *
        • z: Cross product of the magnetic field with the orbital momentum
        • + *
        . + * BEWARE : Do not use this constructor if it is planned to be used with an equatorial orbit as the magnetic field and + * orbital momentum vectors will be parallel and cause an error to be thrown + * + * @param inertialFrame inertial frame in which position-velocity coordinates will be given when computing transform and + * rotation + * @param magneticField Earth magnetic field model + * @param bodyFrame body frame related to body shape + */ + public LocalMagneticFieldFrame(final Frame inertialFrame, + final GeoMagneticField magneticField, + final Frame bodyFrame) { + this(inertialFrame, magneticField, LOFBuilderVector.PLUS_MOMENTUM, bodyFrame); + } + + /** + * Constructor with custom definition of the local orbital frame: + *
          + *
        • x: Magnetic field
        • + *
        • y: Completes orthonormal frame
        • + *
        • z: Cross product of the magnetic field with chosen {@link LOFBuilderVector vector}
        • + *
        + * For near-polar orbits, it is suggested to use the {@link LOFBuilderVector orbital momentum} to define the local + * orbital frame. However, for near-equatorial orbits, it is advised to use either the + * {@link LOFBuilderVector position or the velocity}. + * + * @param inertialFrame inertial frame in which position-velocity coordinates will be given when computing transform and + * rotation + * @param magneticField Earth magnetic field model + * @param lofBuilderVector vector used to define the local orbital frame + * @param bodyFrame body frame related to body shape + */ + public LocalMagneticFieldFrame(final Frame inertialFrame, final GeoMagneticField magneticField, + final LOFBuilderVector lofBuilderVector, + final Frame bodyFrame) { + this.inertialFrame = inertialFrame; + this.magneticField = magneticField; + this.lofBuilderVector = lofBuilderVector; + // Default WGS84 body shape as this is the one used by default in GeoMagneticField + this.wgs84BodyShape = ReferenceEllipsoid.getWgs84(bodyFrame); + } + + /** + * {@inheritDoc} Direction as X axis aligned with magnetic field vector, Y axis aligned with the cross product of the + * magnetic field vector with chosen {@link LOFBuilderVector vector type}. + *

        + * BEWARE: In this implementation, the method simply fieldify the normal rotation with given field. + * Hence all derivatives are lost. + */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldAbsoluteDate date, + final FieldPVCoordinates pv) { + // TODO Implement field equivalent of "calculateField" method in GeoMagneticField + final Rotation rotation = rotationFromInertial(date.toAbsoluteDate(), pv.toPVCoordinates()); + return new FieldRotation<>(field, rotation); + } + + /** + * {@inheritDoc} Direction as X axis aligned with magnetic field vector, Z axis aligned with the cross product of the + * magnetic field vector with chosen {@link LOFBuilderVector vector type}. + */ + @Override + public Rotation rotationFromInertial(final AbsoluteDate date, final PVCoordinates pv) { + // Express satellite coordinates in body frame + final StaticTransform inertialToBodyFrame = inertialFrame.getStaticTransformTo(wgs84BodyShape.getBodyFrame(), date); + final Vector3D posBody = inertialToBodyFrame.transformPosition(pv.getPosition()); + + // Compute satellite coordinates LLA and magnetic field vector in body frame + final double lat = posBody.getDelta(); + final double lng = posBody.getAlpha(); + final double alt = posBody.getNorm() - wgs84BodyShape.getEquatorialRadius(); + final Vector3D magnVectorBody = magneticField.calculateField(lat, lng, alt).getFieldVector(); + + // Compute magnetic field in inertial frame + final StaticTransform bodyToInertialFrame = inertialToBodyFrame.getInverse(); + final Vector3D magnVector = bodyToInertialFrame.transformVector(magnVectorBody); + + return new Rotation(magnVector, magnVector.crossProduct(lofBuilderVector.getVector(pv)), + Vector3D.PLUS_I, Vector3D.PLUS_K); + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return "LOCAL_MAGNETIC_FIELD_FRAME"; + } + + /** Get interlai frame. + * @return inertial frame + */ + public Frame getInertialFrame() { + return inertialFrame; + } + + /** Get geomagnetid field. + * @return geo magnetic field + */ + public GeoMagneticField getMagneticField() { + return magneticField; + } + + /** + * Enum defining how the +j axis of the local orbital frame will be defined. + *

        + * For example, if {@code MINUS_MOMENTUM} is chosen, +j aligned as the cross product of the momentum vector and the + * magnetic field vector. The resulting body frame will be +x aligned with magnetic field, +z aligned with negative + * momentum, +y orthonormal. + */ + public enum LOFBuilderVector { + + /** Positive position vector. */ + PLUS_POSITION { + /** {@inheritDoc} */ + @Override + Vector3D getVector(final PVCoordinates pv) { + return pv.getPosition(); + } + }, + + /** Positive velocity vector. */ + PLUS_VELOCITY { + /** {@inheritDoc} */ + @Override + Vector3D getVector(final PVCoordinates pv) { + return pv.getVelocity(); + } + }, + + /** Positive orbital momentum vector. */ + PLUS_MOMENTUM { + /** {@inheritDoc} */ + @Override + Vector3D getVector(final PVCoordinates pv) { + return pv.getMomentum(); + } + }, + + /** Negative position vector. */ + MINUS_POSITION { + /** {@inheritDoc} */ + @Override + Vector3D getVector(final PVCoordinates pv) { + return pv.getPosition().negate(); + } + }, + + /** Negative velocity vector. */ + MINUS_VELOCITY { + /** {@inheritDoc} */ + @Override + Vector3D getVector(final PVCoordinates pv) { + return pv.getVelocity().negate(); + } + }, + + /** Negative orbital momentum vector. */ + MINUS_MOMENTUM { + /** {@inheritDoc} */ + @Override + Vector3D getVector(final PVCoordinates pv) { + return pv.getMomentum().negate(); + } + }; + + /** + * @param pv position-velocity coordinates expressed in the instance inertial frame + * + * @return Vector used to define the local orbital frame by computing the cross product of the magnetic field with + * this + */ + abstract Vector3D getVector(PVCoordinates pv); + } + +} diff --git a/src/main/java/org/orekit/frames/LocalOrbitalFrame.java b/src/main/java/org/orekit/frames/LocalOrbitalFrame.java index 0eb7a6791a..eff8664423 100644 --- a/src/main/java/org/orekit/frames/LocalOrbitalFrame.java +++ b/src/main/java/org/orekit/frames/LocalOrbitalFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -46,16 +46,16 @@ public class LocalOrbitalFrame extends Frame { * propagations. * * @param parent parent frame (must be non-null) - * @param type frame type + * @param lof local orbital frame * @param provider provider used to compute frame motion. * @param name name of the frame * @exception IllegalArgumentException if the parent frame is null */ - public LocalOrbitalFrame(final Frame parent, final LOFType type, + public LocalOrbitalFrame(final Frame parent, final LOF lof, final PVCoordinatesProvider provider, final String name) throws IllegalArgumentException { - super(parent, new LocalProvider(type, provider, parent), name, false); + super(parent, new LocalProvider(lof, provider, parent), name, false); } /** Local provider for transforms. */ @@ -64,8 +64,8 @@ private static class LocalProvider implements TransformProvider { /** Serializable UID. */ private static final long serialVersionUID = 20170421L; - /** Frame type. */ - private final LOFType type; + /** Local orbital frame. */ + private final LOF lof; /** Provider used to compute frame motion. */ private final PVCoordinatesProvider provider; @@ -74,20 +74,20 @@ private static class LocalProvider implements TransformProvider { private final Frame reference; /** Simple constructor. - * @param type frame type + * @param lof local orbital frame * @param provider provider used to compute frame motion * @param reference reference frame */ - LocalProvider(final LOFType type, final PVCoordinatesProvider provider, + LocalProvider(final LOF lof, final PVCoordinatesProvider provider, final Frame reference) { - this.type = type; - this.provider = provider; - this.reference = reference; + this.lof = lof; + this.provider = provider; + this.reference = reference; } /** {@inheritDoc} */ public Transform getTransform(final AbsoluteDate date) { - return type.transformFromInertial(date, provider.getPVCoordinates(date, reference)); + return lof.transformFromInertial(date, provider.getPVCoordinates(date, reference)); } /** diff --git a/src/main/java/org/orekit/frames/MODProvider.java b/src/main/java/org/orekit/frames/MODProvider.java index d47d19449f..3f7a9cc061 100644 --- a/src/main/java/org/orekit/frames/MODProvider.java +++ b/src/main/java/org/orekit/frames/MODProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/OrphanFrame.java b/src/main/java/org/orekit/frames/OrphanFrame.java index 65b3ab06b7..b0d9c13ba7 100644 --- a/src/main/java/org/orekit/frames/OrphanFrame.java +++ b/src/main/java/org/orekit/frames/OrphanFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/PoleCorrection.java b/src/main/java/org/orekit/frames/PoleCorrection.java index 929dbaf224..d1bc2faaf9 100644 --- a/src/main/java/org/orekit/frames/PoleCorrection.java +++ b/src/main/java/org/orekit/frames/PoleCorrection.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/Predefined.java b/src/main/java/org/orekit/frames/Predefined.java index 8caab8dece..921f4e1245 100644 --- a/src/main/java/org/orekit/frames/Predefined.java +++ b/src/main/java/org/orekit/frames/Predefined.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -257,7 +257,7 @@ public enum Predefined { /** PZ-90.11 frame. */ PZ90_11("PZ90.11"); - /** Name fo the frame. */ + /** Name of the frame. */ private final String name; /** Simple constructor. diff --git a/src/main/java/org/orekit/frames/PredictedEOPHistory.java b/src/main/java/org/orekit/frames/PredictedEOPHistory.java new file mode 100644 index 0000000000..013d436d47 --- /dev/null +++ b/src/main/java/org/orekit/frames/PredictedEOPHistory.java @@ -0,0 +1,176 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitInternalError; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions.NutationCorrectionConverter; +import org.orekit.utils.SecularAndHarmonic; + +/** This class extends an {@link EOPHistory} for some weeks using fitting. + *

        + * The goal of this class is to provide a reasonable prediction of + * Earth Orientation Parameters past the last date available in + * regular {@link EOPHistory}, which just generated corrections set + * to 0 when they have no data. + *

        + *

        + * The prediction is based on fitting of last data, with both + * {@link SecularAndHarmonic secular (polynomial) and harmonic (periodic)} + * terms. The extended entries are generated at one point per day + * and are continuous (i.e. no leap seconds are introduced) + *

        + *

        + * After construction, the history contains both the initial + * raw history and an extension part appended after it. + *

        + * @see EOPFitter + * @see SecularAndHarmonic + * @since 12.0 + * @author Luc Maisonobe + */ +public class PredictedEOPHistory extends EOPHistory implements Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = 20230309L; + + /** Raw EOP history to extend. */ + private final EOPHistory rawHistory; + + /** Duration of the extension period (s). */ + private final double extensionDuration; + + /** Fitter for all Earth Orientation Parameters. */ + private final EOPFitter fitter; + + /** Simple constructor. + * @param rawHistory raw EOP history to extend. + * @param extensionDuration duration of the extension period (s) + * @param fitter fitter for all Earth Orientation Parameters + */ + public PredictedEOPHistory(final EOPHistory rawHistory, final double extensionDuration, + final EOPFitter fitter) { + super(rawHistory.getConventions(), rawHistory.getInterpolationDegree(), + extendHistory(rawHistory, extensionDuration, fitter), + rawHistory.isSimpleEop(), rawHistory.getTimeScales()); + this.rawHistory = rawHistory; + this.extensionDuration = extensionDuration; + this.fitter = fitter; + } + + /** Extends raw history. + * @param rawHistory raw EOP history to extend. + * @param extensionDuration duration of the extension period (s) + * @param fitter fitter for all Earth Orientation Parameters + * @return extended history + */ + private static Collection extendHistory(final EOPHistory rawHistory, + final double extensionDuration, + final EOPFitter fitter) { + + + // fit model + final EOPFittedModel model = fitter.fit(rawHistory); + + // create a converter for nutation corrections + final NutationCorrectionConverter converter = + rawHistory.getConventions().getNutationCorrectionConverter(rawHistory.getTimeScales()); + + // generate extension entries + final List rawEntries = rawHistory.getEntries(); + final EOPEntry last = rawEntries.get(rawEntries.size() - 1); + final int n = (int) FastMath.rint(extensionDuration / Constants.JULIAN_DAY); + final List entries = new ArrayList<>(rawEntries.size() + n); + entries.addAll(rawEntries); + for (int i = 0; i < n; ++i) { + final AbsoluteDate date = last.getDate().shiftedBy((i + 1) * Constants.JULIAN_DAY); + final double dut1 = model.getDUT1().osculatingValue(date); + final double lod = -Constants.JULIAN_DAY * model.getDUT1().osculatingDerivative(date); + final double xp = model.getXp().osculatingValue(date); + final double yp = model.getYp().osculatingValue(date); + final double xpRate = model.getXp().osculatingDerivative(date); + final double ypRate = model.getYp().osculatingDerivative(date); + final double dx = model.getDx().osculatingValue(date); + final double dy = model.getDy().osculatingValue(date); + final double[] equinox = converter.toEquinox(date, dx, dy); + entries.add(new EOPEntry(last.getMjd() + i + 1, dut1, lod, xp, yp, xpRate, ypRate, + equinox[0], equinox[1], dx, dy, + last.getITRFType(), date)); + } + + return entries; + + } + + /** Replace the instance with a data transfer object for serialization. + * @return data transfer object that will be serialized + */ + @DefaultDataContext + private Object writeReplace() { + return new DataTransferObject(rawHistory, extensionDuration, fitter); + } + + /** Internal class used only for serialization. */ + @DefaultDataContext + private static class DataTransferObject implements Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = 20230309L; + + /** Raw EOP history to extend. */ + private final EOPHistory rawHistory; + + /** Duration of the extension period (s). */ + private final double extensionDuration; + + /** Fitter for all Earth Orientation Parameters. */ + private final EOPFitter fitter; + + /** Simple constructor. + * @param rawHistory raw EOP history to extend. + * @param extensionDuration duration of the extension period (s) + * @param fitter fitter for all Earth Orientation Parameters + */ + DataTransferObject(final EOPHistory rawHistory, final double extensionDuration, final EOPFitter fitter) { + this.rawHistory = rawHistory; + this.extensionDuration = extensionDuration; + this.fitter = fitter; + } + + /** Replace the deserialized data transfer object with a {@link PredictedEOPHistory}. + * @return replacement {@link PredictedEOPHistory} + */ + private Object readResolve() { + try { + return new PredictedEOPHistory(rawHistory, extensionDuration, fitter); + } catch (OrekitException oe) { + throw new OrekitInternalError(oe); + } + } + + } + +} diff --git a/src/main/java/org/orekit/frames/RapidDataAndPredictionColumnsLoader.java b/src/main/java/org/orekit/frames/RapidDataAndPredictionColumnsLoader.java index 66149683e2..d89c11f2e1 100644 --- a/src/main/java/org/orekit/frames/RapidDataAndPredictionColumnsLoader.java +++ b/src/main/java/org/orekit/frames/RapidDataAndPredictionColumnsLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -64,7 +64,7 @@ * @see finals file format description at USNO */ class RapidDataAndPredictionColumnsLoader extends AbstractEopLoader - implements EOPHistoryLoader { + implements EopHistoryLoader { /** Field for year, month and day parsing. */ private static final String INTEGER2_FIELD = "((?:\\p{Blank}|\\p{Digit})\\p{Digit})"; @@ -313,7 +313,7 @@ public Collection parse(final InputStream input, final String name) final double lod; if (lodPartA.trim().length() == 0) { // lod part from bulletin A is blank - lod = 0; + lod = Double.NaN; } else { final Matcher lodAMatcher = LOD_PATTERN_A.matcher(lodPartA); if (lodAMatcher.matches()) { @@ -383,7 +383,8 @@ public Collection parse(final InputStream input, final String name) // get a configuration for current name and date range configuration = getItrfVersionProvider().getConfiguration(name, mjd); } - history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1], + history.add(new EOPEntry(mjd, dtu1, lod, x, y, Double.NaN, Double.NaN, + equinox[0], equinox[1], nro[0], nro[1], configuration.getVersion(), mjdDate)); } diff --git a/src/main/java/org/orekit/frames/ShiftingTransformProvider.java b/src/main/java/org/orekit/frames/ShiftingTransformProvider.java index 971eba9ab0..1a1de06d92 100644 --- a/src/main/java/org/orekit/frames/ShiftingTransformProvider.java +++ b/src/main/java/org/orekit/frames/ShiftingTransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -146,12 +146,12 @@ public > FieldTransform getTransform(final (GenericTimeStampedCache>) fieldCaches.get(date.getField()); if (fieldCache == null) { fieldCache = - new GenericTimeStampedCache>(cache.getNeighborsSize(), + new GenericTimeStampedCache>(cache.getMaxNeighborsSize(), cache.getMaxSlots(), cache.getMaxSpan(), cache.getNewSlotQuantumGap(), new FieldTransformGenerator<>(date.getField(), - cache.getNeighborsSize(), + cache.getMaxNeighborsSize(), interpolatingProvider, interpolatingProvider.getStep())); fieldCaches.put(date.getField(), fieldCache); @@ -165,6 +165,33 @@ public > FieldTransform getTransform(final return closest.shiftedBy(date.durationFrom(closest.getDate())); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + @SuppressWarnings("unchecked") + GenericTimeStampedCache> fieldCache = + (GenericTimeStampedCache>) fieldCaches.get(date.getField()); + if (fieldCache == null) { + fieldCache = + new GenericTimeStampedCache>(cache.getMaxNeighborsSize(), + cache.getMaxSlots(), + cache.getMaxSpan(), + cache.getNewSlotQuantumGap(), + new FieldTransformGenerator<>(date.getField(), + cache.getMaxNeighborsSize(), + interpolatingProvider, + interpolatingProvider.getStep())); + fieldCaches.put(date.getField(), fieldCache); + } + + // retrieve a sample from the thread-safe cache + final FieldTransform closest = fieldCache.getNeighbors(date.toAbsoluteDate()).reduce((t0, t1) -> + date.durationFrom(t0.getDate()).abs().getReal() < date.durationFrom(t1.getDate()).abs().getReal() ? + t0 : t1 + ).get(); + return closest.staticShiftedBy(date.durationFrom(closest.getDate())); + } + /** Replace the instance with a data transfer object for serialization. *

        * This intermediate class serializes only the data needed for generation, diff --git a/src/main/java/org/orekit/frames/SingleParameterFitter.java b/src/main/java/org/orekit/frames/SingleParameterFitter.java new file mode 100644 index 0000000000..4f0d938a37 --- /dev/null +++ b/src/main/java/org/orekit/frames/SingleParameterFitter.java @@ -0,0 +1,312 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames; + +import java.io.Serializable; +import java.util.List; +import java.util.ListIterator; +import java.util.function.ToDoubleFunction; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.SecularAndHarmonic; + +/** Fitter for one Earth Orientation Parameter. + * @see PredictedEOPHistory + * @see EOPFitter + * @see SecularAndHarmonic + * @since 12.0 + * @author Luc Maisonobe + */ +public class SingleParameterFitter implements Serializable { + + /** Sun pulsation, one year period. */ + public static final double SUN_PULSATION = MathUtils.TWO_PI / Constants.JULIAN_YEAR; + + /** Moon pulsation (one Moon draconic period). */ + public static final double MOON_DRACONIC_PULSATION = MathUtils.TWO_PI / (27.212221 * Constants.JULIAN_DAY); + + /** Serializable UID. */ + private static final long serialVersionUID = 20230309L; + + /** Duration of the fitting window at the end of the raw history (s). */ + private final double fittingDuration; + + /** Time constant of the exponential decay weight. */ + private final double timeConstant; + + /** Convergence on fitted parameter. */ + private final double convergence; + + /** Degree of the polynomial model. */ + private final int degree; + + /** Pulsations of harmonic part (rad/s). */ + private final double[] pulsations; + + /** Simple constructor. + * @param fittingDuration duration of the fitting window at the end of the raw history (s) + * @param timeConstant time constant \(\tau\) of the exponential decay weight, point weight is \(e^{\frac{t-t_0}{\tau}}\), + * i.e. points far in the past before \(t_0\) have smaller weights + * @param convergence convergence on fitted parameter + * @param degree degree of the polynomial model + * @param pulsations pulsations of harmonic part (rad/s) + * @see #createDefaultDut1FitterShortTermPrediction() + * @see #createDefaultDut1FitterLongTermPrediction() + * @see #createDefaultPoleFitterShortTermPrediction() + * @see #createDefaultPoleFitterLongTermPrediction() + * @see #createDefaultNutationFitterShortTermPrediction() + * @see #createDefaultNutationFitterLongTermPrediction() + * @see SecularAndHarmonic + */ + public SingleParameterFitter(final double fittingDuration, final double timeConstant, final double convergence, + final int degree, final double... pulsations) { + this.fittingDuration = fittingDuration; + this.timeConstant = timeConstant; + this.convergence = convergence; + this.degree = degree; + this.pulsations = pulsations.clone(); + } + + /** Perform secular and harmonic fitting. + * @param rawHistory EOP history to fit + * @param extractor extractor for Earth Orientation Parameter + * @return configured fitter + */ + public SecularAndHarmonic fit(final EOPHistory rawHistory, final ToDoubleFunction extractor) { + + final List rawEntries = rawHistory.getEntries(); + final EOPEntry last = rawEntries.get(rawEntries.size() - 1); + + // create fitter + final SecularAndHarmonic sh = new SecularAndHarmonic(degree, pulsations); + + // set up convergence + sh.setConvergenceRMS(convergence); + + // set up reference date and initial guess to a constant value + final double[] initialGuess = new double[degree + 1 + 2 * pulsations.length]; + initialGuess[0] = extractor.applyAsDouble(last); + sh.resetFitting(last.getDate(), initialGuess); + + // sample history + final AbsoluteDate fitStart = last.getDate().shiftedBy(-fittingDuration); + final ListIterator backwardIterator = rawEntries.listIterator(rawEntries.size()); + while (backwardIterator.hasPrevious()) { + final EOPEntry entry = backwardIterator.previous(); + if (entry.getDate().isAfterOrEqualTo(fitStart)) { + // the entry belongs to the fitting interval + sh.addWeightedPoint(entry.getDate(), extractor.applyAsDouble(entry), + FastMath.exp(entry.getDate().durationFrom(fitStart) / timeConstant)); + } else { + // we have processed all entries from the fitting interval + break; + } + } + + // perform fitting + sh.fit(); + + return sh; + + } + + /** Create fitter with default parameters adapted for fitting orientation parameters dUT1 and LOD + * for short term prediction. + *

        + * The main difference between these settings and {@link #createDefaultDut1FitterLongTermPrediction() + * the settings for long prediction} is the much smaller \(\tau\). This means more + * weight is set to the points at the end of the history, hence forcing the fitted prediction + * model to be closer to these points, hence the prediction error to be smaller just after + * raw history end. On the other hand, this implies that the model will diverge on long term. + * These settings are intended when prediction is used for at most 5 days after raw EOP end. + *

        + *
          + *
        • fitting duration set to one {@link Constants#JULIAN_YEAR year}
        • + *
        • time constant \(\tau\) of the exponential decay set to 6 {@link Constants#JULIAN_DAY days}
        • + *
        • convergence set to 10⁻¹² s
        • + *
        • polynomial part set to degree 3
        • + *
        • one harmonic term at {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        + * @return fitter with default configuration for orientation parameters dUT1 and LOD + * @see #createDefaultDut1FitterShortTermPrediction() + */ + public static SingleParameterFitter createDefaultDut1FitterShortTermPrediction() { + return new SingleParameterFitter(Constants.JULIAN_YEAR, 6 * Constants.JULIAN_DAY, 1.0e-12, 3, + SUN_PULSATION, 2 * SUN_PULSATION, 3 * SUN_PULSATION, + MOON_DRACONIC_PULSATION, 2 * MOON_DRACONIC_PULSATION, 3 * MOON_DRACONIC_PULSATION); + } + + /** Create fitter with default parameters adapted for fitting orientation parameters dUT1 and LOD + * for long term prediction. + *

        + * The main difference between these settings and {@link #createDefaultDut1FitterShortTermPrediction() + * the settings for short prediction} is the much larger \(\tau\). This means weight + * is spread throughout history, hence forcing the fitted prediction model to be remain very stable + * on the long term. On the other hand, this implies that the model will start with already a much + * larger error just after raw history end. + * These settings are intended when prediction is used for 5 days after raw EOP end or more. + *

        + *
          + *
        • fitting duration set to three {@link Constants#JULIAN_YEAR years}
        • + *
        • time constant \(\tau\) of the exponential decay set to 60 {@link Constants#JULIAN_DAY days}
        • + *
        • convergence set to 10⁻¹² s
        • + *
        • polynomial part set to degree 3
        • + *
        • one harmonic term at {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        + * @return fitter with default configuration for orientation parameters dUT1 and LOD + * @see #createDefaultDut1FitterShortTermPrediction() + */ + public static SingleParameterFitter createDefaultDut1FitterLongTermPrediction() { + return new SingleParameterFitter(3 * Constants.JULIAN_YEAR, 60 * Constants.JULIAN_DAY, 1.0e-12, 3, + SUN_PULSATION, 2 * SUN_PULSATION, 3 * SUN_PULSATION, + MOON_DRACONIC_PULSATION, 2 * MOON_DRACONIC_PULSATION, 3 * MOON_DRACONIC_PULSATION); + } + + /** Create fitter with default parameters adapted for fitting pole parameters Xp and Yp + * for long term prediction. + *

        + * The main difference between these settings and {@link #createDefaultPoleFitterLongTermPrediction() + * the settings for long prediction} is the much smaller \(\tau\). This means more + * weight is set to the points at the end of the history, hence forcing the fitted prediction + * model to be closer to these points, hence the prediction error to be smaller just after + * raw history end. On the other hand, this implies that the model will diverge on long term. + * These settings are intended when prediction is used for at most 5 days after raw EOP end. + *

        + *
          + *
        • fitting duration set to one {@link Constants#JULIAN_YEAR year}
        • + *
        • time constant \(\tau\) of the exponential decay set to 12 {@link Constants#JULIAN_DAY days}
        • + *
        • convergence set to 10⁻¹² rad
        • + *
        • polynomial part set to degree 3
        • + *
        • one harmonic term at {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        + * @return fitter with default configuration for pole parameters Xp and Yp + */ + public static SingleParameterFitter createDefaultPoleFitterShortTermPrediction() { + return new SingleParameterFitter(Constants.JULIAN_YEAR, 12 * Constants.JULIAN_DAY, 1.0e-12, 3, + SUN_PULSATION, 2 * SUN_PULSATION, 3 * SUN_PULSATION, + MOON_DRACONIC_PULSATION, 2 * MOON_DRACONIC_PULSATION, 3 * MOON_DRACONIC_PULSATION); + } + + /** Create fitter with default parameters adapted for fitting pole parameters Xp and Yp + * for long term prediction. + *

        + * The main difference between these settings and {@link #createDefaultPoleFitterShortTermPrediction() + * the settings for short prediction} is the much larger \(\tau\). This means weight + * is spread throughout history, hence forcing the fitted prediction model to be remain very stable + * on the long term. On the other hand, this implies that the model will start with already a much + * larger error just after raw history end. + * These settings are intended when prediction is used for 5 days after raw EOP end or more. + *

        + *
          + *
        • fitting duration set to three {@link Constants#JULIAN_YEAR years}
        • + *
        • time constant \(\tau\) of the exponential decay set to 60 {@link Constants#JULIAN_DAY days}
        • + *
        • convergence set to 10⁻¹² rad
        • + *
        • polynomial part set to degree 3
        • + *
        • one harmonic term at {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        + * @return fitter with default configuration for pole parameters Xp and Yp + */ + public static SingleParameterFitter createDefaultPoleFitterLongTermPrediction() { + return new SingleParameterFitter(3 * Constants.JULIAN_YEAR, 60 * Constants.JULIAN_DAY, 1.0e-12, 3, + SUN_PULSATION, 2 * SUN_PULSATION, 3 * SUN_PULSATION, + MOON_DRACONIC_PULSATION, 2 * MOON_DRACONIC_PULSATION, 3 * MOON_DRACONIC_PULSATION); + } + + /** Create fitter with default parameters adapted for fitting nutation parameters dx and dy + * for long term prediction. + *

        + * The main difference between these settings and {@link #createDefaultNutationFitterLongTermPrediction() + * the settings for long prediction} is the much smaller \(\tau\). This means more + * weight is set to the points at the end of the history, hence forcing the fitted prediction + * model to be closer to these points, hence the prediction error to be smaller just after + * raw history end. On the other hand, this implies that the model will diverge on long term. + * These settings are intended when prediction is used for at most 5 days after raw EOP end. + *

        + *
          + *
        • fitting duration set to one {@link Constants#JULIAN_YEAR year}
        • + *
        • time constant \(\tau\) of the exponential decay set to 12 {@link Constants#JULIAN_DAY days}
        • + *
        • convergence set to 10⁻¹² s
        • + *
        • polynomial part set to degree 3
        • + *
        • one harmonic term at {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        + * @return fitter with default configuration for pole nutation parameters dx and dy + */ + public static SingleParameterFitter createDefaultNutationFitterShortTermPrediction() { + return new SingleParameterFitter(Constants.JULIAN_YEAR, 12 * Constants.JULIAN_DAY, 1.0e-12, 3, + SUN_PULSATION, 2 * SUN_PULSATION, 3 * SUN_PULSATION, + MOON_DRACONIC_PULSATION, 2 * MOON_DRACONIC_PULSATION, 3 * MOON_DRACONIC_PULSATION); + } + + /** Create fitter with default parameters adapted for fitting nutation parameters dx and dy + * for long term prediction. + *

        + * The main difference between these settings and {@link #createDefaultNutationFitterShortTermPrediction() + * the settings for short prediction} is the much larger \(\tau\). This means weight + * is spread throughout history, hence forcing the fitted prediction model to be remain very stable + * on the long term. On the other hand, this implies that the model will start with already a much + * larger error just after raw history end. + * These settings are intended when prediction is used for 5 days after raw EOP end or more. + *

        + *
          + *
        • fitting duration set to three {@link Constants#JULIAN_YEAR years}
        • + *
        • time constant \(\tau\) of the exponential decay set to 60 {@link Constants#JULIAN_DAY days}
        • + *
        • convergence set to 10⁻¹² s
        • + *
        • polynomial part set to degree 3
        • + *
        • one harmonic term at {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #SUN_PULSATION}}
        • + *
        • one harmonic term at {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 2 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        • one harmonic term at 3 times {@link #MOON_DRACONIC_PULSATION}}
        • + *
        + * @return fitter with default configuration for pole nutation parameters dx and dy + */ + public static SingleParameterFitter createDefaultNutationFitterLongTermPrediction() { + return new SingleParameterFitter(3 * Constants.JULIAN_YEAR, 60 * Constants.JULIAN_DAY, 1.0e-12, 3, + SUN_PULSATION, 2 * SUN_PULSATION, 3 * SUN_PULSATION, + MOON_DRACONIC_PULSATION, 2 * MOON_DRACONIC_PULSATION, 3 * MOON_DRACONIC_PULSATION); + } + +} diff --git a/src/main/java/org/orekit/frames/TEMEProvider.java b/src/main/java/org/orekit/frames/TEMEProvider.java index 5f98d8e9f8..fa36bbc67e 100644 --- a/src/main/java/org/orekit/frames/TEMEProvider.java +++ b/src/main/java/org/orekit/frames/TEMEProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -104,7 +104,7 @@ public EOPHistory getEOPHistory() { /** {@inheritDoc} */ @Override public TEMEProvider getNonInterpolatingProvider() { - return new TEMEProvider(conventions, eopHistory.getNonInterpolatingEOPHistory(), + return new TEMEProvider(conventions, eopHistory.getEOPHistoryWithoutCachedTidalCorrection(), obliquityFunction, nutationFunction); } diff --git a/src/main/java/org/orekit/frames/TIRFProvider.java b/src/main/java/org/orekit/frames/TIRFProvider.java index f2ca1f9853..0f34061e55 100644 --- a/src/main/java/org/orekit/frames/TIRFProvider.java +++ b/src/main/java/org/orekit/frames/TIRFProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -79,7 +79,7 @@ public EOPHistory getEOPHistory() { /** {@inheritDoc} */ @Override public TIRFProvider getNonInterpolatingProvider() { - return new TIRFProvider(eopHistory.getNonInterpolatingEOPHistory(), ut1); + return new TIRFProvider(eopHistory.getEOPHistoryWithoutCachedTidalCorrection(), ut1); } /** {@inheritDoc} */ @@ -133,6 +133,19 @@ public > FieldTransform getTransform(final } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + // compute proper rotation + final T correctedERA = era.value(date); + // set up the transform from parent CIRF + final FieldRotation rotation = new FieldRotation<>( + FieldVector3D.getPlusK(date.getField()), + correctedERA, + RotationConvention.FRAME_TRANSFORM); + return FieldStaticTransform.of(date, rotation); + } + /** Get the Earth Rotation Angle at the current date. * @param date the date * @return Earth Rotation Angle at the current date in radians diff --git a/src/main/java/org/orekit/frames/TODProvider.java b/src/main/java/org/orekit/frames/TODProvider.java index e66ae6588a..93c825a784 100644 --- a/src/main/java/org/orekit/frames/TODProvider.java +++ b/src/main/java/org/orekit/frames/TODProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -101,7 +101,7 @@ public EOPHistory getEOPHistory() { /** {@inheritDoc} */ @Override public TODProvider getNonInterpolatingProvider() { - return new TODProvider(conventions, eopHistory.getNonInterpolatingEOPHistory(), + return new TODProvider(conventions, eopHistory.getEOPHistoryWithoutCachedTidalCorrection(), obliquityFunction, nutationFunction); } diff --git a/src/main/java/org/orekit/frames/TopocentricFrame.java b/src/main/java/org/orekit/frames/TopocentricFrame.java index 44f396affe..4722bd37e7 100644 --- a/src/main/java/org/orekit/frames/TopocentricFrame.java +++ b/src/main/java/org/orekit/frames/TopocentricFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -36,9 +36,11 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TrackingCoordinates; /** Topocentric frame. @@ -64,9 +66,14 @@ public class TopocentricFrame extends Frame implements PVCoordinatesProvider { /** Body shape on which the local point is defined. */ private final BodyShape parentShape; - /** Point where the topocentric frame is defined. */ + /** Geodetic point where the topocentric frame is defined. */ private final GeodeticPoint point; + /** Cartesian point where the topocentric frame is defined. + * @since 12.0 + */ + private final Vector3D cartesianPoint; + /** Simple constructor. * @param parentShape body shape on which the local point is defined * @param point local surface point where topocentric frame is defined @@ -76,16 +83,20 @@ public TopocentricFrame(final BodyShape parentShape, final GeodeticPoint point, final String name) { super(parentShape.getBodyFrame(), - new Transform(AbsoluteDate.ARBITRARY_EPOCH, - new Transform(AbsoluteDate.ARBITRARY_EPOCH, - parentShape.transform(point).negate()), - new Transform(AbsoluteDate.ARBITRARY_EPOCH, - new Rotation(point.getEast(), point.getZenith(), - Vector3D.PLUS_I, Vector3D.PLUS_K), - Vector3D.ZERO)), - name, false); - this.parentShape = parentShape; - this.point = point; + new Transform(AbsoluteDate.ARBITRARY_EPOCH, + new Transform(AbsoluteDate.ARBITRARY_EPOCH, + parentShape.transform(point).negate()), + new Transform(AbsoluteDate.ARBITRARY_EPOCH, + new Rotation(point.getEast(), point.getZenith(), + Vector3D.PLUS_I, Vector3D.PLUS_K), + Vector3D.ZERO)), + name, false); + this.parentShape = parentShape; + this.point = point; + this.cartesianPoint = getTransformProvider(). + getStaticTransform(AbsoluteDate.ARBITRARY_EPOCH). + getInverse(). + transformPosition(Vector3D.ZERO); } /** Get the body shape on which the local point is defined. @@ -103,7 +114,15 @@ public GeodeticPoint getPoint() { } /** Get the surface point defining the origin of the frame. - * @param tyoe of the elements + * @return surface point defining the origin of the frame in body frame + * @since 12.0 + */ + public Vector3D getCartesianPoint() { + return cartesianPoint; + } + + /** Get the surface point defining the origin of the frame. + * @param type of the elements * @param field of the elements * @return surface point defining the origin of the frame * @since 9.3 @@ -111,8 +130,8 @@ public GeodeticPoint getPoint() { public > FieldGeodeticPoint getPoint(final Field field) { final T zero = field.getZero(); return new FieldGeodeticPoint<>(zero.add(point.getLatitude()), - zero.add(point.getLongitude()), - zero.add(point.getAltitude())); + zero.add(point.getLongitude()), + zero.add(point.getAltitude())); } /** Get the zenith direction of topocentric frame, expressed in parent shape frame. @@ -133,7 +152,7 @@ public Vector3D getNadir() { return point.getNadir(); } - /** Get the north direction of topocentric frame, expressed in parent shape frame. + /** Get the north direction of topocentric frame, expressed in parent shape frame. *

        The north direction is defined in the horizontal plane * (normal to zenith direction) and following the local meridian.

        * @return unit vector in the north direction @@ -171,6 +190,46 @@ public Vector3D getWest() { return point.getWest(); } + /** Get the tracking coordinates of a point with regards to the local point. + * @param extPoint point for which elevation shall be computed + * @param frame frame in which the point is defined + * @param date computation date + * @return tracking coordinates of the point + * @since 12.0 + */ + public TrackingCoordinates getTrackingCoordinates(final Vector3D extPoint, final Frame frame, + final AbsoluteDate date) { + + // transform given point from given frame to topocentric frame + final Vector3D extPointTopo = transformPoint(extPoint, frame, date); + + final double azimuth = computeAzimuthFromTopoPoint(extPointTopo); + + return new TrackingCoordinates(azimuth, extPointTopo.getDelta(), extPointTopo.getNorm()); + + } + + /** Get the tracking coordinates of a point with regards to the local point. + * @param type of the field elements + * @param extPoint point for which elevation shall be computed + * @param frame frame in which the point is defined + * @param date computation date + * @return tracking coordinates of the point + * @since 12.0 + */ + public > FieldTrackingCoordinates getTrackingCoordinates(final FieldVector3D extPoint, + final Frame frame, + final FieldAbsoluteDate date) { + + // Transform given point from given frame to topocentric frame + final FieldVector3D extPointTopo = transformPoint(extPoint, frame, date); + + final T azimuth = computeAzimuthFromTopoPoint(extPointTopo); + + return new FieldTrackingCoordinates<>(azimuth, extPointTopo.getDelta(), extPointTopo.getNorm()); + + } + /** Get the elevation of a point with regards to the local point. *

        The elevation is the angle between the local horizontal and * the direction from local point to given point.

        @@ -183,10 +242,8 @@ public double getElevation(final Vector3D extPoint, final Frame frame, final AbsoluteDate date) { // Transform given point from given frame to topocentric frame - final StaticTransform t = frame.getStaticTransformTo(this, date); - final Vector3D extPointTopo = t.transformPosition(extPoint); + final Vector3D extPointTopo = transformPoint(extPoint, frame, date); - // Elevation angle is PI/2 - angle between zenith and given point direction return extPointTopo.getDelta(); } @@ -201,13 +258,11 @@ public double getElevation(final Vector3D extPoint, final Frame frame, * @since 9.3 */ public > T getElevation(final FieldVector3D extPoint, final Frame frame, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { // Transform given point from given frame to topocentric frame - final FieldTransform t = frame.getTransformTo(this, date); - final FieldVector3D extPointTopo = t.transformPosition(extPoint); + final FieldVector3D extPointTopo = transformPoint(extPoint, frame, date); - // Elevation angle is PI/2 - angle between zenith and given point direction return extPointTopo.getDelta(); } @@ -224,15 +279,9 @@ public double getAzimuth(final Vector3D extPoint, final Frame frame, final AbsoluteDate date) { // Transform given point from given frame to topocentric frame - final StaticTransform t = frame.getStaticTransformTo(this, date); - final Vector3D extPointTopo = t.transformPosition(extPoint); + final Vector3D extPointTopo = transformPoint(extPoint, frame, date); - // Compute azimuth - double azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY()); - if (azimuth < 0.) { - azimuth += MathUtils.TWO_PI; - } - return azimuth; + return computeAzimuthFromTopoPoint(extPointTopo); } @@ -248,18 +297,12 @@ public double getAzimuth(final Vector3D extPoint, final Frame frame, * @since 9.3 */ public > T getAzimuth(final FieldVector3D extPoint, final Frame frame, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { // Transform given point from given frame to topocentric frame - final FieldTransform t = getTransformTo(frame, date).getInverse(); - final FieldVector3D extPointTopo = t.transformPosition(extPoint); + final FieldVector3D extPointTopo = transformPoint(extPoint, frame, date); - // Compute azimuth - T azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY()); - if (azimuth.getReal() < 0.) { - azimuth = azimuth.add(MathUtils.TWO_PI); - } - return azimuth; + return computeAzimuthFromTopoPoint(extPointTopo); } @@ -273,10 +316,8 @@ public double getRange(final Vector3D extPoint, final Frame frame, final AbsoluteDate date) { // Transform given point from given frame to topocentric frame - final StaticTransform t = frame.getStaticTransformTo(this, date); - final Vector3D extPointTopo = t.transformPosition(extPoint); + final Vector3D extPointTopo = transformPoint(extPoint, frame, date); - // Compute range return extPointTopo.getNorm(); } @@ -290,13 +331,11 @@ public double getRange(final Vector3D extPoint, final Frame frame, * @since 9.3 */ public > T getRange(final FieldVector3D extPoint, final Frame frame, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { // Transform given point from given frame to topocentric frame - final FieldTransform t = frame.getTransformTo(this, date); - final FieldVector3D extPointTopo = t.transformPosition(extPoint); + final FieldVector3D extPointTopo = transformPoint(extPoint, frame, date); - // Compute range return extPointTopo.getNorm(); } @@ -316,7 +355,7 @@ public double getRangeRate(final PVCoordinates extPV, final Frame frame, // Compute range rate (doppler) : relative rate along the line of sight return Vector3D.dotProduct(extPVTopo.getPosition(), extPVTopo.getVelocity()) / - extPVTopo.getPosition().getNorm(); + extPVTopo.getPosition().getNorm(); } @@ -329,7 +368,7 @@ public double getRangeRate(final PVCoordinates extPV, final Frame frame, * @since 9.3 */ public > T getRangeRate(final FieldPVCoordinates extPV, final Frame frame, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { // Transform given point from given frame to topocentric frame final FieldTransform t = frame.getTransformTo(this, date); @@ -337,7 +376,7 @@ public > T getRangeRate(final FieldPVCoordinat // Compute range rate (doppler) : relative rate along the line of sight return FieldVector3D.dotProduct(extPVTopo.getPosition(), extPVTopo.getVelocity()).divide( - extPVTopo.getPosition().getNorm()); + extPVTopo.getPosition().getNorm()); } @@ -360,7 +399,7 @@ public GeodeticPoint computeLimitVisibilityPoint(final double radius, final double deltaP = 0.001; final UnivariateSolver solver = new BracketingNthOrderBrentSolver(deltaP / Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - deltaP, deltaP, 5); + deltaP, deltaP, 5); // find the distance such that a point in the specified direction and at the solved-for // distance is exactly at the specified radius @@ -391,11 +430,17 @@ public GeodeticPoint pointAtDistance(final double azimuth, final double elevatio final SinCos scAz = FastMath.sinCos(azimuth); final SinCos scEl = FastMath.sinCos(elevation); final Vector3D observed = new Vector3D(distance * scEl.cos() * scAz.sin(), - distance * scEl.cos() * scAz.cos(), - distance * scEl.sin()); + distance * scEl.cos() * scAz.cos(), + distance * scEl.sin()); return parentShape.transform(observed, this, AbsoluteDate.ARBITRARY_EPOCH); } + /** {@inheritDoc} */ + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + return getStaticTransformTo(frame, date).transformPosition(Vector3D.ZERO); + } + /** Get the {@link PVCoordinates} of the topocentric frame origin in the selected frame. * @param date current date * @param frame the frame where to define the position @@ -403,9 +448,61 @@ public GeodeticPoint pointAtDistance(final double azimuth, final double elevatio */ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { return getTransformTo(frame, date).transformPVCoordinates(new TimeStampedPVCoordinates(date, - Vector3D.ZERO, - Vector3D.ZERO, - Vector3D.ZERO)); + Vector3D.ZERO, + Vector3D.ZERO, + Vector3D.ZERO)); + } + + /** Transform point in topocentric frame. + * @param extPoint point + * @param date current date + * @param frame the frame where to define the position + * @return transformed point in topocentric frame + */ + private Vector3D transformPoint(final Vector3D extPoint, final Frame frame, final AbsoluteDate date) { + final StaticTransform t = frame.getStaticTransformTo(this, date); + return t.transformPosition(extPoint); + } + + /** Transform point in topocentric frame. + * @param type of the field elements + * @param extPoint point + * @param date current date + * @param frame the frame where to define the position + * @return transformed point in topocentric frame + */ + private > FieldVector3D transformPoint(final FieldVector3D extPoint, + final Frame frame, + final FieldAbsoluteDate date) { + final FieldStaticTransform t = frame.getStaticTransformTo(this, date); + return t.transformPosition(extPoint); + } + + /** Compute azimuth from topocentric point. + * @param extPointTopo topocentric point + * @return azimuth + */ + private double computeAzimuthFromTopoPoint(final Vector3D extPointTopo) { + final double azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY()); + if (azimuth < 0.0) { + return azimuth + MathUtils.TWO_PI; + } else { + return azimuth; + } + } + + /** Compute azimuth from topocentric point. + * @param type of the field elements + * @param extPointTopo topocentric point + * @return azimuth + */ + private > T computeAzimuthFromTopoPoint(final FieldVector3D extPointTopo) { + final T azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY()); + if (azimuth.getReal() < 0.0) { + return azimuth.add(MathUtils.TWO_PI); + } else { + return azimuth; + } } } diff --git a/src/main/java/org/orekit/frames/Transform.java b/src/main/java/org/orekit/frames/Transform.java index 28512c4444..f4c6b8655d 100644 --- a/src/main/java/org/orekit/frames/Transform.java +++ b/src/main/java/org/orekit/frames/Transform.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,7 +29,7 @@ import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeInterpolable; +import org.orekit.time.TimeInterpolator; import org.orekit.time.TimeShiftable; import org.orekit.utils.AngularCoordinates; import org.orekit.utils.AngularDerivativesFilter; @@ -37,8 +37,10 @@ import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; /** Transformation class in three dimensional space. @@ -97,7 +99,6 @@ * @author Fabien Maussion */ public class Transform implements - TimeInterpolable, TimeShiftable, Serializable, StaticTransform { @@ -122,8 +123,7 @@ public class Transform implements * @param cartesian Cartesian coordinates of the target frame with respect to the original frame * @param angular angular coordinates of the target frame with respect to the original frame */ - private Transform(final AbsoluteDate date, - final PVCoordinates cartesian, final AngularCoordinates angular) { + public Transform(final AbsoluteDate date, final PVCoordinates cartesian, final AngularCoordinates angular) { this.date = date; this.cartesian = cartesian; this.angular = angular; @@ -361,7 +361,19 @@ public StaticTransform staticShiftedBy(final double dt) { angular.rotationShiftedBy(dt)); } - /** {@inheritDoc} + /** + * Create a so-called static transform from the instance. + * + * @return static part of the transform. It is static in the + * sense that it can only be used to transform directions and positions, but + * not velocities or accelerations. + * @see StaticTransform + */ + public StaticTransform toStaticTransform() { + return StaticTransform.of(date, cartesian.getPosition(), angular.getRotation()); + } + + /** Interpolate a transform from a sample set of existing transforms. *

        * Calling this method is equivalent to call {@link #interpolate(AbsoluteDate, * CartesianDerivativesFilter, AngularDerivativesFilter, Collection)} with {@code cFilter} @@ -369,6 +381,9 @@ public StaticTransform staticShiftedBy(final double dt) { * {@link AngularDerivativesFilter#USE_RRA} * set to true. *

        + * @param interpolationDate interpolation date + * @param sample sample points on which interpolation should be done + * @return a new instance, interpolated at specified date */ public Transform interpolate(final AbsoluteDate interpolationDate, final Stream sample) { return interpolate(interpolationDate, @@ -398,20 +413,31 @@ public Transform interpolate(final AbsoluteDate interpolationDate, final Stream< * @param aFilter filter for derivatives from the sample to use in interpolation * @param sample sample points on which interpolation should be done * @return a new instance, interpolated at specified date - * @since 7.0 + * @since 7.0 */ public static Transform interpolate(final AbsoluteDate date, final CartesianDerivativesFilter cFilter, final AngularDerivativesFilter aFilter, final Collection sample) { + + // Create samples final List datedPV = new ArrayList<>(sample.size()); final List datedAC = new ArrayList<>(sample.size()); for (final Transform t : sample) { datedPV.add(new TimeStampedPVCoordinates(t.getDate(), t.getTranslation(), t.getVelocity(), t.getAcceleration())); datedAC.add(new TimeStampedAngularCoordinates(t.getDate(), t.getRotation(), t.getRotationRate(), t.getRotationAcceleration())); } - final TimeStampedPVCoordinates interpolatedPV = TimeStampedPVCoordinates.interpolate(date, cFilter, datedPV); - final TimeStampedAngularCoordinates interpolatedAC = TimeStampedAngularCoordinates.interpolate(date, aFilter, datedAC); + + // Create interpolators + final TimeInterpolator pvInterpolator = + new TimeStampedPVCoordinatesHermiteInterpolator(datedPV.size(), cFilter); + + final TimeInterpolator angularInterpolator = + new TimeStampedAngularCoordinatesHermiteInterpolator(datedPV.size(), aFilter); + + // Interpolate + final TimeStampedPVCoordinates interpolatedPV = pvInterpolator.interpolate(date, datedPV); + final TimeStampedAngularCoordinates interpolatedAC = angularInterpolator.interpolate(date, datedAC); return new Transform(date, interpolatedPV, interpolatedAC); } diff --git a/src/main/java/org/orekit/frames/TransformGenerator.java b/src/main/java/org/orekit/frames/TransformGenerator.java index 1b418c2b23..1a21060d2b 100644 --- a/src/main/java/org/orekit/frames/TransformGenerator.java +++ b/src/main/java/org/orekit/frames/TransformGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/TransformProvider.java b/src/main/java/org/orekit/frames/TransformProvider.java index 95dbc8c1e5..eecc9cbb16 100644 --- a/src/main/java/org/orekit/frames/TransformProvider.java +++ b/src/main/java/org/orekit/frames/TransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -41,7 +41,7 @@ public interface TransformProvider extends Serializable { * @param date current date * @param type of the field elements * @return transform at specified date - * @since 9.0 + * @since 9.0 */ > FieldTransform getTransform(FieldAbsoluteDate date); @@ -55,7 +55,22 @@ public interface TransformProvider extends Serializable { * @return the static transform. */ default StaticTransform getStaticTransform(AbsoluteDate date) { - return getTransform(date); + return getTransform(date).toStaticTransform(); + } + + /** + * Get a transform for only rotations and translations on the specified date. + * + *

        The default implementation returns {@link #getTransform(AbsoluteDate)} + * but implementations may override it for better performance. + * + * @param type of the elements + * @param date current date. + * @return the static transform. + * @since 12.0 + */ + default > FieldStaticTransform getStaticTransform(FieldAbsoluteDate date) { + return getTransform(date).toStaticTransform(); } } diff --git a/src/main/java/org/orekit/frames/TransformProviderUtils.java b/src/main/java/org/orekit/frames/TransformProviderUtils.java index 32d69ef6c1..53e2f62ef4 100644 --- a/src/main/java/org/orekit/frames/TransformProviderUtils.java +++ b/src/main/java/org/orekit/frames/TransformProviderUtils.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -95,6 +95,12 @@ public > FieldTransform getTransform(final return provider.getTransform(date).getInverse(); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + return provider.getStaticTransform(date).getInverse(); + } + }; } @@ -132,6 +138,16 @@ public > FieldTransform getTransform(final return new FieldTransform<>(date, first.getTransform(date), second.getTransform(date)); } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + return FieldStaticTransform.compose( + date, + first.getStaticTransform(date), + second.getStaticTransform(date) + ); + } + }; } diff --git a/src/main/java/org/orekit/frames/TwoBodiesBaryFrame.java b/src/main/java/org/orekit/frames/TwoBodiesBaryFrame.java index 930a2c129c..67016db880 100644 --- a/src/main/java/org/orekit/frames/TwoBodiesBaryFrame.java +++ b/src/main/java/org/orekit/frames/TwoBodiesBaryFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/TwoBodiesBaryTransformProvider.java b/src/main/java/org/orekit/frames/TwoBodiesBaryTransformProvider.java index fc31a51e58..ec619098a0 100644 --- a/src/main/java/org/orekit/frames/TwoBodiesBaryTransformProvider.java +++ b/src/main/java/org/orekit/frames/TwoBodiesBaryTransformProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/UpdatableFrame.java b/src/main/java/org/orekit/frames/UpdatableFrame.java index d60783874a..971eacb792 100644 --- a/src/main/java/org/orekit/frames/UpdatableFrame.java +++ b/src/main/java/org/orekit/frames/UpdatableFrame.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/VEISProvider.java b/src/main/java/org/orekit/frames/VEISProvider.java index 18cfa3b723..7d192d00b5 100644 --- a/src/main/java/org/orekit/frames/VEISProvider.java +++ b/src/main/java/org/orekit/frames/VEISProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/VersionedITRF.java b/src/main/java/org/orekit/frames/VersionedITRF.java index fa52332350..0200efee1f 100644 --- a/src/main/java/org/orekit/frames/VersionedITRF.java +++ b/src/main/java/org/orekit/frames/VersionedITRF.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/frames/VersionedITRFProvider.java b/src/main/java/org/orekit/frames/VersionedITRFProvider.java index 62785750be..b2394d4d1e 100644 --- a/src/main/java/org/orekit/frames/VersionedITRFProvider.java +++ b/src/main/java/org/orekit/frames/VersionedITRFProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -139,6 +139,26 @@ public > FieldTransform getTransform(final } + /** {@inheritDoc} */ + @Override + public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + + // get the transform from the current EOP + final FieldStaticTransform rawTransform = rawProvider.getStaticTransform(date); + + // add the conversion layer + final ITRFVersion.Converter converterForDate = getConverter(date.toAbsoluteDate()); + if (converterForDate == null) { + return rawTransform; + } else { + return FieldStaticTransform.compose( + date, + rawTransform, + converterForDate.getStaticTransform(date)); + } + + } + /** Get a converter for the date. * @param date date to check * @return converter that should be applied for this date, or null diff --git a/src/main/java/org/orekit/frames/encounter/AbstractEncounterLOF.java b/src/main/java/org/orekit/frames/encounter/AbstractEncounterLOF.java new file mode 100644 index 0000000000..7b623541be --- /dev/null +++ b/src/main/java/org/orekit/frames/encounter/AbstractEncounterLOF.java @@ -0,0 +1,124 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames.encounter; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * Abstract class for encounter frame between two objects. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public abstract class AbstractEncounterLOF implements EncounterLOF { + + /** + * Other position and velocity of the encounter frame. Can be null. + *

        + * BEWARE: This is not the origin of the encounter local orbital frame ! + */ + private PVCoordinates other; + + /** + * Other position and velocity of the encounter frame. Can be null. + *

        + * BEWARE: This is not the origin of the encounter local orbital frame ! + */ + private FieldPVCoordinates fieldOther; + + /** + * Constructor with {@link PVCoordinates}. + * + * @param other other object to create the encounter local orbital frame with (not the origin of the frame !) + */ + protected AbstractEncounterLOF(final PVCoordinates other) { + this.other = other; + } + + /** + * Constructor with {@link FieldPVCoordinates}. + * + * @param other other object to create the encounter frame with (not the origin of the frame !) + * @param type of the field elements + */ + protected > AbstractEncounterLOF(final FieldPVCoordinates other) { + this.fieldOther = other; + } + + /** + * Get the rotation from inertial to this encounter local orbital frame. + *

        + * BEWARE: The given origin's position and velocity coordinates must be given in the frame in which this instance + * has been expressed in. + * + * @param field field to which the elements belong + * @param origin position-velocity of the origin in the same inertial frame as the one this instance has been expressed + * in. + * @param type of the field elements + * + * @return rotation from inertial to this encounter local orbital frame + */ + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates origin) { + return rotationFromInertial(field, origin, getFieldOther(field)); + } + + /** + * Get the rotation from inertial to this encounter local orbital frame. + *

        + * BEWARE: The given origin's position and velocity coordinates must be given in the frame in which this instance + * has been expressed in. + * + * @param origin position-velocity of the origin in some inertial frame + * + * @return rotation from inertial to this encounter local orbital frame + */ + public Rotation rotationFromInertial(final PVCoordinates origin) { + return rotationFromInertial(origin, getOther()); + } + + /** + * Get the field version of other's position and velocity coordinates. If the instance has been created with normal + * {@link PVCoordinates}, then it will build its field equivalent. + * + * @param field field of the elements + * @param type of the field elements + * + * @return field version of other's position and velocity coordinates + */ + @SuppressWarnings("unchecked") + @Override + public > FieldPVCoordinates getFieldOther(final Field field) { + return fieldOther == null ? new FieldPVCoordinates<>(field, other) : (FieldPVCoordinates) fieldOther; + } + + /** + * Get the normal version of other's position and velocity coordinates. If the instance has been created with field + * {@link FieldPVCoordinates}, then it will convert it to its {@link PVCoordinates} equivalent. + * + * @return normal version of other's position and velocity coordinates + */ + @Override + public PVCoordinates getOther() { + return other == null ? fieldOther.toPVCoordinates() : other; + } +} diff --git a/src/main/java/org/orekit/frames/encounter/DefaultEncounterLOF.java b/src/main/java/org/orekit/frames/encounter/DefaultEncounterLOF.java new file mode 100644 index 0000000000..172798875f --- /dev/null +++ b/src/main/java/org/orekit/frames/encounter/DefaultEncounterLOF.java @@ -0,0 +1,112 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames.encounter; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * Default encounter local orbital frame. + *

        + * Note that it is up to the user to choose which object should be at the origin. + *

        + * It is defined as follows : + *

          + *
        • z axis : Normalized relative velocity vector.
        • + *
        • y axis : Normalized cross product between z axis and other relative to origin position.
        • + *
        • x axis : Completes the right handed coordinate system.
        • + *
        + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class DefaultEncounterLOF extends AbstractEncounterLOF { + + /** + * Constructor. + * + * @param other other object to create the encounter frame with (not the origin of the frame !) + */ + public DefaultEncounterLOF(final PVCoordinates other) { + super(other); + } + + /** + * Field constructor. + * + * @param other other object to create the encounter frame with (not the origin of the frame !) + * @param type of the field elements + */ + public > DefaultEncounterLOF(final FieldPVCoordinates other) { + super(other); + } + + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates origin, final PVCoordinates other) { + final Vector3D zAxis = other.getVelocity().subtract(origin.getVelocity()).normalize(); + final Vector3D yAxis = zAxis.crossProduct(other.getPosition().subtract(origin.getPosition())); + + return new Rotation(yAxis, zAxis, Vector3D.PLUS_J, Vector3D.PLUS_K); + } + + /** + * {@inheritDoc} + *

        + * In this case, return (0,0,1); + */ + @Override + public > FieldVector3D getAxisNormalToCollisionPlane(final Field field) { + return FieldVector3D.getPlusK(field); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates origin, + final FieldPVCoordinates other) { + final FieldVector3D otherVelocity = other.getVelocity(); + final FieldVector3D otherPosition = other.getPosition(); + + final FieldVector3D zAxis = otherVelocity.subtract(origin.getVelocity()).normalize(); + final FieldVector3D yAxis = zAxis.crossProduct(otherPosition.subtract(origin.getPosition())); + + return new FieldRotation<>(yAxis, zAxis, FieldVector3D.getPlusJ(field), FieldVector3D.getPlusK(field)); + } + + /** + * {@inheritDoc} + *

        + * In this case, return (0,0,1); + */ + @Override + public Vector3D getAxisNormalToCollisionPlane() { + return Vector3D.PLUS_K; + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return "DEFAULT_ENCOUNTER_LOF"; + } +} diff --git a/src/main/java/org/orekit/frames/encounter/EncounterLOF.java b/src/main/java/org/orekit/frames/encounter/EncounterLOF.java new file mode 100644 index 0000000000..b4a89ea405 --- /dev/null +++ b/src/main/java/org/orekit/frames/encounter/EncounterLOF.java @@ -0,0 +1,280 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames.encounter; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.twod.FieldVector2D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.hipparchus.linear.Array2DRowFieldMatrix; +import org.hipparchus.linear.Array2DRowRealMatrix; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.util.MathArrays; +import org.orekit.frames.LOF; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Interface for encounter local orbital frame. + *

        + * Encounter local orbital frame are defined using two objects, one of them is placed at the origin and the other is + * expressed relatively to the origin. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public interface EncounterLOF extends LOF { + + /** + * {@inheritDoc} It is unnecessary to use this method when dealing with {@link EncounterLOF}, use + * {@link #rotationFromInertial(Field, FieldPVCoordinates)} instead. + */ + @Override + default > FieldRotation rotationFromInertial(Field field, + FieldAbsoluteDate date, + FieldPVCoordinates pv) { + return rotationFromInertial(field, pv); + } + + /** + * {@inheritDoc} It is unnecessary to use this method when dealing with {@link EncounterLOF}, use + * {@link #rotationFromInertial(PVCoordinates)} instead. + */ + @Override + default Rotation rotationFromInertial(AbsoluteDate date, PVCoordinates pv) { + return rotationFromInertial(pv); + } + + /** + * Get the rotation from inertial to this encounter local orbital frame. + *

        + * BEWARE: The given origin's position and velocity coordinates must be given in the frame in which this instance + * has been expressed in. + * + * @param field field to which the elements belong + * @param origin position-velocity of the origin in the same inertial frame as the one this instance has been expressed + * in. + * @param type of the field elements + * + * @return rotation from inertial to this encounter local orbital frame + */ + default > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates origin) { + return rotationFromInertial(field, origin, getFieldOther(field)); + } + + /** + * Get the rotation from inertial to this encounter local orbital frame. + *

        + * BEWARE: The given origin's position and velocity coordinates must be given in the frame in which this instance + * has been expressed in. + * + * @param origin position-velocity of the origin in some inertial frame + * + * @return rotation from inertial to this encounter local orbital frame + */ + default Rotation rotationFromInertial(final PVCoordinates origin) { + return rotationFromInertial(origin, getOther()); + } + + /** + * Get the rotation from inertial to this encounter local orbital frame. + *

        + * BEWARE: The given origin's position and velocity coordinates must be given in the frame in which this instance + * has been expressed in. + * + * @param field field to which the elements belong + * @param origin position-velocity of the origin in the same inertial frame as other + * @param other position-velocity of the other in the same inertial frame as origin + * @param type of the field elements + * + * @return rotation from inertial to this encounter local orbital frame + */ + > FieldRotation rotationFromInertial(Field field, + FieldPVCoordinates origin, + FieldPVCoordinates other); + + /** + * Get the rotation from inertial to this encounter local orbital frame. + *

        + * BEWARE: The given origin's position and velocity coordinates must be given in the frame in which this instance + * has been expressed in. + * + * @param origin position-velocity of the origin in the same inertial frame as other + * @param other position-velocity of the other instance in the same inertial frame as origin + * + * @return rotation from inertial to this encounter local orbital frame + */ + Rotation rotationFromInertial(PVCoordinates origin, PVCoordinates other); + + /** + * Project given {@link RealMatrix matrix} expressed in this encounter local orbital frame onto the collision plane. + * + * @param matrix matrix to project, a 3 by 3 matrix is expected + * + * @return projected matrix onto the collision plane defined by this encounter local orbital frame + */ + default RealMatrix projectOntoCollisionPlane(RealMatrix matrix) { + final RealMatrix projectionMatrix = computeProjectionMatrix(); + return projectionMatrix.multiply(matrix.multiplyTransposed(projectionMatrix)); + } + + /** + * Get the 2x3 projection matrix that projects values expressed in this encounter local orbital frame to the collision + * plane. + * + * @return 2x3 projection matrix + */ + default RealMatrix computeProjectionMatrix() { + // Remove axis normal to collision plane from the identity matrix + final RealMatrix identity = MatrixUtils.createRealIdentityMatrix(3); + final List projectionMatrixDataList = Arrays.stream(identity.getData()) + .filter(values -> !Arrays.equals(values, + getAxisNormalToCollisionPlane().toArray())) + .collect(Collectors.toList()); + + // Map list to double[][] + final double[][] projectionMatrixData = new double[2][3]; + for (int i = 0; i < 2; i++) { + projectionMatrixData[i] = projectionMatrixDataList.get(i); + } + + return new Array2DRowRealMatrix(projectionMatrixData); + } + + /** + * Project given {@link RealMatrix matrix} expressed in this encounter local orbital frame onto the collision plane + * defined by this same encounter local orbital frame. + * + * @param matrix matrix to project, a 3 by 3 matrix is expected + * @param type of the field elements + * + * @return projected matrix onto the collision plane defined by this encounter local orbital frame + */ + default > FieldMatrix projectOntoCollisionPlane(FieldMatrix matrix) { + final FieldMatrix projectionMatrix = computeProjectionMatrix(matrix.getField()); + return projectionMatrix.multiply(matrix.multiplyTransposed(projectionMatrix)); + } + + /** + * Get the 2x3 projection matrix that projects values expressed in this encounter local orbital frame to the collision + * plane defined by this same encounter local orbital frame. + * + * @param field field to which the elements belong + * @param type of the field elements + * + * @return 2x3 projection matrix + */ + default > FieldMatrix computeProjectionMatrix(Field field) { + // Remove axis normal to collision plane from the identity matrix + final FieldMatrix identity = MatrixUtils.createFieldIdentityMatrix(field, 3); + final List projectionMatrixDataList = Arrays.stream(identity.getData()) + .filter(values -> !Arrays.equals(values, + getAxisNormalToCollisionPlane( + field).toArray())) + .collect(Collectors.toList()); + + // Map list to C[][] + final T[][] projectionMatrixData = MathArrays.buildArray(field, 2, 3); + for (int i = 0; i < 2; i++) { + projectionMatrixData[i] = projectionMatrixDataList.get(i); + } + + return new Array2DRowFieldMatrix<>(projectionMatrixData); + } + + /** + * Get the axis normal to the collision plane (i, j or k) in this encounter local orbital frame. + * + * @param field field of the elements + * @param type of the field elements + * + * @return axis normal to the collision plane (i, j or k) in this encounter local orbital frame + */ + > FieldVector3D getAxisNormalToCollisionPlane(Field field); + + /** + * Project given {@link Vector3D vector} expressed in this encounter local orbital frame onto the collision plane. + * + * @param vector vector to project + * + * @return projected vector onto the collision plane defined by this encounter local orbital frame + */ + default Vector2D projectOntoCollisionPlane(Vector3D vector) { + final RealMatrix projectionMatrix = computeProjectionMatrix(); + final RealMatrix vectorInMatrix = new Array2DRowRealMatrix(vector.toArray()); + + return new Vector2D(projectionMatrix.multiply(vectorInMatrix).getColumn(0)); + } + + /** + * Project given {@link Vector3D vector} expressed in this encounter local orbital frame onto the collision plane. + * + * @param vector vector to project + * @param type of the field elements + * + * @return projected vector onto the collision plane defined by this encounter local orbital frame + */ + default > FieldVector2D projectOntoCollisionPlane( + final FieldVector3D vector) { + final FieldMatrix projectionMatrix = computeProjectionMatrix(vector.getX().getField()); + final FieldMatrix vectorInMatrix = new Array2DRowFieldMatrix<>(vector.toArray()); + + return new FieldVector2D<>(projectionMatrix.multiply(vectorInMatrix).getColumn(0)); + } + + /** Get other's position and velocity coordinates. + * @param field field of the element + * @param type of the element + * + * @return other's position and velocity coordinates + */ + > FieldPVCoordinates getFieldOther(Field field); + + /** + * Get the axis normal to the collision plane (i, j or k) in this encounter local orbital frame. + * + * @return axis normal to the collision plane (i, j or k) in this encounter local orbital frame + */ + Vector3D getAxisNormalToCollisionPlane(); + + /** Get other's position and velocity coordinates. + * @return other's position and velocity coordinates + */ + PVCoordinates getOther(); + + /** Get flag that indicates if current local orbital frame shall be treated as pseudo-inertial. + * @return flag that indicates if current local orbital frame shall be treated as pseudo-inertial + */ + @Override + default boolean isQuasiInertial() { + return true; + } + +} diff --git a/src/main/java/org/orekit/frames/encounter/EncounterLOFType.java b/src/main/java/org/orekit/frames/encounter/EncounterLOFType.java new file mode 100644 index 0000000000..119240ea81 --- /dev/null +++ b/src/main/java/org/orekit/frames/encounter/EncounterLOFType.java @@ -0,0 +1,94 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames.encounter; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * Enum for encounter local orbital frame. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public enum EncounterLOFType { + + /** + * Default encounter local orbital frame based on Figure 1.3 from Romain Serra's thesis. + *

        + * Note that it is up to the user to choose which object should be at the origin. + */ + DEFAULT { + /** {@inheritDoc} */ + @Override + public DefaultEncounterLOF getFrame(final PVCoordinates other) { + return new DefaultEncounterLOF(other); + } + + /** {@inheritDoc} */ + @Override + public > EncounterLOF getFrame( + final FieldPVCoordinates other) { + return new DefaultEncounterLOF(other); + } + + }, + /** + * Valsecchi encounter local orbital frame based on Valsecchi formulation from : "Valsecchi, G. B., Milani, A., Gronchi, + * G. F. & Ches- ley, S. R. Resonant returns to close approaches: Analytical theory. Astronomy & Astrophysics + * 408, 1179–1196 (2003)." + *

        + * Note that it is up to the user to choose which object should be at the origin. + */ + VALSECCHI { + /** {@inheritDoc} */ + @Override + public ValsecchiEncounterFrame getFrame(final PVCoordinates other) { + return new ValsecchiEncounterFrame(other); + } + + /** {@inheritDoc} */ + @Override + public > EncounterLOF getFrame( + final FieldPVCoordinates other) { + return new ValsecchiEncounterFrame(other); + } + + }; + + /** + * Get encounter local orbital frame associated to this enum. + * + * @param other other object {@link PVCoordinates position and velocity coordinates} that is not the origin of the + * encounter frame + * + * @return encounter local orbital frame associated to this enum + */ + public abstract EncounterLOF getFrame(PVCoordinates other); + + /** + * Get encounter local orbital frame associated to this enum. + * + * @param other other object {@link PVCoordinates position and velocity coordinates} that is not the origin of the + * encounter frame + * @param type of the field elements + * + * @return encounter local orbital frame associated to this enum + */ + public abstract > EncounterLOF getFrame(FieldPVCoordinates other); +} diff --git a/src/main/java/org/orekit/frames/encounter/ValsecchiEncounterFrame.java b/src/main/java/org/orekit/frames/encounter/ValsecchiEncounterFrame.java new file mode 100644 index 0000000000..17c7e8b966 --- /dev/null +++ b/src/main/java/org/orekit/frames/encounter/ValsecchiEncounterFrame.java @@ -0,0 +1,107 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.frames.encounter; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * Valsecchi encounter local orbital frame based on Valsecchi formulation from : "Valsecchi, G. B., Milani, A., Gronchi, G. + * F. & Ches- ley, S. R. Resonant returns to close approaches: Analytical theory. Astronomy & Astrophysics 408, + * 1179–1196 (2003).". + *

        + * Note that it is up to the user to choose which object should be at the origin. + * + * @author Vincent Cucchietti + * @author Quentin Parpaite + * @since 12.0 + */ +public class ValsecchiEncounterFrame extends AbstractEncounterLOF { + + /** + * Constructor. + * + * @param other other object to create the encounter frame with (not the origin of the frame !) + */ + public ValsecchiEncounterFrame(final PVCoordinates other) { + super(other); + } + + /** + * Constructor. + * + * @param other other object to create the encounter frame with (not the origin of the frame !) + * @param type of the field elements + */ + public > ValsecchiEncounterFrame(final FieldPVCoordinates other) { + super(other); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation rotationFromInertial(final Field field, + final FieldPVCoordinates origin, + final FieldPVCoordinates other) { + final FieldVector3D otherVelocity = other.getVelocity(); + + final FieldVector3D xAxis = origin.getVelocity().crossProduct(otherVelocity).normalize(); + final FieldVector3D yAxis = otherVelocity.subtract(origin.getVelocity()).normalize(); + + return new FieldRotation<>(xAxis, yAxis, FieldVector3D.getPlusI(field), FieldVector3D.getPlusJ(field)); + } + + /** {@inheritDoc} */ + @Override + public Rotation rotationFromInertial(final PVCoordinates origin, final PVCoordinates other) { + final Vector3D xAxis = origin.getVelocity().crossProduct(other.getVelocity()).normalize(); + final Vector3D yAxis = other.getVelocity().subtract(origin.getVelocity()).normalize(); + + return new Rotation(xAxis, yAxis, Vector3D.PLUS_I, Vector3D.PLUS_J); + } + + /** + * {@inheritDoc} + *

        + * In this case, return (0,1,0); + */ + @Override + public > FieldVector3D getAxisNormalToCollisionPlane(final Field field) { + return FieldVector3D.getPlusJ(field); + } + + /** + * {@inheritDoc} + *

        + * In this case, return (0,1,0); + */ + @Override + public Vector3D getAxisNormalToCollisionPlane() { + return Vector3D.PLUS_J; + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return "VALSECCHI_ENCOUNTER_LOF"; + } +} diff --git a/src/main/java/org/orekit/frames/encounter/package-info.java b/src/main/java/org/orekit/frames/encounter/package-info.java new file mode 100644 index 0000000000..5613c302bb --- /dev/null +++ b/src/main/java/org/orekit/frames/encounter/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Package specific to encounter local orbital frame. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +package org.orekit.frames.encounter; diff --git a/src/main/java/org/orekit/frames/package-info.java b/src/main/java/org/orekit/frames/package-info.java index f5861460eb..e01a765ea7 100644 --- a/src/main/java/org/orekit/frames/package-info.java +++ b/src/main/java/org/orekit/frames/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/geometry/fov/AbstractFieldOfView.java b/src/main/java/org/orekit/geometry/fov/AbstractFieldOfView.java index dda532a9fc..6bd0bd5e01 100644 --- a/src/main/java/org/orekit/geometry/fov/AbstractFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/AbstractFieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/geometry/fov/CircularFieldOfView.java b/src/main/java/org/orekit/geometry/fov/CircularFieldOfView.java index f0450af534..56eb97965f 100644 --- a/src/main/java/org/orekit/geometry/fov/CircularFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/CircularFieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/geometry/fov/DoubleDihedraFieldOfView.java b/src/main/java/org/orekit/geometry/fov/DoubleDihedraFieldOfView.java index ffd3a36183..143523686a 100644 --- a/src/main/java/org/orekit/geometry/fov/DoubleDihedraFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/DoubleDihedraFieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java b/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java index 6c38c00695..f9f15066dd 100644 --- a/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/geometry/fov/FieldOfView.java b/src/main/java/org/orekit/geometry/fov/FieldOfView.java index 05199b4e70..fc85574892 100644 --- a/src/main/java/org/orekit/geometry/fov/FieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/FieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java b/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java index 2cfc2aad0e..6281c9b0ef 100644 --- a/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -210,7 +210,7 @@ public List> getFootprint(final Transform fovToBody, // none of the Field Of View loops cross the body // either the body is outside of Field Of View, or it is fully contained // we check the center - final Vector3D bodyCenter = fovToBody.getInverse().transformPosition(Vector3D.ZERO); + final Vector3D bodyCenter = fovToBody.toStaticTransform().getInverse().transformPosition(Vector3D.ZERO); if (zone.checkPoint(new S2Point(bodyCenter)) != Region.Location.OUTSIDE) { // the body is fully contained in the Field Of View // we use the full limb as the footprint diff --git a/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java b/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java index 4ab5f405c6..5a92811602 100644 --- a/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -154,7 +154,7 @@ public List> getFootprint(final Transform fovToBody, // the Field Of View loop does not cross the body // either the body is outside of Field Of View, or it is fully contained // we check the center - final Vector3D bodyCenter = fovToBody.getInverse().transformPosition(Vector3D.ZERO); + final Vector3D bodyCenter = fovToBody.toStaticTransform().getInverse().transformPosition(Vector3D.ZERO); if (offsetFromBoundary(bodyCenter, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0) { // the body is fully contained in the Field Of View // the previous loop did compute the full limb as the footprint diff --git a/src/main/java/org/orekit/geometry/fov/package-info.java b/src/main/java/org/orekit/geometry/fov/package-info.java index 8638a74b37..1ae62dbe28 100644 --- a/src/main/java/org/orekit/geometry/fov/package-info.java +++ b/src/main/java/org/orekit/geometry/fov/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/DOP.java b/src/main/java/org/orekit/gnss/DOP.java index ee73f65abd..2bf9c7daca 100644 --- a/src/main/java/org/orekit/gnss/DOP.java +++ b/src/main/java/org/orekit/gnss/DOP.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/DOPComputer.java b/src/main/java/org/orekit/gnss/DOPComputer.java index a835a092fd..cb8edeb562 100644 --- a/src/main/java/org/orekit/gnss/DOPComputer.java +++ b/src/main/java/org/orekit/gnss/DOPComputer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,6 +30,7 @@ import org.orekit.propagation.Propagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ElevationMask; +import org.orekit.utils.TrackingCoordinates; /** * This class aims at computing the dilution of precision. @@ -144,13 +145,13 @@ public DOP compute(final AbsoluteDate date, final List gnss) { final double[][] satDir = new double[gnss.size()][4]; int satNb = 0; for (Propagator prop : gnss) { - final Vector3D pos = prop.getPVCoordinates(date, frame).getPosition(); - final double elev = frame.getElevation(pos, frame, date); - final double elMin = (elevationMask != null) ? - elevationMask.getElevation(frame.getAzimuth(pos, frame, date)) : - minElevation; + final Vector3D pos = prop.getPosition(date, frame); + final TrackingCoordinates tc = frame.getTrackingCoordinates(pos, frame, date); + final double elMin = (elevationMask != null) ? + elevationMask.getElevation(tc.getAzimuth()) : + minElevation; // Only visible satellites are considered - if (elev > elMin) { + if (tc.getElevation() > elMin) { // Create the rows of the H matrix final Vector3D r = pos.normalize(); satDir[satNb][0] = r.getX(); diff --git a/src/main/java/org/orekit/gnss/Frequency.java b/src/main/java/org/orekit/gnss/Frequency.java index eb4631c806..06655b6648 100644 --- a/src/main/java/org/orekit/gnss/Frequency.java +++ b/src/main/java/org/orekit/gnss/Frequency.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -72,12 +72,22 @@ public enum Frequency { /** In the ANTEX files, both C01 and C02 refer to Beidou B1 signal (1561.098 MHz). */ C02(SatelliteSystem.BEIDOU, "B1", 152.6), + /** In the ANTEX files, C05 appears without much reference + * for consistency with Rinex 4 tables, we assume it is B2a (1176.45 MHz). + */ + C05(SatelliteSystem.BEIDOU, "B2a", 115), + /** In the ANTEX files, C06 appears without much reference, we assume it is B2 (1207.14 MHz). */ C06(SatelliteSystem.BEIDOU, "B2", 118), /** In the ANTEX files, C07 seems to refer to a signal close to E06, probably B3... (1268.52 MHz). */ C07(SatelliteSystem.BEIDOU, "B3", 124), + /** In the ANTEX files, C08 appears without much reference + * for consistency with Rinex 4 tables, we assume it is B2 (B2a+B2b) (1191.795 MHz). + */ + C08(SatelliteSystem.BEIDOU, "B2 (B2a+B2b)", 116.5), + /** Beidou B1 (1561.098 MHz). */ B01(SatelliteSystem.BEIDOU, "B1", 152.6), @@ -87,19 +97,24 @@ public enum Frequency { /** Beidou B3 (1268.52 MHz). */ B03(SatelliteSystem.BEIDOU, "B3", 124), - /** Beidou B1 (1575.42 MHz). - * FIXME the name must be updated in 12.0. - * It has been set to B04 as a workaround to handle the incompatibility between Rinex 3.02 and Rinex 3.04 for C2X - * In 3.02 the frequency of C2X is equal to 1561.098 MHz whereas in 3.04 it is equal to 1575.42 MHz - */ - B04(SatelliteSystem.BEIDOU, "B1", 154), + /** Beidou B1C (1575.42 MHz). */ + B1C(SatelliteSystem.BEIDOU, "B1C", 154), + + /** Beidou B1A (1575.42 MHz). */ + B1A(SatelliteSystem.BEIDOU, "B1A", 154), /** Beidou B2a (1176.45 MHz). */ - B05(SatelliteSystem.BEIDOU, "B2a", 115), + B2A(SatelliteSystem.BEIDOU, "B2a", 115), + + /** Beidou B2b (1207.14 MHz). */ + B2B(SatelliteSystem.BEIDOU, "B2b", 118), /** Beidou B2 (B2a + B2b) (1191.795MHz). */ B08(SatelliteSystem.BEIDOU, "B2 (B2a+B2b)", 116.5), + /** Beidou B3A (1268.52 MHz). */ + B3A(SatelliteSystem.BEIDOU, "B3A", 124), + /** QZSS L1 (1575.42 MHz). */ J01(SatelliteSystem.QZSS, "L1", 154), diff --git a/src/main/java/org/orekit/gnss/MeasurementType.java b/src/main/java/org/orekit/gnss/MeasurementType.java index ee03d30225..b84aecdb8c 100644 --- a/src/main/java/org/orekit/gnss/MeasurementType.java +++ b/src/main/java/org/orekit/gnss/MeasurementType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/ObservationTimeScale.java b/src/main/java/org/orekit/gnss/ObservationTimeScale.java new file mode 100644 index 0000000000..f9d0450432 --- /dev/null +++ b/src/main/java/org/orekit/gnss/ObservationTimeScale.java @@ -0,0 +1,74 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScales; + +/** Observation time scales. + * @since 12.0 + */ +public enum ObservationTimeScale { + + /** GPS time scale. */ + GPS { + public TimeScale getTimeScale(final TimeScales timeScales) { + return timeScales.getGPS(); + } + }, + + /** Galileo time scale. */ + GAL { + public TimeScale getTimeScale(final TimeScales timeScales) { + return timeScales.getGST(); + } + }, + + /** GLONASS time scale. */ + GLO { + public TimeScale getTimeScale(final TimeScales timeScales) { + return timeScales.getGLONASS(); + } + }, + + /** QZSS time scale. */ + QZS { + public TimeScale getTimeScale(final TimeScales timeScales) { + return timeScales.getQZSS(); + } + }, + + /** Beidou time scale. */ + BDT { + public TimeScale getTimeScale(final TimeScales timeScales) { + return timeScales.getBDT(); + } + }, + + /** IRNSS time scale. */ + IRN { + public TimeScale getTimeScale(final TimeScales timeScales) { + return timeScales.getIRNSS(); + } + }; + + /** Get time scale. + * @param timeScales time scales factory + * @return time scale + */ + public abstract TimeScale getTimeScale(TimeScales timeScales); + +} diff --git a/src/main/java/org/orekit/gnss/ObservationType.java b/src/main/java/org/orekit/gnss/ObservationType.java index adaad2dc58..2a5dc0d753 100644 --- a/src/main/java/org/orekit/gnss/ObservationType.java +++ b/src/main/java/org/orekit/gnss/ObservationType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -120,46 +120,49 @@ public enum ObservationType { S8(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.E08), /** Pseudorange Galileo E1 A / Beidou B1A for Rinex3. */ - C1A(MeasurementType.PSEUDO_RANGE, SignalCode.A, Frequency.E01, Frequency.B04), + C1A(MeasurementType.PSEUDO_RANGE, SignalCode.A, Frequency.E01, Frequency.B1A), - /** Pseudorange Galileo E1 I/NAV OS/CS/SoL for Rinex3. */ - C1B(MeasurementType.PSEUDO_RANGE, SignalCode.B, Frequency.E01), + /** Pseudorange Galileo E1 I/NAV OS/CS/SoL / QZSS geo signal for Rinex3. */ + C1B(MeasurementType.PSEUDO_RANGE, SignalCode.B, Frequency.E01, Frequency.J01), /** Pseudorange GPS L1 C/A / GLONASS G1 C/A / Galileo E1 C / SBAS L1 C/A / QZSS L1 C/A for Rinex3. */ C1C(MeasurementType.PSEUDO_RANGE, SignalCode.C, Frequency.G01, Frequency.R01, Frequency.E01, Frequency.S01, Frequency.J01), /** Pseudorange Beidou B1 Data for Rinex3. */ - C1D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.B04), + C1D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.B1A), + + /** Pseudorange QZSS L1 C/B for Rinex4. */ + C1E(MeasurementType.PSEUDO_RANGE, SignalCode.C, Frequency.J01), /** Pseudorange Beidou B1 I for Rinex3.02. */ C1I(MeasurementType.PSEUDO_RANGE, SignalCode.I, Frequency.B01), - /** Pseudorange GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3. */ - C1L(MeasurementType.PSEUDO_RANGE, SignalCode.L, Frequency.G01, Frequency.J01), + /** Pseudorange GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3, Beidou B1A for Rinex3.03. */ + C1L(MeasurementType.PSEUDO_RANGE, SignalCode.L, Frequency.G01, Frequency.J01, Frequency.B1A), /** Pseudorange GPS L1 M for Rinex3. */ C1M(MeasurementType.PSEUDO_RANGE, SignalCode.M, Frequency.G01), /** Pseudorange GPS L1 P(AS off) / GLONASS G1 P / Beidou C1 Pilot for Rinex3. */ - C1P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.G01, Frequency.R01, Frequency.B04), + C1P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.G01, Frequency.R01, Frequency.B1A), /** Pseudorange Beidou B1 Q for Rinex3.02. */ C1Q(MeasurementType.PSEUDO_RANGE, SignalCode.Q, Frequency.B01), - /** Pseudorange GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3. */ - C1S(MeasurementType.PSEUDO_RANGE, SignalCode.S, Frequency.G01, Frequency.J01), + /** Pseudorange GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3, Beidou B1A for Rinex3.03. */ + C1S(MeasurementType.PSEUDO_RANGE, SignalCode.S, Frequency.G01, Frequency.J01, Frequency.B1A), /** Pseudorange GPS L1 Z-tracking and similar (AS on) for Rinex3. */ C1W(MeasurementType.PSEUDO_RANGE, SignalCode.W, Frequency.G01), /** Pseudorange GPS L1 L1C (D+P) / Galileo E1 B+C / QZSS L1 L1C(D+P) / Beidou B1 Data+Pilot for Rinex3. */ - C1X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B04), + C1X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B1A), /** Pseudorange GPS L1 Y for Rinex3. */ C1Y(MeasurementType.PSEUDO_RANGE, SignalCode.Y, Frequency.G01), - /** Pseudorange Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3. */ - C1Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.E01, Frequency.J01), + /** Pseudorange Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3, Beidou B1A for Rinex3.03. */ + C1Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.E01, Frequency.J01, Frequency.B1A), /** Pseudorange GPS L2 C/A / GLONASS G2 C/A for Rinex3. */ C2C(MeasurementType.PSEUDO_RANGE, SignalCode.C, Frequency.G02, Frequency.R02), @@ -188,8 +191,8 @@ public enum ObservationType { /** Pseudorange GPS L2 Z-tracking and similar (AS on) for Rinex3. */ C2W(MeasurementType.PSEUDO_RANGE, SignalCode.W, Frequency.G02), - /** Pseudorange GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3. */ - C2X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.G02, Frequency.J02), + /** Pseudorange GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3, Beidou B1 I+Q for Rinex3.03. */ + C2X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.G02, Frequency.J02, Frequency.B01), /** Pseudorange GPS L2 Y for Rinex3. */ C2Y(MeasurementType.PSEUDO_RANGE, SignalCode.Y, Frequency.G02), @@ -222,19 +225,19 @@ public enum ObservationType { C5C(MeasurementType.PSEUDO_RANGE, SignalCode.C, Frequency.I05), /** Pseudorange QZSS L5 D / Beidou B2a Data for Rinex3. */ - C5D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.J05, Frequency.B05), + C5D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.J05, Frequency.B2A), /** Pseudorange GPS L5 I/ Galileo E5a F/NAV OS / SBAS L5 I / QZSS L5 I for Rinex3. */ C5I(MeasurementType.PSEUDO_RANGE, SignalCode.I, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Pseudorange QZSS L5 P / Beidou B2a Pilot for Rinex3. */ - C5P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.J05, Frequency.B05), + C5P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.J05, Frequency.B2A), /** Pseudorange GPS L5 Q/ Galileo E5a Q / SBAS L5 Q / QZSS L5 Q for Rinex3. */ C5Q(MeasurementType.PSEUDO_RANGE, SignalCode.Q, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Pseudorange GPS L5 I+Q/ Galileo E5a I+Q / SBAS L5 I+Q / QZSS L5 I+Q / IRNSS L5 B+C / Beidou B2a Data+Pilot for Rinex3. */ - C5X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B05), + C5X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B2A), /** Pseudorange QZSS L5 D+P for Rinex3. */ C5Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.J05), @@ -248,6 +251,9 @@ public enum ObservationType { /** Pseudorange Galileo E6 C no data for Rinex3. */ C6C(MeasurementType.PSEUDO_RANGE, SignalCode.C, Frequency.E06), + /** Pseudorange Beidou B3A for Rinex3. */ + C6D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.B3A), + /** Pseudorange QZSS L6E for Rinex3. */ C6E(MeasurementType.PSEUDO_RANGE, SignalCode.E, Frequency.J06), @@ -257,6 +263,9 @@ public enum ObservationType { /** Pseudorange QZSS LEX(6) L for Rinex3. */ C6L(MeasurementType.PSEUDO_RANGE, SignalCode.L, Frequency.J06), + /** Pseudorange Beidou B3A for Rinex3. */ + C6P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.B3A), + /** Pseudorange Beidou B3 Q for Rinex3. */ C6Q(MeasurementType.PSEUDO_RANGE, SignalCode.Q, Frequency.B03), @@ -266,17 +275,17 @@ public enum ObservationType { /** Pseudorange Galileo E6 B+C / QZSS LEX(6) S+L / Beidou B3 I+Q / GLONASS G2a L2CSI+L2OCp for Rinex3. */ C6X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.E06, Frequency.J06, Frequency.B03, Frequency.R06), - /** Pseudorange Galileo E6 A+B+C / QZSS L6(D+E) for Rinex3. */ - C6Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.E06, Frequency.J06), + /** Pseudorange Galileo E6 A+B+C / QZSS L6(D+E) / Beidou B3A for Rinex3. */ + C6Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.E06, Frequency.J06, Frequency.B3A), /** Pseudorange Beidou B2b Data for Rinex3. */ - C7D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.B02), + C7D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.B02, Frequency.B2B), /** Pseudorange Galileo E5b I I/NAV OS/CS/SoL / Beidou B2 I for Rinex3. */ C7I(MeasurementType.PSEUDO_RANGE, SignalCode.I, Frequency.E07, Frequency.B02), /** Pseudorange Beidou B2b Pilot for Rinex3. */ - C7P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.B02), + C7P(MeasurementType.PSEUDO_RANGE, SignalCode.P, Frequency.B02, Frequency.B2B), /** Pseudorange Galileo Q no data / Beidou B2 Q for Rinex3. */ C7Q(MeasurementType.PSEUDO_RANGE, SignalCode.Q, Frequency.E07, Frequency.B02), @@ -285,7 +294,7 @@ public enum ObservationType { C7X(MeasurementType.PSEUDO_RANGE, SignalCode.X, Frequency.E07, Frequency.B02), /** Pseudorange Beidou B2b Data+Pilot for Rinex3. */ - C7Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.B02), + C7Z(MeasurementType.PSEUDO_RANGE, SignalCode.Z, Frequency.B02, Frequency.B2B), /** Pseudorange Beidou B2(B2a+B2b) Data for Rinex3. */ C8D(MeasurementType.PSEUDO_RANGE, SignalCode.D, Frequency.B08), @@ -330,46 +339,49 @@ public enum ObservationType { CD(MeasurementType.PSEUDO_RANGE, SignalCode.C, Frequency.R02), /** Doppler Galileo E1 A / Beidou B1 B1A for Rinex3. */ - D1A(MeasurementType.DOPPLER, SignalCode.A, Frequency.E01, Frequency.B04), + D1A(MeasurementType.DOPPLER, SignalCode.A, Frequency.E01, Frequency.B1A), - /** Doppler Galileo E1 I/NAV OS/CS/SoL for Rinex3. */ - D1B(MeasurementType.DOPPLER, SignalCode.B, Frequency.E01), + /** Doppler Galileo E1 I/NAV OS/CS/SoL / QZSS geo signal for Rinex3. */ + D1B(MeasurementType.DOPPLER, SignalCode.B, Frequency.E01, Frequency.J01), /** Doppler GPS L1 C/A / GLONASS G1 C/A / Galileo E1 C / SBAS L1 C/A / QZSS L1 C/A for Rinex3. */ D1C(MeasurementType.DOPPLER, SignalCode.C, Frequency.G01, Frequency.R01, Frequency.E01, Frequency.S01, Frequency.J01), /** Doppler Beidou B1 Data for Rinex3. */ - D1D(MeasurementType.DOPPLER, SignalCode.D, Frequency.B04), + D1D(MeasurementType.DOPPLER, SignalCode.D, Frequency.B1A), + + /** Doppler QZSS L1 C/B for Rinex4. */ + D1E(MeasurementType.DOPPLER, SignalCode.C, Frequency.J01), /** Doppler Beidou B1 I for Rinex3. */ D1I(MeasurementType.DOPPLER, SignalCode.I, Frequency.B01), - /** Doppler GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3. */ - D1L(MeasurementType.DOPPLER, SignalCode.L, Frequency.G01, Frequency.J01), + /** Doppler GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3, Beidou B1A for Rinex3.03. */ + D1L(MeasurementType.DOPPLER, SignalCode.L, Frequency.G01, Frequency.J01, Frequency.B1A), /** Doppler GPS L2 M for Rinex3. */ D1M(MeasurementType.DOPPLER, SignalCode.M, Frequency.G02), /** Doppler GPS L1 codeless / Beidou B1 codeless for Rinex3. */ - D1N(MeasurementType.DOPPLER, SignalCode.CODELESS, Frequency.G01, Frequency.B04), + D1N(MeasurementType.DOPPLER, SignalCode.CODELESS, Frequency.G01, Frequency.B1A), /** Doppler GPS L2 P(AS off) / GLONASS G2 P / Beidou B1 Pilot for Rinex3. */ - D1P(MeasurementType.DOPPLER, SignalCode.P, Frequency.G02, Frequency.R02, Frequency.B04), + D1P(MeasurementType.DOPPLER, SignalCode.P, Frequency.G02, Frequency.R02, Frequency.B1A), - /** Doppler GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3. */ - D1S(MeasurementType.DOPPLER, SignalCode.S, Frequency.G01, Frequency.J01), + /** Doppler GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3, Beidou B1A for Rinex3.03. */ + D1S(MeasurementType.DOPPLER, SignalCode.S, Frequency.G01, Frequency.J01, Frequency.B1A), /** Doppler GPS L1 Z-tracking and similar (AS on) for Rinex3. */ D1W(MeasurementType.DOPPLER, SignalCode.W, Frequency.G01), /** Doppler GPS L1 L1C (D+P) / Galileo E1 B+C / QZSS L1 L1C(D+P) / Beidou B1 Data+Pilot for Rinex3. */ - D1X(MeasurementType.DOPPLER, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B04), + D1X(MeasurementType.DOPPLER, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B1A), /** Doppler GPS L1 Y for Rinex3. */ D1Y(MeasurementType.DOPPLER, SignalCode.Y, Frequency.G01), - /** Doppler Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3. */ - D1Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.E01, Frequency.J01), + /** Doppler Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3, Beidou B1A for Rinex3.03. */ + D1Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.E01, Frequency.J01, Frequency.B1A), /** Doppler GPS L2 C/A / GLONASS G2 C/A for Rinex3. */ D2C(MeasurementType.DOPPLER, SignalCode.C, Frequency.G02, Frequency.R02), @@ -401,8 +413,8 @@ public enum ObservationType { /** Doppler GPS L2 Z-tracking and similar (AS on) for Rinex3. */ D2W(MeasurementType.DOPPLER, SignalCode.W, Frequency.G02), - /** Doppler GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3. */ - D2X(MeasurementType.DOPPLER, SignalCode.X, Frequency.G02, Frequency.J02), + /** Doppler GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3, Beidou B1 I+Q for Rinex3.03. */ + D2X(MeasurementType.DOPPLER, SignalCode.X, Frequency.G02, Frequency.J02, Frequency.B01), /** Doppler GPS L2 Y for Rinex3. */ D2Y(MeasurementType.DOPPLER, SignalCode.Y, Frequency.G02), @@ -435,19 +447,19 @@ public enum ObservationType { D5C(MeasurementType.DOPPLER, SignalCode.C, Frequency.I05), /** Doppler QZSS L5 D / Beidou B2a Data for Rinex3. */ - D5D(MeasurementType.DOPPLER, SignalCode.D, Frequency.J05, Frequency.B05), + D5D(MeasurementType.DOPPLER, SignalCode.D, Frequency.J05, Frequency.B2A), /** Doppler GPS L5 I/ Galileo E5a F/NAV OS / SBAS L5 I / QZSS L5 I for Rinex3. */ D5I(MeasurementType.DOPPLER, SignalCode.I, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Doppler QZSS L5 P / Beidou B2a Pilot for Rinex3. */ - D5P(MeasurementType.DOPPLER, SignalCode.P, Frequency.J05, Frequency.B05), + D5P(MeasurementType.DOPPLER, SignalCode.P, Frequency.J05, Frequency.B2A), /** Doppler GPS L5 Q/ Galileo E5a Q / SBAS L5 Q / QZSS L5 Q for Rinex3. */ D5Q(MeasurementType.DOPPLER, SignalCode.Q, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Doppler GPS L5 I+Q/ Galileo E5a I+Q / SBAS L5 I+Q / QZSS L5 I+Q / IRNSS L5 B+C / Beidou B2a Data+Pilot for Rinex3. */ - D5X(MeasurementType.DOPPLER, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B05), + D5X(MeasurementType.DOPPLER, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B2A), /** Doppler QZSS L5 D+P for Rinex3. */ D5Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.J05), @@ -461,35 +473,41 @@ public enum ObservationType { /** Doppler Galileo E6 C no data for Rinex3. */ D6C(MeasurementType.DOPPLER, SignalCode.C, Frequency.E06), + /** Doppler Beidou B3A for Rinex3. */ + D6D(MeasurementType.DOPPLER, SignalCode.D, Frequency.B3A), + /** Doppler QZSS L6E for Rinex3. */ D6E(MeasurementType.DOPPLER, SignalCode.E, Frequency.J06), /** Doppler Beidou B3 I for Rinex3. */ D6I(MeasurementType.DOPPLER, SignalCode.I, Frequency.B03), - /** Doppler Beidou B3 Q for Rinex3. */ - D6Q(MeasurementType.DOPPLER, SignalCode.Q, Frequency.B03), - /** Doppler QZSS LEX(6) L for Rinex3. */ D6L(MeasurementType.DOPPLER, SignalCode.L, Frequency.J06), + /** Doppler Beidou B3A for Rinex3. */ + D6P(MeasurementType.DOPPLER, SignalCode.P, Frequency.B3A), + + /** Doppler Beidou B3 Q for Rinex3. */ + D6Q(MeasurementType.DOPPLER, SignalCode.Q, Frequency.B03), + /** Doppler QZSS LEX(6) S for Rinex3. */ D6S(MeasurementType.DOPPLER, SignalCode.S, Frequency.J06), /** Doppler Galileo E6 B+C / QZSS LEX(6) S+L / Beidou B3 I+Q / GLONASS G2a L2CSI+L2OCp for Rinex3. */ D6X(MeasurementType.DOPPLER, SignalCode.X, Frequency.E06, Frequency.J06, Frequency.B03, Frequency.R06), - /** Doppler Galileo E6 A+B+C / QZSS L6(D+E) for Rinex3. */ - D6Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.E06, Frequency.J06), + /** Doppler Galileo E6 A+B+C / QZSS L6(D+E) / Beidou B3A for Rinex3. */ + D6Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.E06, Frequency.J06, Frequency.B3A), /** Doppler Beidou B2b Data for Rinex3. */ - D7D(MeasurementType.DOPPLER, SignalCode.D, Frequency.B02), + D7D(MeasurementType.DOPPLER, SignalCode.D, Frequency.B02, Frequency.B2B), /** Doppler Galileo E5b I I/NAV OS/CS/SoL / Beidou B2 I for Rinex3. */ D7I(MeasurementType.DOPPLER, SignalCode.I, Frequency.E07, Frequency.B02), /** Doppler Beidou B2b Pilot for Rinex3. */ - D7P(MeasurementType.DOPPLER, SignalCode.P, Frequency.B02), + D7P(MeasurementType.DOPPLER, SignalCode.P, Frequency.B02, Frequency.B2B), /** Doppler Galileo Q no data / Beidou B2 Q for Rinex3. */ D7Q(MeasurementType.DOPPLER, SignalCode.Q, Frequency.E07, Frequency.B02), @@ -498,7 +516,7 @@ public enum ObservationType { D7X(MeasurementType.DOPPLER, SignalCode.X, Frequency.E07, Frequency.B02), /** Doppler Beidou B2b Data+Pilot for Rinex3. */ - D7Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.B02), + D7Z(MeasurementType.DOPPLER, SignalCode.Z, Frequency.B02, Frequency.B2B), /** Doppler Beidou B2(B2a+B2b) Data for Rinex3. */ D8D(MeasurementType.DOPPLER, SignalCode.D, Frequency.B08), @@ -543,22 +561,25 @@ public enum ObservationType { DD(MeasurementType.DOPPLER, SignalCode.C, Frequency.R02), /** Carrier-phase Galileo E1 A / Beidou B1 B1A for Rinex3. */ - L1A(MeasurementType.CARRIER_PHASE, SignalCode.A, Frequency.E01, Frequency.B04), + L1A(MeasurementType.CARRIER_PHASE, SignalCode.A, Frequency.E01, Frequency.B1A), - /** Carrier-phase Galileo E1 I/NAV OS/CS/SoL for Rinex3. */ - L1B(MeasurementType.CARRIER_PHASE, SignalCode.B, Frequency.E01), + /** Carrier-phase Galileo E1 I/NAV OS/CS/SoL / QZSS geo signal for Rinex3. */ + L1B(MeasurementType.CARRIER_PHASE, SignalCode.B, Frequency.E01, Frequency.J01), /** Carrier-phase GPS L1 C/A / GLONASS G1 C/A / Galileo E1 C / SBAS L1 C/A / QZSS L1 C/A for Rinex3. */ L1C(MeasurementType.CARRIER_PHASE, SignalCode.C, Frequency.G01, Frequency.R01, Frequency.E01, Frequency.S01, Frequency.J01), /** Carrier-phase Beidou B1 Data for Rinex3. */ - L1D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.B04), + L1D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.B1A), + + /** Carrier-phase QZSS L1 C/B for Rinex4. */ + L1E(MeasurementType.CARRIER_PHASE, SignalCode.C, Frequency.J01), /** Carrier-phase Beidou B1 I for Rinex3. */ L1I(MeasurementType.CARRIER_PHASE, SignalCode.I, Frequency.B01), - /** Carrier-phase GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3. */ - L1L(MeasurementType.CARRIER_PHASE, SignalCode.L, Frequency.G01, Frequency.J01), + /** Carrier-phase GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3, Beidou B1A for Rinex3.03. */ + L1L(MeasurementType.CARRIER_PHASE, SignalCode.L, Frequency.G01, Frequency.J01, Frequency.B1A), /** Carrier-phase GPS L2 M for Rinex3. */ L1M(MeasurementType.CARRIER_PHASE, SignalCode.M, Frequency.G02), @@ -567,22 +588,22 @@ public enum ObservationType { L1N(MeasurementType.CARRIER_PHASE, SignalCode.CODELESS, Frequency.G01), /** Carrier-phase GPS L2 P(AS off) / GLONASS G2 P / Beidou B1 Pilot for Rinex3. */ - L1P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.G02, Frequency.R02, Frequency.B04), + L1P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.G02, Frequency.R02, Frequency.B1A), - /** Carrier-phase GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3. */ - L1S(MeasurementType.CARRIER_PHASE, SignalCode.S, Frequency.G01, Frequency.J01), + /** Carrier-phase GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3, Beidou B1A for Rinex3.03. */ + L1S(MeasurementType.CARRIER_PHASE, SignalCode.S, Frequency.G01, Frequency.J01, Frequency.B1A), /** Carrier-phase GPS L1 Z-tracking and similar (AS on) for Rinex3. */ L1W(MeasurementType.CARRIER_PHASE, SignalCode.W, Frequency.G01), /** Carrier-phase GPS L1 L1C (D+P) / Galileo E1 B+C / QZSS L1 L1C(D+P) / Beidou B1 Data+Pilot for Rinex3. */ - L1X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B04), + L1X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B1A), /** Carrier-phase GPS L1 Y for Rinex3. */ L1Y(MeasurementType.CARRIER_PHASE, SignalCode.Y, Frequency.G01), - /** Carrier-phase Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3. */ - L1Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.E01, Frequency.J01), + /** Carrier-phase Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3, Beidou B1A for Rinex3.03. */ + L1Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.E01, Frequency.J01, Frequency.B1A), /** Carrier-phase GPS L2 C/A / GLONASS G2 C/A for Rinex3. */ L2C(MeasurementType.CARRIER_PHASE, SignalCode.C, Frequency.G02, Frequency.R02), @@ -599,8 +620,8 @@ public enum ObservationType { /** Carrier-phase GPS L2 M for Rinex3. */ L2M(MeasurementType.CARRIER_PHASE, SignalCode.M, Frequency.G02), - /** Carrier-phase GPS L2 codeless / Beidou B1 codeless for Rinex3. */ - L2N(MeasurementType.CARRIER_PHASE, SignalCode.CODELESS, Frequency.G02, Frequency.B04), + /** Carrier-phase GPS L2 codeless. */ + L2N(MeasurementType.CARRIER_PHASE, SignalCode.CODELESS, Frequency.G02), /** Carrier-phase GPS L2 P(AS off) / GLONASS G2 P for Rinex3. */ L2P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.G02, Frequency.R02), @@ -614,8 +635,8 @@ public enum ObservationType { /** Carrier-phase GPS L2 Z-tracking and similar (AS on) for Rinex3. */ L2W(MeasurementType.CARRIER_PHASE, SignalCode.W, Frequency.G02), - /** Carrier-phase GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3. */ - L2X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.G02, Frequency.J02), + /** Carrier-phase GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3, Beidou B1 I+Q for Rinex3.03. */ + L2X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.G02, Frequency.J02, Frequency.B01), /** Carrier-phase GPS L2 Y for Rinex3. */ L2Y(MeasurementType.CARRIER_PHASE, SignalCode.Y, Frequency.G02), @@ -648,19 +669,19 @@ public enum ObservationType { L5C(MeasurementType.CARRIER_PHASE, SignalCode.C, Frequency.I05), /** Carrier-phase QZSS L5 / Beidou B2a Data D for Rinex3. */ - L5D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.J05, Frequency.B05), + L5D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.J05, Frequency.B2A), /** Carrier-phase GPS L5 I/ Galileo E5a F/NAV OS / SBAS L5 I / QZSS L5 I for Rinex3. */ L5I(MeasurementType.CARRIER_PHASE, SignalCode.I, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Carrier-phase QZSS L5 P / Beidou B2a Pilot for Rinex3. */ - L5P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.J05, Frequency.B05), + L5P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.J05, Frequency.B2A), /** Carrier-phase GPS L5 Q/ Galileo E5a Q / SBAS L5 Q / QZSS L5 Q for Rinex3. */ L5Q(MeasurementType.CARRIER_PHASE, SignalCode.Q, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Carrier-phase GPS L5 I+Q/ Galileo E5a I+Q / SBAS L5 I+Q / QZSS L5 I+Q / IRNSS L5 B+C / Beidou B2a Data+Pilot for Rinex3. */ - L5X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B05), + L5X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B2A), /** Carrier-phase QZSS L5 D+P for Rinex3. */ L5Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.J05), @@ -674,35 +695,41 @@ public enum ObservationType { /** Carrier-phase Galileo E6 C no data for Rinex3. */ L6C(MeasurementType.CARRIER_PHASE, SignalCode.C, Frequency.E06), + /** Carrier-phase Beidou B3A for Rinex3. */ + L6D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.B3A), + /** Carrier-phase QZSS L6E for Rinex3. */ L6E(MeasurementType.CARRIER_PHASE, SignalCode.E, Frequency.J06), /** Carrier-phase Beidou B3 I for Rinex3. */ L6I(MeasurementType.CARRIER_PHASE, SignalCode.I, Frequency.B03), - /** Carrier-phase Beidou B3 Q for Rinex3. */ - L6Q(MeasurementType.CARRIER_PHASE, SignalCode.Q, Frequency.B03), - /** Carrier-phase QZSS LEX(6) L for Rinex3. */ L6L(MeasurementType.CARRIER_PHASE, SignalCode.L, Frequency.J06), + /** Carrier-phase Beidou B3A for Rinex3. */ + L6P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.B3A), + + /** Carrier-phase Beidou B3 Q for Rinex3. */ + L6Q(MeasurementType.CARRIER_PHASE, SignalCode.Q, Frequency.B03), + /** Carrier-phase QZSS LEX(6) S for Rinex3. */ L6S(MeasurementType.CARRIER_PHASE, SignalCode.S, Frequency.J06), /** Carrier-phase Galileo E6 B+C / QZSS LEX(6) S+L / Beidou B3 I+Q / GLONASS G2a L2CSI+L2OCp for Rinex3. */ L6X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.E06, Frequency.J06, Frequency.B03, Frequency.R06), - /** Carrier-phase Galileo E6 A+B+C / QZSS L6(D+E) for Rinex3. */ - L6Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.E06, Frequency.J06), + /** Carrier-phase Galileo E6 A+B+C / QZSS L6(D+E) / Beidou B3A for Rinex3. */ + L6Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.E06, Frequency.J06, Frequency.B3A), /** Carrier-phase Beidou B2b Data for Rinex3. */ - L7D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.B02), + L7D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.B02, Frequency.B2B), /** Carrier-phase Galileo E5b I I/NAV OS/CS/SoL / Beidou B2 I for Rinex3. */ L7I(MeasurementType.CARRIER_PHASE, SignalCode.I, Frequency.E07, Frequency.B02), /** Carrier-phase Beidou B2b Pilot for Rinex3. */ - L7P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.B02), + L7P(MeasurementType.CARRIER_PHASE, SignalCode.P, Frequency.B02, Frequency.B2B), /** Carrier-phase Galileo Q no data / Beidou B2 Q for Rinex3. */ L7Q(MeasurementType.CARRIER_PHASE, SignalCode.Q, Frequency.E07, Frequency.B02), @@ -711,7 +738,7 @@ public enum ObservationType { L7X(MeasurementType.CARRIER_PHASE, SignalCode.X, Frequency.E07, Frequency.B02), /** Carrier-phase Beidou B2b Data+Pilot for Rinex3. */ - L7Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.B02), + L7Z(MeasurementType.CARRIER_PHASE, SignalCode.Z, Frequency.B02, Frequency.B2B), /** Carrier-phase Beidou B2(B2a+B2b) Data for Rinex3. */ L8D(MeasurementType.CARRIER_PHASE, SignalCode.D, Frequency.B08), @@ -744,46 +771,49 @@ public enum ObservationType { L0(MeasurementType.CARRIER_PHASE, SignalCode.CODELESS), /** Signal-strength Galileo E1 A / Beidou B1 B1A for Rinex3. */ - S1A(MeasurementType.SIGNAL_STRENGTH, SignalCode.A, Frequency.E01, Frequency.B04), + S1A(MeasurementType.SIGNAL_STRENGTH, SignalCode.A, Frequency.E01, Frequency.B1A), - /** Signal-strength Galileo E1 I/NAV OS/CS/SoL for Rinex3. */ - S1B(MeasurementType.SIGNAL_STRENGTH, SignalCode.B, Frequency.E01), + /** Signal-strength Galileo E1 I/NAV OS/CS/SoL / QZSS geo signal for Rinex3. */ + S1B(MeasurementType.SIGNAL_STRENGTH, SignalCode.B, Frequency.E01, Frequency.J01), /** Signal-strength GPS L1 C/A / GLONASS G1 C/A / Galileo E1 C / SBAS L1 C/A / QZSS L1 C/A for Rinex3. */ S1C(MeasurementType.SIGNAL_STRENGTH, SignalCode.C, Frequency.G01, Frequency.R01, Frequency.E01, Frequency.S01, Frequency.J01), /** Signal-strength Beidou B1 Data for Rinex3. */ - S1D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.B04), + S1D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.B1A), + + /** Signal-strength QZSS L1 C/B for Rinex3. */ + S1E(MeasurementType.SIGNAL_STRENGTH, SignalCode.C, Frequency.J01), /** Signal-strength Beidou B1 I for Rinex3. */ S1I(MeasurementType.SIGNAL_STRENGTH, SignalCode.I, Frequency.B01), - /** Signal-strength GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3. */ - S1L(MeasurementType.SIGNAL_STRENGTH, SignalCode.L, Frequency.G01, Frequency.J01), + /** Signal-strength GPS L1 L1C(P) / QZSS L1 L1C(P) for Rinex3, Beidou B1A for Rinex3.03. */ + S1L(MeasurementType.SIGNAL_STRENGTH, SignalCode.L, Frequency.G01, Frequency.J01, Frequency.B1A), /** Signal-strength GPS L2 M for Rinex3. */ S1M(MeasurementType.SIGNAL_STRENGTH, SignalCode.M, Frequency.G02), /** Signal-strength GPS L1 codeless / Beidou B1 codeless for Rinex3. */ - S1N(MeasurementType.SIGNAL_STRENGTH, SignalCode.CODELESS, Frequency.G01, Frequency.B04), + S1N(MeasurementType.SIGNAL_STRENGTH, SignalCode.CODELESS, Frequency.G01, Frequency.B1A), /** Signal-strength GPS L2 P(AS off) / GLONASS G2 P / Beidou B1 Pilot for Rinex3. */ - S1P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.G02, Frequency.R02, Frequency.B04), + S1P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.G02, Frequency.R02, Frequency.B1A), - /** Signal-strength GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3. */ - S1S(MeasurementType.SIGNAL_STRENGTH, SignalCode.S, Frequency.G01, Frequency.J01), + /** Signal-strength GPS L1 L1C(D) / QZSS L1 L1C(D) for Rinex3, Beidou B1A for Rinex3.03. */ + S1S(MeasurementType.SIGNAL_STRENGTH, SignalCode.S, Frequency.G01, Frequency.J01, Frequency.B1A), /** Signal-strength GPS L1 Z-tracking and similar (AS on) for Rinex3. */ S1W(MeasurementType.SIGNAL_STRENGTH, SignalCode.W, Frequency.G01), /** Signal-strength GPS L1 L1C (D+P) / Galileo E1 B+C / QZSS L1 L1C(D+P) / Beidou B1 Data+Pilot for Rinex3. */ - S1X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B04), + S1X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.G01, Frequency.E01, Frequency.J01, Frequency.B1A), /** Signal-strength GPS L1 Y for Rinex3. */ S1Y(MeasurementType.SIGNAL_STRENGTH, SignalCode.Y, Frequency.G01), - /** Signal-strength Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3. */ - S1Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.E01, Frequency.J01), + /** Signal-strength Galileo E1 C1Z A+B+C / QZSS L1 L1-SAIF for Rinex3, Beidou B1A for Rinex3.03. */ + S1Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.E01, Frequency.J01, Frequency.B1A), /** Signal-strength GPS L2 C/A / GLONASS G2 C/A for Rinex3. */ S2C(MeasurementType.SIGNAL_STRENGTH, SignalCode.C, Frequency.G02, Frequency.R02), @@ -815,8 +845,8 @@ public enum ObservationType { /** Signal-strength GPS L2 Z-tracking and similar (AS on) for Rinex3. */ S2W(MeasurementType.SIGNAL_STRENGTH, SignalCode.W, Frequency.G02), - /** Signal-strength GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3. */ - S2X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.G02, Frequency.J02), + /** Signal-strength GPS L2 L2C (M+L) / QZSS L2 L2C(M+L) for Rinex3, Beidou B1 I+Q for Rinex3.03. */ + S2X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.G02, Frequency.J02, Frequency.B01), /** Signal-strength GPS L2 Y for Rinex3. */ S2Y(MeasurementType.SIGNAL_STRENGTH, SignalCode.Y, Frequency.G02), @@ -849,19 +879,19 @@ public enum ObservationType { S5C(MeasurementType.SIGNAL_STRENGTH, SignalCode.C, Frequency.I05), /** Signal-strength QZSS L5 D / Beidou B2a Data for Rinex3. */ - S5D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.J05, Frequency.B05), + S5D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.J05, Frequency.B2A), /** Signal-strength GPS L5 I/ Galileo E5a F/NAV OS / SBAS L5 I / QZSS L5 I for Rinex3. */ S5I(MeasurementType.SIGNAL_STRENGTH, SignalCode.I, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Signal-strength QZSS L5 P / Beidou B2a Pilot for Rinex3. */ - S5P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.J05, Frequency.B05), + S5P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.J05, Frequency.B2A), /** Signal-strength GPS L5 Q/ Galileo E5a Q / SBAS L5 Q / QZSS L5 Q for Rinex3. */ S5Q(MeasurementType.SIGNAL_STRENGTH, SignalCode.Q, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05), /** Signal-strength GPS L5 I+Q/ Galileo E5a I+Q / SBAS L5 I+Q / QZSS L5 I+Q / IRNSS L5 B+C / Beidou B2a Data+Pilot for Rinex3. */ - S5X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B05), + S5X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.G05, Frequency.E05, Frequency.S05, Frequency.J05, Frequency.I05, Frequency.B2A), /** Signal-strength QZSS L5 D+P for Rinex3. */ S5Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.J05), @@ -875,35 +905,41 @@ public enum ObservationType { /** Signal-strength Galileo E6 C no data for Rinex3. */ S6C(MeasurementType.SIGNAL_STRENGTH, SignalCode.C, Frequency.E06), + /** Signal-strength Beidou B3A for Rinex3. */ + S6D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.B3A), + /** Signal-strength QZSS L6E for Rinex3. */ S6E(MeasurementType.SIGNAL_STRENGTH, SignalCode.E, Frequency.J06), /** Signal-strength Beidou B3 I for Rinex3. */ S6I(MeasurementType.SIGNAL_STRENGTH, SignalCode.I, Frequency.B03), - /** Signal-strength Beidou B3 Q for Rinex3. */ - S6Q(MeasurementType.SIGNAL_STRENGTH, SignalCode.Q, Frequency.B03), - /** Signal-strength QZSS LEX(6) L for Rinex3. */ S6L(MeasurementType.SIGNAL_STRENGTH, SignalCode.L, Frequency.J06), + /** Signal-strength Beidou B3A for Rinex3. */ + S6P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.B3A), + + /** Signal-strength Beidou B3 Q for Rinex3. */ + S6Q(MeasurementType.SIGNAL_STRENGTH, SignalCode.Q, Frequency.B03), + /** Signal-strength QZSS LEX(6) S for Rinex3. */ S6S(MeasurementType.SIGNAL_STRENGTH, SignalCode.S, Frequency.J06), /** Signal-strength Galileo E6 B+C / QZSS LEX(6) S+L / Beidou B3 I+Q / GLONASS G2a L2CSI+L2OCp for Rinex3. */ S6X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.E06, Frequency.J06, Frequency.B03, Frequency.R06), - /** Signal-strength Galileo E6 A+B+C / QZSS L6(D+E) for Rinex3. */ - S6Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.E06, Frequency.J06), + /** Signal-strength Galileo E6 A+B+C / QZSS L6(D+E) / Beidou B3A for Rinex3. */ + S6Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.E06, Frequency.J06, Frequency.B3A), /** Signal-strength Beidou B2b Data for Rinex3. */ - S7D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.B02), + S7D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.B02, Frequency.B2B), /** Signal-strength Galileo E5b I I/NAV OS/CS/SoL / Beidou B2 I for Rinex3. */ S7I(MeasurementType.SIGNAL_STRENGTH, SignalCode.I, Frequency.E07, Frequency.B02), /** Signal-strength Beidou B2b Pilot for Rinex3. */ - S7P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.B02), + S7P(MeasurementType.SIGNAL_STRENGTH, SignalCode.P, Frequency.B02, Frequency.B2B), /** Signal-strength Galileo Q no data / Beidou B2 Q for Rinex3. */ S7Q(MeasurementType.SIGNAL_STRENGTH, SignalCode.Q, Frequency.E07, Frequency.B02), @@ -912,7 +948,7 @@ public enum ObservationType { S7X(MeasurementType.SIGNAL_STRENGTH, SignalCode.X, Frequency.E07, Frequency.B02), /** Signal-strength Beidou B2b Data+Pilot for Rinex3. */ - S7Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.B02), + S7Z(MeasurementType.SIGNAL_STRENGTH, SignalCode.Z, Frequency.B02, Frequency.B2B), /** Signal-strength Beidou B2(B2a+B2b) Data for Rinex3. */ S8D(MeasurementType.SIGNAL_STRENGTH, SignalCode.D, Frequency.B08), diff --git a/src/main/java/org/orekit/gnss/RinexObservationHeader.java b/src/main/java/org/orekit/gnss/RinexObservationHeader.java deleted file mode 100644 index 3ca1301e30..0000000000 --- a/src/main/java/org/orekit/gnss/RinexObservationHeader.java +++ /dev/null @@ -1,521 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.gnss; - -import java.util.List; - -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.geometry.euclidean.twod.Vector2D; -import org.orekit.gnss.RinexObservationLoader.Parser.PhaseShiftCorrection; -import org.orekit.time.AbsoluteDate; - -/** Container for Rinex observation file header. - * @since 9.2 - */ -public class RinexObservationHeader { - - /** Rinex Version. */ - private final double rinexVersion; - - /** Satellite System of the Rinex file (G/R/S/E/M). */ - private final SatelliteSystem satelliteSystem; - - /** Name of the Antenna Marker. */ - private final String markerName; - - /** Number of Antenna marker. */ - private final String markerNumber; - - /** Type of Antenna marker. */ - private String markerType; - - /** Name of Observer. */ - private final String observerName; - - /** Name of Agency. */ - private final String agencyName; - - /** Receiver Number. */ - private final String receiverNumber; - - /** Receiver Type. */ - private final String receiverType; - - /** Receiver version. */ - private final String receiverVersion; - - /** Antenna Number. */ - private final String antennaNumber; - - /** Antenna Type. */ - private final String antennaType; - - /** Approximate Marker Position (WGS84). */ - private final Vector3D approxPos; - - /** Antenna Height. */ - private final double antHeight; - - /** Eccentricities of antenna center. */ - private final Vector2D eccentricities; - - /** Position of antenna reference point for antenna on vehicle. */ - private Vector3D antRefPoint; - - /** Observation code of the average phasecenter position w/r to antenna reference point. */ - private String obsCode; - - /** Antenna phasecenter. - * North/East/Up (fixed station) or X/Y/Z in body fixed system (vehicle). */ - private Vector3D antPhaseCenter; - - /** Antenna B.Sight. - * Direction of the “vertical” antenna axis towards the GNSS satellites. */ - private Vector3D antBSight; - - /** Azimuth of the zero direction of a fixed antenna (degrees, from north). */ - private double antAzi; - - /** Zero direction of antenna. */ - private Vector3D antZeroDir; - - /** Current center of mass (X,Y,Z, meters) of vehicle in body fixed coordinate system. */ - private Vector3D centerMass; - - /** Unit of the carrier to noise ratio observables Snn (if present) DBHZ: S/N given in dbHz. */ - private String sigStrengthUnit; - - /** Observation interval in seconds. */ - private final double interval; - - /** Time of First observation record. */ - private final AbsoluteDate tFirstObs; - - /** Time of las observation record. */ - private final AbsoluteDate tLastObs; - - /** Realtime-derived receiver clock offset. */ - private final int clkOffset; - - /** List of applied differential code bias corrections. */ - private List listAppliedDCBS; - - /** List of antenna center variation corrections. */ - private List listAppliedPCVS; - - /** List of phase shift correction used to generate phases consistent w/r to cycle shifts. */ - private List phaseShiftCorrections; - - /** Number of leap seconds since 6-Jan-1980. */ - private final int leapSeconds; - - /** Future or past leap seconds ΔtLSF (BNK). - * i.e. future leap second if the week and day number are in the future. */ - private int leapSecondsFuture; - - /** Respective leap second week number. - * For GPS, GAL, QZS and IRN, weeks since 6-Jan-1980. - * When BDS only file leap seconds specified, weeks since 1-Jan-2006 */ - private int leapSecondsWeekNum; - - /** Respective leap second day number. */ - private int leapSecondsDayNum; - - /** Simple constructor, for Rinex 2 Header. - * @param rinexVersion rinex version - * @param satelliteSystem Satellite System of the observation file (G/R/S/E/M) - * @param markerName name of the antenna marker - * @param markerNumber number of the antenna marker - * @param markerType Type of Antenna marker - * @param observerName name of the observer - * @param agencyName name of the agency - * @param receiverNumber number of the receiver - * @param receiverType type of the receiver - * @param receiverVersion version of the receiver - * @param antennaNumber antenna number - * @param antennaType type of the antenna - * @param approxPos Approximate Marker Position (WGS84) - * @param antHeight antenna height - * @param eccentricities Eccentricities of antenna center - * @param antRefPoint Position of antenna reference point for antenna on vehicle - * @param antBSight Antenna B.Sight - * @param centerMass Current center of mass of vehicle in body fixed coordinate system - * @param interval Observation interval in seconds - * @param tFirstObs Time of First observation record - * @param tLastObs Time of last observation record - * @param clkOffset Realtime-derived receiver clock offset - * @param leapSeconds Number of leap seconds since 6-Jan-1980 - */ - public RinexObservationHeader(final double rinexVersion, final SatelliteSystem satelliteSystem, - final String markerName, final String markerNumber, final String markerType, - final String observerName, final String agencyName, final String receiverNumber, - final String receiverType, final String receiverVersion, final String antennaNumber, - final String antennaType, final Vector3D approxPos, final double antHeight, - final Vector2D eccentricities, final Vector3D antRefPoint, final Vector3D antBSight, - final Vector3D centerMass, final double interval, final AbsoluteDate tFirstObs, final AbsoluteDate tLastObs, - final int clkOffset, final int leapSeconds) { - this.rinexVersion = rinexVersion; - this.satelliteSystem = satelliteSystem; - this.markerName = markerName; - this.markerNumber = markerNumber; - this.markerType = markerType; - this.observerName = observerName; - this.agencyName = agencyName; - this.receiverNumber = receiverNumber; - this.receiverType = receiverType; - this.receiverVersion = receiverVersion; - this.antennaNumber = antennaNumber; - this.antennaType = antennaType; - this.approxPos = approxPos; - this.antHeight = antHeight; - this.eccentricities = eccentricities; - this.antRefPoint = antRefPoint; - this.antBSight = antBSight; - this.centerMass = centerMass; - this.interval = interval; - this.tFirstObs = tFirstObs; - this.tLastObs = tLastObs; - this.clkOffset = clkOffset; - this.leapSeconds = leapSeconds; - - } - - /** Simple constructor, for Rinex 3 Header. - * @param rinexVersion rinex version - * @param satelliteSystem Satellite System of the observation file (G/R/S/E/M) - * @param markerName name of the antenna marker - * @param markerNumber number of the antenna marker - * @param markerType Type of Antenna marker - * @param observerName name of the observer - * @param agencyName name of the agency - * @param receiverNumber number of the receiver - * @param receiverType type of the receiver - * @param receiverVersion version of the receiver - * @param antennaNumber antenna number - * @param antennaType type of the antenna - * @param approxPos Approximate Marker Position (WGS84) - * @param antHeight antenna height - * @param eccentricities Eccentricities of antenna center - * @param antRefPoint Position of antenna reference point for antenna on vehicle - * @param obsCode Observation code of the average phasecenter position w/r to antenna reference point - * @param antPhaseCenter Antenna phasecenter - * @param antBSight Antenna B.Sight - * @param antAzi Azimuth of the zero direction of a fixed antenna - * @param antZeroDir Zero direction of antenna - * @param centerMass Current center of mass of vehicle in body fixed coordinate system - * @param sigStrengthUnit Unit of the carrier to noise ratio observables - * @param interval Observation interval in seconds - * @param tFirstObs Time of First observation record - * @param tLastObs Time of last observation record - * @param clkOffset Realtime-derived receiver clock offset - * @param listAppliedDCBS List of applied differential code bias corrections - * @param listAppliedPCVS List of antenna center variation corrections - * @param phaseShiftCorrections List of phase shift correction used to generate phases consistent w/r to cycle shifts - * @param leapSeconds Number of leap seconds since 6-Jan-1980 - * @param leapSecondsFuture Future or past leap seconds - * @param leapSecondsWeekNum Respective leap second week number - * @param leapSecondsDayNum Respective leap second day number - */ - public RinexObservationHeader(final double rinexVersion, final SatelliteSystem satelliteSystem, - final String markerName, final String markerNumber, final String markerType, - final String observerName, final String agencyName, final String receiverNumber, - final String receiverType, final String receiverVersion, final String antennaNumber, - final String antennaType, final Vector3D approxPos, final double antHeight, - final Vector2D eccentricities, final Vector3D antRefPoint, final String obsCode, - final Vector3D antPhaseCenter, final Vector3D antBSight, final double antAzi, - final Vector3D antZeroDir, final Vector3D centerMass, final String sigStrengthUnit, - final double interval, final AbsoluteDate tFirstObs, final AbsoluteDate tLastObs, - final int clkOffset, final List listAppliedDCBS, - final List listAppliedPCVS, - final List phaseShiftCorrections, final int leapSeconds, - final int leapSecondsFuture, final int leapSecondsWeekNum, final int leapSecondsDayNum) { - this.rinexVersion = rinexVersion; - this.satelliteSystem = satelliteSystem; - this.markerName = markerName; - this.markerNumber = markerNumber; - this.observerName = observerName; - this.agencyName = agencyName; - this.receiverNumber = receiverNumber; - this.receiverType = receiverType; - this.receiverVersion = receiverVersion; - this.antennaNumber = antennaNumber; - this.antennaType = antennaType; - this.approxPos = approxPos; - this.antHeight = antHeight; - this.eccentricities = eccentricities; - this.clkOffset = clkOffset; - this.interval = interval; - this.tFirstObs = tFirstObs; - this.tLastObs = tLastObs; - this.leapSeconds = leapSeconds; - this.markerType = markerType; - this.sigStrengthUnit = sigStrengthUnit; - this.phaseShiftCorrections = phaseShiftCorrections; - this.obsCode = obsCode; - this.listAppliedDCBS = listAppliedDCBS; - this.listAppliedPCVS = listAppliedPCVS; - this.leapSecondsDayNum = leapSecondsDayNum; - this.leapSecondsFuture = leapSecondsFuture; - this.leapSecondsWeekNum = leapSecondsWeekNum; - this.centerMass = centerMass; - this.antAzi = antAzi; - this.antBSight = antBSight; - this.antZeroDir = antZeroDir; - this.antRefPoint = antRefPoint; - this.antPhaseCenter = antPhaseCenter; - - } - - /** Get Rinex Version. - * @return rinex version of the file - */ - public double getRinexVersion() { - return rinexVersion; - } - - /** Get Satellite System. - * @return satellite system of the observation file - */ - public SatelliteSystem getSatelliteSystem() { - return satelliteSystem; - } - - /** Get name of the antenna marker. - * @return name of the antenna marker - */ - public String getMarkerName() { - return markerName; - } - - /** Get number of the antenna marker. - * @return number of the antenna marker - */ - public String getMarkerNumber() { - return markerNumber; - } - - /** Get name of the observer. - * @return name of the observer - */ - public String getObserverName() { - return observerName; - } - - /** Get name of the agency. - * @return name of the agency - */ - public String getAgencyName() { - return agencyName; - } - - /** Get the number of the receiver. - * @return number of the receiver - */ - public String getReceiverNumber() { - return receiverNumber; - } - - /** Get the type of the receiver. - * @return type of the receiver - */ - public String getReceiverType() { - return receiverType; - } - - /** Get the version of the receiver. - * @return version of the receiver - */ - public String getReceiverVersion() { - return receiverVersion; - } - - /** Get the number of the antenna. - * @return number of the antenna - */ - public String getAntennaNumber() { - return antennaNumber; - } - - /** Get the type of the antenna. - * @return type of the antenna - */ - public String getAntennaType() { - return antennaType; - } - - /** Get the Approximate Marker Position. - * @return Approximate Marker Position - */ - public Vector3D getApproxPos() { - return approxPos; - } - - /** Get the antenna height. - * @return height of the antenna - */ - public double getAntennaHeight() { - return antHeight; - } - - /** Get the eccentricities of antenna center. - * @return Eccentricities of antenna center - */ - public Vector2D getEccentricities() { - return eccentricities; - } - - /** Get the realtime-derived receiver clock offset. - * @return realtime-derived receiver clock offset - */ - public int getClkOffset() { - return clkOffset; - } - - /** Get the observation interval in seconds. - * @return Observation interval in seconds - */ - public double getInterval() { - return interval; - } - - /** Get the time of First observation record. - * @return Time of First observation record - */ - public AbsoluteDate getTFirstObs() { - return tFirstObs; - } - - /** Get the time of last observation record. - * @return Time of last observation record - */ - public AbsoluteDate getTLastObs() { - return tLastObs; - } - - /** Get the Number of leap seconds since 6-Jan-1980. - * @return Number of leap seconds since 6-Jan-1980 - */ - public int getLeapSeconds() { - return leapSeconds; - } - - /** Get type of the antenna marker. - * @return type of the antenna marker - */ - public String getMarkerType() { - return markerType; - } - - /** Get the position of antenna reference point for antenna on vehicle. - * @return Position of antenna reference point for antenna on vehicle - */ - public Vector3D getAntennaReferencePoint() { - return antRefPoint; - } - - /** Get the observation code of the average phasecenter position w/r to antenna reference point. - * @return Observation code of the average phasecenter position w/r to antenna reference point - */ - public String getObservationCode() { - return obsCode; - } - - /** Get the antenna phasecenter. - * @return Antenna phasecenter - */ - public Vector3D getAntennaPhaseCenter() { - return antPhaseCenter; - } - - /** Get the antenna B.Sight. - * @return Antenna B.Sight - */ - public Vector3D getAntennaBSight() { - return antBSight; - } - - /** Get the azimuth of the zero direction of a fixed antenna. - * @return Azimuth of the zero direction of a fixed antenna - */ - public double getAntennaAzimuth() { - return antAzi; - } - - /** Get the zero direction of antenna. - * @return Zero direction of antenna - */ - public Vector3D getAntennaZeroDirection() { - return antZeroDir; - } - - /** Get the current center of mass of vehicle in body fixed coordinate system. - * @return Current center of mass of vehicle in body fixed coordinate system - */ - public Vector3D getCenterMass() { - return centerMass; - } - - /** Get the unit of the carrier to noise ratio observables. - * @return Unit of the carrier to noise ratio observables - */ - public String getSignalStrengthUnit() { - return sigStrengthUnit; - } - - /** Get the future or past leap seconds. - * @return Future or past leap seconds - */ - public int getLeapSecondsFuture() { - return leapSecondsFuture; - } - - /** Get the respective leap second week number. - * @return Respective leap second week number - */ - public int getLeapSecondsWeekNum() { - return leapSecondsWeekNum; - } - - /** Get the respective leap second day number. - * @return Respective leap second day number - */ - public int getLeapSecondsDayNum() { - return leapSecondsDayNum; - } - - /** Get the list of applied differential code bias corrections. - * @return list of applied differential code bias corrections - */ - public List getListAppliedDCBS() { - return listAppliedDCBS; - } - - /** Get the list of antenna center variation corrections. - * @return List of antenna center variation corrections - */ - public List getListAppliedPCVS() { - return listAppliedPCVS; - } - - /** Get the list of phase shift correction used to generate phases consistent w/r to cycle shifts. - * @return List of phase shift correction used to generate phases consistent w/r to cycle shifts - */ - public List getPhaseShiftCorrections() { - return phaseShiftCorrections; - } - -} diff --git a/src/main/java/org/orekit/gnss/RinexObservationLoader.java b/src/main/java/org/orekit/gnss/RinexObservationLoader.java deleted file mode 100644 index 22bffff677..0000000000 --- a/src/main/java/org/orekit/gnss/RinexObservationLoader.java +++ /dev/null @@ -1,1247 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.gnss; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.hipparchus.exception.DummyLocalizable; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.geometry.euclidean.twod.Vector2D; -import org.hipparchus.util.FastMath; -import org.orekit.annotation.DefaultDataContext; -import org.orekit.data.DataContext; -import org.orekit.data.DataLoader; -import org.orekit.data.DataProvidersManager; -import org.orekit.data.DataSource; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeScale; -import org.orekit.time.TimeScales; - -/** Loader for Rinex measurements files. - *

        - * Supported versions are: 2.00, 2.10, 2.11, 2.12 (unofficial), 2.20 (unofficial), - * 3.00, 3.01, 3.02, 3.03, and 3.04. - *

        - * @see rinex 2.0 - * @see rinex 2.10 - * @see rinex 2.11 - * @see unofficial rinex 2.12 - * @see unofficial rinex 2.20 - * @see rinex 3.00 - * @see rinex 3.01 - * @see rinex 3.02 - * @see rinex 3.03 - * @see rinex 3.04 - * @since 9.2 - */ -public class RinexObservationLoader { - - /** Default supported files name pattern for rinex 2 observation files. */ - public static final String DEFAULT_RINEX_2_SUPPORTED_NAMES = "^\\w{4}\\d{3}[0a-x](?:\\d{2})?\\.\\d{2}[oO]$"; - - /** Default supported files name pattern for rinex 3 observation files. */ - public static final String DEFAULT_RINEX_3_SUPPORTED_NAMES = "^\\w{9}_\\w{1}_\\d{11}_\\d{2}\\w_\\d{2}\\w{1}_\\w{2}\\.rnx$"; - - // CHECKSTYLE: stop JavadocVariable check - private static final String RINEX_VERSION_TYPE = "RINEX VERSION / TYPE"; - private static final String COMMENT = "COMMENT"; - private static final String PGM_RUN_BY_DATE = "PGM / RUN BY / DATE"; - private static final String MARKER_NAME = "MARKER NAME"; - private static final String MARKER_NUMBER = "MARKER NUMBER"; - private static final String MARKER_TYPE = "MARKER TYPE"; - private static final String OBSERVER_AGENCY = "OBSERVER / AGENCY"; - private static final String REC_NB_TYPE_VERS = "REC # / TYPE / VERS"; - private static final String ANT_NB_TYPE = "ANT # / TYPE"; - private static final String APPROX_POSITION_XYZ = "APPROX POSITION XYZ"; - private static final String ANTENNA_DELTA_H_E_N = "ANTENNA: DELTA H/E/N"; - private static final String ANTENNA_DELTA_X_Y_Z = "ANTENNA: DELTA X/Y/Z"; - private static final String ANTENNA_PHASECENTER = "ANTENNA: PHASECENTER"; - private static final String ANTENNA_B_SIGHT_XYZ = "ANTENNA: B.SIGHT XYZ"; - private static final String ANTENNA_ZERODIR_AZI = "ANTENNA: ZERODIR AZI"; - private static final String ANTENNA_ZERODIR_XYZ = "ANTENNA: ZERODIR XYZ"; - private static final String NB_OF_SATELLITES = "# OF SATELLITES"; - private static final String WAVELENGTH_FACT_L1_2 = "WAVELENGTH FACT L1/2"; - private static final String RCV_CLOCK_OFFS_APPL = "RCV CLOCK OFFS APPL"; - private static final String INTERVAL = "INTERVAL"; - private static final String TIME_OF_FIRST_OBS = "TIME OF FIRST OBS"; - private static final String TIME_OF_LAST_OBS = "TIME OF LAST OBS"; - private static final String LEAP_SECONDS = "LEAP SECONDS"; - private static final String PRN_NB_OF_OBS = "PRN / # OF OBS"; - private static final String NB_TYPES_OF_OBSERV = "# / TYPES OF OBSERV"; - private static final String END_OF_HEADER = "END OF HEADER"; - private static final String CENTER_OF_MASS_XYZ = "CENTER OF MASS: XYZ"; - private static final String SIGNAL_STRENGTH_UNIT = "SIGNAL STRENGTH UNIT"; - private static final String SYS_NB_OBS_TYPES = "SYS / # / OBS TYPES"; - private static final String SYS_DCBS_APPLIED = "SYS / DCBS APPLIED"; - private static final String SYS_PCVS_APPLIED = "SYS / PCVS APPLIED"; - private static final String SYS_SCALE_FACTOR = "SYS / SCALE FACTOR"; - private static final String SYS_PHASE_SHIFT = "SYS / PHASE SHIFT"; - private static final String SYS_PHASE_SHIFTS = "SYS / PHASE SHIFTS"; - private static final String GLONASS_SLOT_FRQ_NB = "GLONASS SLOT / FRQ #"; - private static final String GLONASS_COD_PHS_BIS = "GLONASS COD/PHS/BIS"; - private static final String OBS_SCALE_FACTOR = "OBS SCALE FACTOR"; - - private static final String GPS = "GPS"; - private static final String GAL = "GAL"; - private static final String GLO = "GLO"; - private static final String QZS = "QZS"; - private static final String BDT = "BDT"; - private static final String IRN = "IRN"; - // CHECKSTYLE: resume JavadocVariable check - - /** Rinex Observations. */ - private final List observationDataSets; - - /** Set of time scales. */ - private final TimeScales timeScales; - - /** Simple constructor. - *

        - * This constructor is used when the rinex files are managed by the - * global {@link DataContext#getDefault() default data context}. - *

        - * @param supportedNames regular expression for supported files names - * @see #RinexObservationLoader(String, DataProvidersManager, TimeScales) - */ - @DefaultDataContext - public RinexObservationLoader(final String supportedNames) { - this(supportedNames, DataContext.getDefault().getDataProvidersManager(), - DataContext.getDefault().getTimeScales()); - } - - /** - * Create a RINEX loader/parser with the given source of RINEX auxiliary data files. - * - *

        - * This constructor is used when the rinex files are managed by the given - * {@code dataProvidersManager}. - *

        - * @param supportedNames regular expression for supported files names - * @param dataProvidersManager provides access to auxiliary data. - * @param timeScales the set of time scales to use when parsing dates. - * @since 10.1 - */ - public RinexObservationLoader(final String supportedNames, - final DataProvidersManager dataProvidersManager, - final TimeScales timeScales) { - observationDataSets = new ArrayList<>(); - this.timeScales = timeScales; - dataProvidersManager.feed(supportedNames, new Parser()); - } - - /** Simple constructor. This constructor uses the {@link DataContext#getDefault() - * default data context}. - * - * @param source source for the RINEX data - * @see #RinexObservationLoader(DataSource, TimeScales) - */ - @DefaultDataContext - public RinexObservationLoader(final DataSource source) { - this(source, DataContext.getDefault().getTimeScales()); - } - - /** - * Loads RINEX from the given input stream using the specified auxiliary data. - * - * @param source source for the RINEX data - * @param timeScales the set of time scales to use when parsing dates. - * @since 10.1 - */ - public RinexObservationLoader(final DataSource source, final TimeScales timeScales) { - try { - this.timeScales = timeScales; - observationDataSets = new ArrayList<>(); - try (InputStream is = source.getOpener().openStreamOnce(); - BufferedInputStream bis = new BufferedInputStream(is)) { - new Parser().loadData(bis, source.getName()); - } - } catch (IOException ioe) { - throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage())); - } - } - - /** Get parsed rinex observations data sets. - * @return unmodifiable view of parsed rinex observations - * @since 9.3 - */ - public List getObservationDataSets() { - return Collections.unmodifiableList(observationDataSets); - } - - /** Parser for rinex files. - */ - public class Parser implements DataLoader { - - /** Index of label in data lines. */ - private static final int LABEL_START = 60; - - /** File type accepted (only Observation Data). */ - private static final String FILE_TYPE = "O"; //Only Observation Data files - - /** Name of the file. */ - private String name; - - /** Current line. */ - private String line; - - /** current line number. */ - private int lineNumber; - - /** {@inheritDoc} */ - @Override - public boolean stillAcceptsData() { - // we load all rinex files we can find - return true; - } - - /** {@inheritDoc} */ - @Override - public void loadData(final InputStream input, final String fileName) - throws IOException, OrekitException { - - try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { - - this.name = fileName; - this.line = null; - this.lineNumber = 0; - - // placeholders for parsed data - SatelliteSystem satelliteSystem = null; - double formatVersion = Double.NaN; - boolean inRinexVersion = false; - SatelliteSystem obsTypesSystem = null; - String markerName = null; - String markerNumber = null; - String markerType = null; - String observerName = null; - String agencyName = null; - String receiverNumber = null; - String receiverType = null; - String receiverVersion = null; - String antennaNumber = null; - String antennaType = null; - Vector3D approxPos = null; - Vector3D antRefPoint = null; - String obsCode = null; - Vector3D antPhaseCenter = null; - Vector3D antBSight = null; - double antAzi = Double.NaN; - Vector3D antZeroDir = null; - Vector3D centerMass = null; - double antHeight = Double.NaN; - Vector2D eccentricities = Vector2D.ZERO; - int clkOffset = -1; - int nbTypes = -1; - int nbSat = -1; - double interval = Double.NaN; - AbsoluteDate tFirstObs = AbsoluteDate.PAST_INFINITY; - AbsoluteDate tLastObs = AbsoluteDate.FUTURE_INFINITY; - TimeScale timeScale = null; - String timeScaleStr = null; - int leapSeconds = 0; - AbsoluteDate tObs = AbsoluteDate.PAST_INFINITY; - String[] satsObsList = null; - int eventFlag = -1; - int nbSatObs = -1; - int nbLinesSat = -1; - double rcvrClkOffset = 0; - boolean inMarkerName = false; - boolean inObserver = false; - boolean inRecType = false; - boolean inAntType = false; - boolean inAproxPos = false; - boolean inAntDelta = false; - boolean inTypesObs = false; - boolean inFirstObs = false; - boolean inPhaseShift = false; - RinexObservationHeader rinexHeader = null; - int scaleFactor = 1; - int nbObsScaleFactor = 0; - final List scaleFactorCorrections = new ArrayList<>(); - final Map> listTypeObs = new HashMap<>(); - - //First line must always contain Rinex Version, File Type and Satellite Systems Observed - readLine(reader, true); - if (line.length() < LABEL_START || !RINEX_VERSION_TYPE.equals(line.substring(LABEL_START).trim())) { - throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name); - } - formatVersion = parseDouble(0, 9); - final int format100 = (int) FastMath.rint(100 * formatVersion); - - if (format100 != 200 && format100 != 210 && format100 != 211 && - format100 != 212 && format100 != 220 && format100 != 300 && - format100 != 301 && format100 != 302 && format100 != 303 && - format100 != 304) { - throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name); - } - - //File Type must be Observation_Data - if (!(parseString(20, 1)).equals(FILE_TYPE)) { - throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name); - } - satelliteSystem = SatelliteSystem.parseSatelliteSystem(parseString(40, 1)); - inRinexVersion = true; - - switch (format100 / 100) { - case 2: { - - final int MAX_OBS_TYPES_PER_LINE_RNX2 = 9; - final int MAX_N_SAT_OBSERVATION = 12; - final int MAX_N_TYPES_OBSERVATION = 5; - final int MAX_OBS_TYPES_SCALE_FACTOR = 8; - final List typesObs = new ArrayList<>(); - - while (readLine(reader, false)) { - - if (rinexHeader == null) { - switch (line.substring(LABEL_START).trim()) { - case COMMENT : - // nothing to do - break; - case PGM_RUN_BY_DATE : - // nothing to do - break; - case MARKER_NAME : - markerName = parseString(0, 60); - inMarkerName = true; - break; - case MARKER_NUMBER : - markerNumber = parseString(0, 20); - break; - case MARKER_TYPE : - markerType = parseString(0, 20); - break; - case OBSERVER_AGENCY : - observerName = parseString(0, 20); - agencyName = parseString(20, 40); - inObserver = true; - break; - case REC_NB_TYPE_VERS : - receiverNumber = parseString(0, 20); - receiverType = parseString(20, 20); - receiverVersion = parseString(40, 20); - inRecType = true; - break; - case ANT_NB_TYPE : - antennaNumber = parseString(0, 20); - antennaType = parseString(20, 20); - inAntType = true; - break; - case APPROX_POSITION_XYZ : - approxPos = new Vector3D(parseDouble(0, 14), parseDouble(14, 14), - parseDouble(28, 14)); - inAproxPos = true; - break; - case ANTENNA_DELTA_H_E_N : - antHeight = parseDouble(0, 14); - eccentricities = new Vector2D(parseDouble(14, 14), parseDouble(28, 14)); - inAntDelta = true; - break; - case ANTENNA_DELTA_X_Y_Z : - antRefPoint = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case ANTENNA_B_SIGHT_XYZ : - antBSight = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case CENTER_OF_MASS_XYZ : - centerMass = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case NB_OF_SATELLITES : - nbSat = parseInt(0, 6); - break; - case WAVELENGTH_FACT_L1_2 : - //Optional line in header - //Not stored for now - break; - case RCV_CLOCK_OFFS_APPL : - clkOffset = parseInt(0, 6); - break; - case INTERVAL : - interval = parseDouble(0, 10); - break; - case TIME_OF_FIRST_OBS : - switch (satelliteSystem) { - case GPS: - timeScale = timeScales.getGPS(); - break; - case GALILEO: - timeScale = timeScales.getGST(); - break; - case GLONASS: - timeScale = timeScales.getGLONASS(); - break; - case MIXED: - //in Case of Mixed data, Timescale must be specified in the Time of First line - timeScaleStr = parseString(48, 3); - - if (timeScaleStr.equals(GPS)) { - timeScale = timeScales.getGPS(); - } else if (timeScaleStr.equals(GAL)) { - timeScale = timeScales.getGST(); - } else if (timeScaleStr.equals(GLO)) { - timeScale = timeScales.getGLONASS(); - } else { - throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name); - } - break; - default : - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - - tFirstObs = new AbsoluteDate(parseInt(0, 6), - parseInt(6, 6), - parseInt(12, 6), - parseInt(18, 6), - parseInt(24, 6), - parseDouble(30, 13), timeScale); - inFirstObs = true; - break; - case TIME_OF_LAST_OBS : - tLastObs = new AbsoluteDate(parseInt(0, 6), - parseInt(6, 6), - parseInt(12, 6), - parseInt(18, 6), - parseInt(24, 6), - parseDouble(30, 13), timeScale); - break; - case LEAP_SECONDS : - leapSeconds = parseInt(0, 6); - break; - case PRN_NB_OF_OBS : - //Optional line in header, indicates number of Observations par Satellite - //Not stored for now - break; - case NB_TYPES_OF_OBSERV : - nbTypes = parseInt(0, 6); - final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX2 - 1 ) / MAX_OBS_TYPES_PER_LINE_RNX2; - - for (int j = 0; j < nbLinesTypesObs; j++) { - if (j > 0) { - readLine(reader, true); - } - final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX2, nbTypes - typesObs.size()); - for (int i = 0; i < iMax; i++) { - try { - typesObs.add(ObservationType.valueOf(parseString(10 + (6 * i), 2))); - } catch (IllegalArgumentException iae) { - throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY, - parseString(10 + (6 * i), 2), name, lineNumber); - } - } - } - inTypesObs = true; - break; - case OBS_SCALE_FACTOR : - scaleFactor = FastMath.max(1, parseInt(0, 6)); - nbObsScaleFactor = parseInt(6, 6); - if (nbObsScaleFactor > MAX_OBS_TYPES_SCALE_FACTOR) { - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - final List typesObsScaleFactor = new ArrayList<>(nbObsScaleFactor); - for (int i = 0; i < nbObsScaleFactor; i++) { - typesObsScaleFactor.add(ObservationType.valueOf(parseString(16 + (6 * i), 2))); - } - scaleFactorCorrections.add(new ScaleFactorCorrection(satelliteSystem, - scaleFactor, typesObsScaleFactor)); - break; - case END_OF_HEADER : - //We make sure that we have read all the mandatory fields inside the header of the Rinex - if (!inRinexVersion || !inMarkerName || - !inObserver || !inRecType || !inAntType || - formatVersion < 2.20 && !inAproxPos || - formatVersion < 2.20 && !inAntDelta || - !inTypesObs || !inFirstObs) { - throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name); - } - - //Header information gathered - rinexHeader = new RinexObservationHeader(formatVersion, satelliteSystem, - markerName, markerNumber, markerType, observerName, - agencyName, receiverNumber, receiverType, - receiverVersion, antennaNumber, antennaType, - approxPos, antHeight, eccentricities, - antRefPoint, antBSight, centerMass, interval, - tFirstObs, tLastObs, clkOffset, leapSeconds); - break; - default : - if (rinexHeader == null) { - //There must be an error due to an unknown Label inside the Header - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - } - } else { - - //Start of a new Observation - rcvrClkOffset = 0; - nbLinesSat = -1; - eventFlag = -1; - nbSatObs = -1; - satsObsList = null; - tObs = null; - - eventFlag = parseInt(28, 1); - //If eventFlag>1, we skip the corresponding lines to the next observation - if (eventFlag > 1) { - if (eventFlag == 6) { - nbSatObs = parseInt(29, 3); - nbLinesSat = (nbSatObs + 12 - 1) / 12; - final int nbLinesObs = (nbTypes + 5 - 1) / 5; - final int nbLinesSkip = (nbLinesSat - 1) + nbSatObs * nbLinesObs; - for (int i = 0; i < nbLinesSkip; i++) { - readLine(reader, true); - } - } else { - final int nbLinesSkip = parseInt(29, 3); - for (int i = 0; i < nbLinesSkip; i++) { - readLine(reader, true); - } - } - } else { - - int y = parseInt(0, 3); - if (79 < y && y <= 99) { - y += 1900; - } else if (0 <= y && y <= 79) { - y += 2000; - } - tObs = new AbsoluteDate(y, - parseInt(3, 3), - parseInt(6, 3), - parseInt(9, 3), - parseInt(12, 3), - parseDouble(15, 11), timeScale); - - nbSatObs = parseInt(29, 3); - satsObsList = new String[nbSatObs]; - //If the total number of satellites was indicated in the Header - if (nbSat != -1 && nbSatObs > nbSat) { - //we check that the number of Sat in the observation is consistent - throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS, - lineNumber, name, nbSatObs, nbSat); - } - - nbLinesSat = (nbSatObs + MAX_N_SAT_OBSERVATION - 1) / MAX_N_SAT_OBSERVATION; - for (int j = 0; j < nbLinesSat; j++) { - if (j > 0) { - readLine(reader, true); - } - final int iMax = FastMath.min(MAX_N_SAT_OBSERVATION, nbSatObs - j * MAX_N_SAT_OBSERVATION); - for (int i = 0; i < iMax; i++) { - satsObsList[i + MAX_N_SAT_OBSERVATION * j] = parseString(32 + 3 * i, 3); - } - - //Read the Receiver Clock offset, if present - rcvrClkOffset = parseDouble(68, 12); - if (Double.isNaN(rcvrClkOffset)) { - rcvrClkOffset = 0.0; - } - - } - - //For each one of the Satellites in this observation - final int nbLinesObs = (nbTypes + MAX_N_TYPES_OBSERVATION - 1) / MAX_N_TYPES_OBSERVATION; - for (int k = 0; k < nbSatObs; k++) { - - - //Once the Date and Satellites list is read: - // - to read the Data for each satellite - // - 5 Observations per line - final List observationData = new ArrayList<>(nbSatObs); - for (int j = 0; j < nbLinesObs; j++) { - readLine(reader, true); - final int iMax = FastMath.min(MAX_N_TYPES_OBSERVATION, nbTypes - observationData.size()); - for (int i = 0; i < iMax; i++) { - final ObservationType type = typesObs.get(observationData.size()); - double value = parseDouble(16 * i, 14); - boolean scaleFactorFound = false; - //We look for the lines of ScaledFactorCorrections - for (int l = 0; l < scaleFactorCorrections.size() && !scaleFactorFound; ++l) { - //We check if the next Observation Type to read needs to be scaled - if (scaleFactorCorrections.get(l).getTypesObsScaled().contains(type)) { - value /= scaleFactorCorrections.get(l).getCorrection(); - scaleFactorFound = true; - } - } - observationData.add(new ObservationData(type, - value, - parseInt(14 + 16 * i, 1), - parseInt(15 + 16 * i, 1))); - } - } - - //We check that the Satellite type is consistent with Satellite System in the top of the file - final SatelliteSystem satelliteSystemSat; - final int id; - if (satsObsList[k].length() < 3) { - // missing satellite system, we use the global one - satelliteSystemSat = satelliteSystem; - id = Integer.parseInt(satsObsList[k]); - } else { - satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(satsObsList[k]); - id = Integer.parseInt(satsObsList[k].substring(1, 3).trim()); - } - if (!satelliteSystem.equals(SatelliteSystem.MIXED)) { - if (!satelliteSystemSat.equals(satelliteSystem)) { - throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM, - lineNumber, name, satelliteSystem, satelliteSystemSat); - } - } - - final int prnNumber; - switch (satelliteSystemSat) { - case GPS: - case GLONASS: - case GALILEO: - prnNumber = id; - break; - case SBAS: - prnNumber = id + 100; - break; - default: - // MIXED satellite system is not allowed here - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - - observationDataSets.add(new ObservationDataSet(rinexHeader, satelliteSystemSat, prnNumber, - tObs, rcvrClkOffset, observationData)); - - } - } - } - } - break; - } - case 3: { - - final int MAX_OBS_TYPES_PER_LINE_RNX3 = 13; - final int MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE = 12; - final int MAX_N_SAT_PHSHIFT_PER_LINE = 10; - - final List typeObs = new ArrayList<>(); - String sigStrengthUnit = null; - int leapSecondsFuture = 0; - int leapSecondsWeekNum = 0; - int leapSecondsDayNum = 0; - final List listAppliedDCBs = new ArrayList<>(); - final List listAppliedPCVS = new ArrayList<>(); - SatelliteSystem satSystemScaleFactor = null; - String[] satsPhaseShift = null; - int nbSatPhaseShift = 0; - SatelliteSystem satSystemPhaseShift = null; - double corrPhaseShift = 0.0; - final List phaseShiftCorrections = new ArrayList<>(); - ObservationType phaseShiftTypeObs = null; - - - while (readLine(reader, false)) { - if (rinexHeader == null) { - switch (line.substring(LABEL_START).trim()) { - case COMMENT : - // nothing to do - break; - case PGM_RUN_BY_DATE : - // nothing to do - break; - case MARKER_NAME : - markerName = parseString(0, 60); - inMarkerName = true; - break; - case MARKER_NUMBER : - markerNumber = parseString(0, 20); - break; - case MARKER_TYPE : - markerType = parseString(0, 20); - //Could be done with an Enumeration - break; - case OBSERVER_AGENCY : - observerName = parseString(0, 20); - agencyName = parseString(20, 40); - inObserver = true; - break; - case REC_NB_TYPE_VERS : - receiverNumber = parseString(0, 20); - receiverType = parseString(20, 20); - receiverVersion = parseString(40, 20); - inRecType = true; - break; - case ANT_NB_TYPE : - antennaNumber = parseString(0, 20); - antennaType = parseString(20, 20); - inAntType = true; - break; - case APPROX_POSITION_XYZ : - approxPos = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - inAproxPos = true; - break; - case ANTENNA_DELTA_H_E_N : - antHeight = parseDouble(0, 14); - eccentricities = new Vector2D(parseDouble(14, 14), - parseDouble(28, 14)); - inAntDelta = true; - break; - case ANTENNA_DELTA_X_Y_Z : - antRefPoint = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case ANTENNA_PHASECENTER : - obsCode = parseString(2, 3); - antPhaseCenter = new Vector3D(parseDouble(5, 9), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case ANTENNA_B_SIGHT_XYZ : - antBSight = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case ANTENNA_ZERODIR_AZI : - antAzi = parseDouble(0, 14); - break; - case ANTENNA_ZERODIR_XYZ : - antZeroDir = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case CENTER_OF_MASS_XYZ : - centerMass = new Vector3D(parseDouble(0, 14), - parseDouble(14, 14), - parseDouble(28, 14)); - break; - case NB_OF_SATELLITES : - nbSat = parseInt(0, 6); - break; - case RCV_CLOCK_OFFS_APPL : - clkOffset = parseInt(0, 6); - break; - case INTERVAL : - interval = parseDouble(0, 10); - break; - case TIME_OF_FIRST_OBS : - switch (satelliteSystem) { - case GPS: - timeScale = timeScales.getGPS(); - break; - case GALILEO: - timeScale = timeScales.getGST(); - break; - case GLONASS: - timeScale = timeScales.getGLONASS(); - break; - case QZSS: - timeScale = timeScales.getQZSS(); - break; - case BEIDOU: - timeScale = timeScales.getBDT(); - break; - case IRNSS: - timeScale = timeScales.getIRNSS(); - break; - case MIXED: - //in Case of Mixed data, Timescale must be specified in the Time of First line - timeScaleStr = parseString(48, 3); - - if (timeScaleStr.equals(GPS)) { - timeScale = timeScales.getGPS(); - } else if (timeScaleStr.equals(GAL)) { - timeScale = timeScales.getGST(); - } else if (timeScaleStr.equals(GLO)) { - timeScale = timeScales.getGLONASS(); - } else if (timeScaleStr.equals(QZS)) { - timeScale = timeScales.getQZSS(); - } else if (timeScaleStr.equals(BDT)) { - timeScale = timeScales.getBDT(); - } else if (timeScaleStr.equals(IRN)) { - timeScale = timeScales.getIRNSS(); - } else { - throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name); - } - break; - default : - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - - tFirstObs = new AbsoluteDate(parseInt(0, 6), - parseInt(6, 6), - parseInt(12, 6), - parseInt(18, 6), - parseInt(24, 6), - parseDouble(30, 13), timeScale); - inFirstObs = true; - break; - case TIME_OF_LAST_OBS : - tLastObs = new AbsoluteDate(parseInt(0, 6), - parseInt(6, 6), - parseInt(12, 6), - parseInt(18, 6), - parseInt(24, 6), - parseDouble(30, 13), timeScale); - break; - case LEAP_SECONDS : - leapSeconds = parseInt(0, 6); - leapSecondsFuture = parseInt(6, 6); - leapSecondsWeekNum = parseInt(12, 6); - leapSecondsDayNum = parseInt(18, 6); - //Time System Identifier must be added, last A3 String - break; - case PRN_NB_OF_OBS : - //Optional line in header, indicates number of Observations par Satellite - //Not stored for now - break; - case SYS_NB_OBS_TYPES : - obsTypesSystem = null; - typeObs.clear(); - - obsTypesSystem = SatelliteSystem.parseSatelliteSystem(parseString(0, 1)); - nbTypes = parseInt(3, 3); - - final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX3 - 1) / MAX_OBS_TYPES_PER_LINE_RNX3; - for (int j = 0; j < nbLinesTypesObs; j++) { - if (j > 0) { - readLine(reader, true); - } - final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX3, nbTypes - typeObs.size()); - for (int i = 0; i < iMax; i++) { - try { - typeObs.add(ObservationType.valueOf(parseString(7 + (4 * i), 3))); - } catch (IllegalArgumentException iae) { - throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY, - parseString(7 + (4 * i), 3), name, lineNumber); - } - } - } - listTypeObs.put(obsTypesSystem, new ArrayList<>(typeObs)); - inTypesObs = true; - break; - case SIGNAL_STRENGTH_UNIT : - sigStrengthUnit = parseString(0, 20); - break; - case SYS_DCBS_APPLIED : - - listAppliedDCBs.add(new AppliedDCBS(SatelliteSystem.parseSatelliteSystem(parseString(0, 1)), - parseString(2, 17), parseString(20, 40))); - break; - case SYS_PCVS_APPLIED : - - listAppliedPCVS.add(new AppliedPCVS(SatelliteSystem.parseSatelliteSystem(parseString(0, 1)), - parseString(2, 17), parseString(20, 40))); - break; - case SYS_SCALE_FACTOR : - satSystemScaleFactor = null; - scaleFactor = 1; - nbObsScaleFactor = 0; - - satSystemScaleFactor = SatelliteSystem.parseSatelliteSystem(parseString(0, 1)); - scaleFactor = parseInt(2, 4); - nbObsScaleFactor = parseInt(8, 2); - final List typesObsScaleFactor = new ArrayList<>(nbObsScaleFactor); - - if (nbObsScaleFactor == 0) { - typesObsScaleFactor.addAll(listTypeObs.get(satSystemScaleFactor)); - } else { - final int nbLinesTypesObsScaleFactor = (nbObsScaleFactor + MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE - 1) / - MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE; - for (int j = 0; j < nbLinesTypesObsScaleFactor; j++) { - if ( j > 0) { - readLine(reader, true); - } - final int iMax = FastMath.min(MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE, nbObsScaleFactor - typesObsScaleFactor.size()); - for (int i = 0; i < iMax; i++) { - typesObsScaleFactor.add(ObservationType.valueOf(parseString(11 + (4 * i), 3))); - } - } - } - - scaleFactorCorrections.add(new ScaleFactorCorrection(satSystemScaleFactor, - scaleFactor, typesObsScaleFactor)); - break; - case SYS_PHASE_SHIFT : - case SYS_PHASE_SHIFTS : { - - nbSatPhaseShift = 0; - satsPhaseShift = null; - corrPhaseShift = 0.0; - phaseShiftTypeObs = null; - satSystemPhaseShift = null; - - satSystemPhaseShift = SatelliteSystem.parseSatelliteSystem(parseString(0, 1)); - final String to = parseString(2, 3); - phaseShiftTypeObs = to.isEmpty() ? - null : - ObservationType.valueOf(to.length() < 3 ? "L" + to : to); - nbSatPhaseShift = parseInt(16, 2); - corrPhaseShift = parseDouble(6, 8); - - if (nbSatPhaseShift == 0) { - //If nbSat with Phase Shift is not indicated: all the satellites are affected for this Obs Type - } else { - satsPhaseShift = new String[nbSatPhaseShift]; - final int nbLinesSatPhaseShift = (nbSatPhaseShift + MAX_N_SAT_PHSHIFT_PER_LINE - 1) / MAX_N_SAT_PHSHIFT_PER_LINE; - for (int j = 0; j < nbLinesSatPhaseShift; j++) { - if (j > 0) { - readLine(reader, true); - } - final int iMax = FastMath.min(MAX_N_SAT_PHSHIFT_PER_LINE, nbSatPhaseShift - j * MAX_N_SAT_PHSHIFT_PER_LINE); - for (int i = 0; i < iMax; i++) { - satsPhaseShift[i + 10 * j] = parseString(19 + 4 * i, 3); - } - } - } - phaseShiftCorrections.add(new PhaseShiftCorrection(satSystemPhaseShift, - phaseShiftTypeObs, - corrPhaseShift, - satsPhaseShift)); - inPhaseShift = true; - break; - } - case GLONASS_SLOT_FRQ_NB : - // Not defined yet - break; - case GLONASS_COD_PHS_BIS : - // Not defined yet - break; - case END_OF_HEADER : - //We make sure that we have read all the mandatory fields inside the header of the Rinex - if (!inRinexVersion || !inMarkerName || - !inObserver || !inRecType || !inAntType || - !inAntDelta || !inTypesObs || !inFirstObs || - formatVersion >= 3.01 && !inPhaseShift) { - throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name); - } - - //Header information gathered - rinexHeader = new RinexObservationHeader(formatVersion, satelliteSystem, - markerName, markerNumber, markerType, - observerName, agencyName, receiverNumber, - receiverType, receiverVersion, antennaNumber, - antennaType, approxPos, antHeight, eccentricities, - antRefPoint, obsCode, antPhaseCenter, antBSight, - antAzi, antZeroDir, centerMass, sigStrengthUnit, - interval, tFirstObs, tLastObs, clkOffset, listAppliedDCBs, - listAppliedPCVS, phaseShiftCorrections, leapSeconds, - leapSecondsFuture, leapSecondsWeekNum, leapSecondsDayNum); - break; - default : - if (rinexHeader == null) { - //There must be an error due to an unknown Label inside the Header - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - } - } else { - //If End of Header - - //Start of a new Observation - rcvrClkOffset = 0; - eventFlag = -1; - nbSatObs = -1; - tObs = null; - - //A line that starts with ">" correspond to a new observation epoch - if (parseString(0, 1).equals(">")) { - - eventFlag = parseInt(31, 1); - //If eventFlag>1, we skip the corresponding lines to the next observation - if (eventFlag != 0) { - final int nbLinesSkip = parseInt(32, 3); - for (int i = 0; i < nbLinesSkip; i++) { - readLine(reader, true); - } - } else { - - tObs = new AbsoluteDate(parseInt(2, 4), - parseInt(6, 3), - parseInt(9, 3), - parseInt(12, 3), - parseInt(15, 3), - parseDouble(18, 11), timeScale); - - nbSatObs = parseInt(32, 3); - //If the total number of satellites was indicated in the Header - if (nbSat != -1 && nbSatObs > nbSat) { - //we check that the number of Sat in the observation is consistent - throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS, - lineNumber, name, nbSatObs, nbSat); - } - //Read the Receiver Clock offset, if present - rcvrClkOffset = parseDouble(41, 15); - if (Double.isNaN(rcvrClkOffset)) { - rcvrClkOffset = 0.0; - } - - //For each one of the Satellites in this Observation - for (int i = 0; i < nbSatObs; i++) { - - readLine(reader, true); - - //We check that the Satellite type is consistent with Satellite System in the top of the file - final SatelliteSystem satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(parseString(0, 1)); - if (!satelliteSystem.equals(SatelliteSystem.MIXED)) { - if (!satelliteSystemSat.equals(satelliteSystem)) { - throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM, - lineNumber, name, satelliteSystem, satelliteSystemSat); - } - } - - final int prn = parseInt(1, 2); - final int prnNumber; - switch (satelliteSystemSat) { - case GPS: - case GLONASS: - case GALILEO: - case BEIDOU: - case IRNSS: - prnNumber = prn; - break; - case QZSS: - prnNumber = prn + 192; - break; - case SBAS: - prnNumber = prn + 100; - break; - default: - // MIXED satellite system is not allowed here - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - final List observationData = new ArrayList<>(nbSatObs); - for (int j = 0; j < listTypeObs.get(satelliteSystemSat).size(); j++) { - final ObservationType rf = listTypeObs.get(satelliteSystemSat).get(j); - boolean scaleFactorFound = false; - //We look for the lines of ScaledFactorCorrections that correspond to this SatSystem - int k = 0; - double value = parseDouble(3 + j * 16, 14); - while (k < scaleFactorCorrections.size() && !scaleFactorFound) { - if (scaleFactorCorrections.get(k).getSatelliteSystem().equals(satelliteSystemSat)) { - //We check if the next Observation Type to read needs to be scaled - if (scaleFactorCorrections.get(k).getTypesObsScaled().contains(rf)) { - value /= scaleFactorCorrections.get(k).getCorrection(); - scaleFactorFound = true; - } - } - k++; - } - observationData.add(new ObservationData(rf, - value, - parseInt(17 + j * 16, 1), - parseInt(18 + j * 16, 1))); - } - observationDataSets.add(new ObservationDataSet(rinexHeader, satelliteSystemSat, prnNumber, - tObs, rcvrClkOffset, observationData)); - - } - } - } - } - } - break; - } - default: - //If RINEX Version is neither 2 nor 3 - throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name); - } - } - } - - /** Read a new line. - * @param reader reader from where to read line - * @param complainIfEnd if true an exception should be thrown if end of file is encountered - * @return true if a line has been read - * @exception IOException if a read error occurs - */ - private boolean readLine(final BufferedReader reader, final boolean complainIfEnd) - throws IOException { - line = reader.readLine(); - if (line == null && complainIfEnd) { - throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE, name); - } - lineNumber++; - return line != null; - } - - /** Extract a string from a line. - * @param start start index of the string - * @param length length of the string - * @return parsed string - */ - private String parseString(final int start, final int length) { - if (line.length() > start) { - return line.substring(start, FastMath.min(line.length(), start + length)).trim(); - } else { - return null; - } - } - - /** Extract an integer from a line. - * @param start start index of the integer - * @param length length of the integer - * @return parsed integer - */ - private int parseInt(final int start, final int length) { - if (line.length() > start && !parseString(start, length).isEmpty()) { - return Integer.parseInt(parseString(start, length)); - } else { - return 0; - } - } - - /** Extract a double from a line. - * @param start start index of the real - * @param length length of the real - * @return parsed real, or {@code Double.NaN} if field was empty - */ - private double parseDouble(final int start, final int length) { - if (line.length() > start && !parseString(start, length).isEmpty()) { - return Double.parseDouble(parseString(start, length)); - } else { - return Double.NaN; - } - } - - /** Phase Shift corrections. - * Contains the phase shift corrections used to - * generate phases consistent with respect to cycle shifts. - */ - public class PhaseShiftCorrection { - - /** Satellite System. */ - private final SatelliteSystem satSystemPhaseShift; - /** Carrier Phase Observation Code (may be null). */ - private final ObservationType typeObsPhaseShift; - /** Phase Shift Corrections (cycles). */ - private final double phaseShiftCorrection; - /** List of satellites involved. */ - private final String[] satsPhaseShift; - - /** Simple constructor. - * @param satSystemPhaseShift Satellite System - * @param typeObsPhaseShift Carrier Phase Observation Code (may be null) - * @param phaseShiftCorrection Phase Shift Corrections (cycles) - * @param satsPhaseShift List of satellites involved - */ - private PhaseShiftCorrection(final SatelliteSystem satSystemPhaseShift, - final ObservationType typeObsPhaseShift, - final double phaseShiftCorrection, final String[] satsPhaseShift) { - this.satSystemPhaseShift = satSystemPhaseShift; - this.typeObsPhaseShift = typeObsPhaseShift; - this.phaseShiftCorrection = phaseShiftCorrection; - this.satsPhaseShift = satsPhaseShift; - } - - /** Get the Satellite System. - * @return Satellite System. - */ - public SatelliteSystem getSatelliteSystem() { - return satSystemPhaseShift; - } - /** Get the Carrier Phase Observation Code. - *

        - * The observation code may be null for the uncorrected reference - * signal group - *

        - * @return Carrier Phase Observation Code. - */ - public ObservationType getTypeObs() { - return typeObsPhaseShift; - } - /** Get the Phase Shift Corrections. - * @return Phase Shift Corrections (cycles) - */ - public double getCorrection() { - return phaseShiftCorrection; - } - /** Get the list of satellites involved. - * @return List of satellites involved (if null, all the sats are involved) - */ - public String[] getSatsCorrected() { - //If empty, all the satellites of this constellation are affected for this Observation type - return satsPhaseShift == null ? null : satsPhaseShift.clone(); - } - } - - /** Scale Factor to be applied. - * Contains the scale factors of 10 applied to the data before - * being stored into the RINEX file. - */ - public class ScaleFactorCorrection { - - /** Satellite System. */ - private final SatelliteSystem satSystemScaleFactor; - /** List of Observations types that have been scaled. */ - private final List typesObsScaleFactor; - /** Factor to divide stored observations with before use. */ - private final double scaleFactor; - - /** Simple constructor. - * @param satSystemScaleFactor Satellite System - * @param scaleFactor Factor to divide stored observations (1,10,100,1000) - * @param typesObsScaleFactor List of Observations types that have been scaled - */ - private ScaleFactorCorrection(final SatelliteSystem satSystemScaleFactor, - final double scaleFactor, - final List typesObsScaleFactor) { - this.satSystemScaleFactor = satSystemScaleFactor; - this.scaleFactor = scaleFactor; - this.typesObsScaleFactor = typesObsScaleFactor; - } - /** Get the Satellite System. - * @return Satellite System - */ - public SatelliteSystem getSatelliteSystem() { - return satSystemScaleFactor; - } - /** Get the Scale Factor. - * @return Scale Factor - */ - public double getCorrection() { - return scaleFactor; - } - /** Get the list of Observation Types scaled. - * @return List of Observation types scaled - */ - public List getTypesObsScaled() { - return typesObsScaleFactor; - } - } - - } - -} diff --git a/src/main/java/org/orekit/gnss/SEMParser.java b/src/main/java/org/orekit/gnss/SEMParser.java index c7ee27cee5..6e59aa6f58 100644 --- a/src/main/java/org/orekit/gnss/SEMParser.java +++ b/src/main/java/org/orekit/gnss/SEMParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -267,9 +267,7 @@ private void readAlmanac(final BufferedReader reader, final int week, final doub almanac.setSatConfiguration(Integer.parseInt(token[0].trim())); // Adds the almanac to the list - final AbsoluteDate date = - new GNSSDate(week, toa * 1000, SatelliteSystem.GPS, timeScales) - .getDate(); + final AbsoluteDate date = new GNSSDate(week, toa, SatelliteSystem.GPS, timeScales).getDate(); almanac.setDate(date); almanac.setTime(toa); almanac.setWeek(week); diff --git a/src/main/java/org/orekit/gnss/SatInSystem.java b/src/main/java/org/orekit/gnss/SatInSystem.java new file mode 100644 index 0000000000..8fa20b6a43 --- /dev/null +++ b/src/main/java/org/orekit/gnss/SatInSystem.java @@ -0,0 +1,75 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss; + +/** Container for satellite system and PRN. + * @author Luc Maisonobe + * @since 12.0 + */ +public class SatInSystem { + + /** Satellite system. */ + private final SatelliteSystem system; + + /** PRN number. */ + private final int prn; + + /** Simple constructor. + * @param system satellite system + * @param prn Pseudo Random Number + */ + public SatInSystem(final SatelliteSystem system, final int prn) { + this.system = system; + this.prn = prn; + } + + /** Get the system this satellite belongs to. + * @return system this satellite belongs to + */ + public SatelliteSystem getSystem() { + return system; + } + + /** Get the Pseudo Random Number of the satellite. + * @return Pseudo Random Number of the satellite + */ + public int getPRN() { + return prn; + } + + /** Get a 2-digits Pseudo Random Number for RINEX files. + * @return 2-digits Pseudo Random Number for RINEX files + */ + public int getTwoDigitsRinexPRN() { + return system == SatelliteSystem.SBAS ? prn - 100 : (system == SatelliteSystem.QZSS ? prn - 192 : prn); + } + + @Override + public boolean equals(final Object object) { + if (object instanceof SatInSystem) { + final SatInSystem other = (SatInSystem) object; + return getSystem().equals(other.getSystem()) && getPRN() == other.getPRN(); + } + return false; + } + + @Override + public int hashCode() { + return getSystem().hashCode() ^ getPRN(); + } + +} diff --git a/src/main/java/org/orekit/gnss/SatelliteSystem.java b/src/main/java/org/orekit/gnss/SatelliteSystem.java index fb57f2fb07..290d8c06e9 100644 --- a/src/main/java/org/orekit/gnss/SatelliteSystem.java +++ b/src/main/java/org/orekit/gnss/SatelliteSystem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,8 +21,6 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; -import org.orekit.time.TimeScale; -import org.orekit.time.TimeScales; /** * Enumerate for satellite system. @@ -32,29 +30,119 @@ */ public enum SatelliteSystem { - /** GPS system. */ - GPS('G'), + /** User-defined system A. + * @since 12.0 + */ + USER_DEFINED_A('A', null), - /** GLONASS system. */ - GLONASS('R'), + /** User-defined system B. + * @since 12.0 + */ + USER_DEFINED_B('B', null), + + /** Beidou system. */ + BEIDOU('C', ObservationTimeScale.BDT), + + /** User-defined system D. + * @since 12.0 + */ + USER_DEFINED_D('D', null), /** Galileo system. */ - GALILEO('E'), + GALILEO('E', ObservationTimeScale.GAL), - /** Beidou system. */ - BEIDOU('C'), + /** User-defined system F. + * @since 12.0 + */ + USER_DEFINED_F('F', null), + + /** GPS system. */ + GPS('G', ObservationTimeScale.GPS), + + /** User-defined system H. + * @since 12.0 + */ + USER_DEFINED_H('H', null), + + /** Indian Regional Navigation Satellite System system (NavIC). */ + IRNSS('I', ObservationTimeScale.IRN), /** Quasi-Zenith Satellite System system. */ - QZSS('J'), + QZSS('J', ObservationTimeScale.QZS), - /** Indian Regional Navigation Satellite System system. */ - IRNSS('I'), + /** User-defined system K. + * @since 12.0 + */ + USER_DEFINED_K('K', null), - /** SBAS system. */ - SBAS('S'), + /** User-defined system L. + * @since 12.0 + */ + USER_DEFINED_L('L', null), /** Mixed system. */ - MIXED('M'); + MIXED('M', null), + + /** User-defined system N. + * @since 12.0 + */ + USER_DEFINED_N('N', null), + + /** User-defined system O. + * @since 12.0 + */ + USER_DEFINED_O('O', null), + + /** User-defined system P. + * @since 12.0 + */ + USER_DEFINED_P('P', null), + + /** User-defined system Q. + * @since 12.0 + */ + USER_DEFINED_Q('Q', null), + + /** GLONASS system. */ + GLONASS('R', ObservationTimeScale.GLO), + + /** SBAS system. */ + SBAS('S', null), + + /** User-defined system T. + * @since 12.0 + */ + USER_DEFINED_T('T', null), + + /** User-defined system U. + * @since 12.0 + */ + USER_DEFINED_U('U', null), + + /** User-defined system V. + * @since 12.0 + */ + USER_DEFINED_V('V', null), + + /** User-defined system W. + * @since 12.0 + */ + USER_DEFINED_W('W', null), + + /** User-defined system X. + * @since 12.0 + */ + USER_DEFINED_X('X', null), + + /** User-defined system Y. + * @since 12.0 + */ + USER_DEFINED_Y('Y', null), + + /** User-defined system Z. + * @since 12.0 + */ + USER_DEFINED_Z('Z', null); /** Parsing map. */ private static final Map KEYS_MAP = new HashMap<>(); @@ -67,11 +155,18 @@ public enum SatelliteSystem { /** Key for the system. */ private final char key; + /** Observation time scale. + * @since 12.0 + */ + private final ObservationTimeScale observationTimeScale; + /** Simple constructor. * @param key key letter + * @param observationTimeScale observation time scale (may be null) */ - SatelliteSystem(final char key) { - this.key = key; + SatelliteSystem(final char key, final ObservationTimeScale observationTimeScale) { + this.key = key; + this.observationTimeScale = observationTimeScale; } /** Get the key for the system. @@ -98,46 +193,24 @@ public static SatelliteSystem parseSatelliteSystem(final String s) return satelliteSystem; } - /** Get default time scale for satellite system. - * @param timeScales the set of timeScales to use - * @return the default time scale among the given set matching to satellite system, - * null if there are not + /** Parse a string to get the satellite system. + *

        + * The string first character must be the satellite system, or empty to get GPS as default + *

        + * @param s string to parse + * @return the satellite system + * @since 12.0 */ - public TimeScale getDefaultTimeSystem(final TimeScales timeScales) { - - TimeScale timeScale = null; - switch (this) { - case GPS: - timeScale = timeScales.getGPS(); - break; - - case GALILEO: - timeScale = timeScales.getGST(); - break; - - case GLONASS: - timeScale = timeScales.getGLONASS(); - break; - - case QZSS: - timeScale = timeScales.getQZSS(); - break; - - case BEIDOU: - timeScale = timeScales.getBDT(); - break; - - case IRNSS: - timeScale = timeScales.getIRNSS(); - break; - - // Default value is null - default: - timeScale = null; - break; - } + public static SatelliteSystem parseSatelliteSystemWithGPSDefault(final String s) { + return s.isEmpty() ? SatelliteSystem.GPS : parseSatelliteSystem(s); + } - return timeScale; + /** Get observation time scale for satellite system. + * @return observation time scale, null if there are not + * @since 12.0 + */ + public ObservationTimeScale getObservationTimeScale() { + return observationTimeScale; } } diff --git a/src/main/java/org/orekit/gnss/SignalCode.java b/src/main/java/org/orekit/gnss/SignalCode.java index 9c2a1147b8..001857e759 100644 --- a/src/main/java/org/orekit/gnss/SignalCode.java +++ b/src/main/java/org/orekit/gnss/SignalCode.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,7 +30,7 @@ public enum SignalCode { /** Galileo B I/NAV and B C/NAV / IRNSS B RS / GLONASS L1OCp and LO2Cp codes. */ B, - /** GPS C/A / GLONASS C/A / Galileo C / SBAS C/A / QZSS C/A / IRNSS C RS(P) codes. */ + /** GPS C/A / GLONASS C/A / Galileo C / SBAS C/A / QZSS C/A / QZSS C/B / IRNSS C RS(P) codes. */ C, /** GPS L1(C/A) + (P2-P1) / QZSS L5D / Beidou Data codes. */ diff --git a/src/main/java/org/orekit/gnss/TimeSystem.java b/src/main/java/org/orekit/gnss/TimeSystem.java index 50ed5cfeed..bc207bd6db 100644 --- a/src/main/java/org/orekit/gnss/TimeSystem.java +++ b/src/main/java/org/orekit/gnss/TimeSystem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.function.Function; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; @@ -34,52 +35,102 @@ public enum TimeSystem { /** Global Positioning System. */ - GPS("GPS"), + GPS("GPS", "GP", "G", ts -> ts.getGPS()), /** GLONASS. */ - GLONASS("GLO"), + GLONASS("GLO", "GL", "R", ts -> ts.getGLONASS()), /** GALILEO. */ - GALILEO("GAL"), + GALILEO("GAL", "GA", "E", ts -> ts.getGST()), /** International Atomic Time. */ - TAI("TAI"), + TAI("TAI", null, null, ts -> ts.getTAI()), /** Coordinated Universal Time. */ - UTC("UTC"), + UTC("UTC", "UT", null, ts -> ts.getUTC()), /** Quasi-Zenith System. */ - QZSS("QZS"), + QZSS("QZS", "QZ", "J", ts -> ts.getQZSS()), /** Beidou. */ - BEIDOU("BDS"), + BEIDOU("BDS", "BD", "C", ts -> ts.getBDT()), /** IRNSS. */ - IRNSS("IRN"), + IRNSS("IRN", "IR", "I", ts -> ts.getIRNSS()), - /** Unknown. */ - UNKNOWN("LCL"); + /** SBAS. + * @since 12.0 + */ + SBAS("SBAS", "SB", "S", ts -> ts.getUTC()), + + /** GMT (should only by used in RUN BY / DATE entries). + * @since 12.0 + */ + GMT("GMT", null, null, ts -> ts.getUTC()), + + /** Unknown (should only by used in RUN BY / DATE entries). */ + UNKNOWN("LCL", null, null, ts -> ts.getGPS()); - /** Parsing map. */ + /** Parsing key map. */ private static final Map KEYS_MAP = new HashMap<>(); + + /** Parsing two letters code map. + * @since 12.0 + */ + private static final Map TLC_MAP = new HashMap<>(); + + /** Parsing one letters code map. + * @since 12.0 + */ + private static final Map OLC_MAP = new HashMap<>(); + static { for (final TimeSystem timeSystem : values()) { - KEYS_MAP.put(timeSystem.getKey(), timeSystem); + KEYS_MAP.put(timeSystem.key, timeSystem); + if (timeSystem.twoLettersCode != null) { + TLC_MAP.put(timeSystem.twoLettersCode, timeSystem); + } + if (timeSystem.oneLetterCode != null) { + OLC_MAP.put(timeSystem.oneLetterCode, timeSystem); + } } } /** Key for the system. */ private final String key; + /** Two-letters code. + * @since 12.0 + */ + private final String twoLettersCode; + + /** One-letter code. + * @since 12.0 + */ + private final String oneLetterCode; + + /** Time scale provider. + * @since 12.0 + */ + private final Function timeScaleProvider; + /** Simple constructor. * @param key key letter + * @param twoLettersCode two letters code (may be null) + * @param oneLetterCode one letter code (may be null) + * @param timeScaleProvider time scale provider */ - TimeSystem(final String key) { - this.key = key; + TimeSystem(final String key, final String twoLettersCode, final String oneLetterCode, + final Function timeScaleProvider) { + this.key = key; + this.twoLettersCode = twoLettersCode; + this.oneLetterCode = oneLetterCode; + this.timeScaleProvider = timeScaleProvider; } - /** Get the key for the system. - * @return key for the system + /** Get the 3 letters key of the time system. + * @return 3 letters key + * @since 12.0 */ public String getKey() { return key; @@ -102,52 +153,47 @@ public static TimeSystem parseTimeSystem(final String s) return timeSystem; } + /** Parse a string to get the time system. + *

        + * The string must be the two letters code of the time system. + *

        + * @param code string to parse + * @return the time system + * @exception OrekitIllegalArgumentException if the string does not correspond to a time system key + */ + public static TimeSystem parseTwoLettersCode(final String code) + throws OrekitIllegalArgumentException { + final TimeSystem timeSystem = TLC_MAP.get(code); + if (timeSystem == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.UNKNOWN_TIME_SYSTEM, code); + } + return timeSystem; + } + + /** Parse a string to get the time system. + *

        + * The string must be the one letters code of the time system. + * The one letter code is the RINEX GNSS system flag. + *

        + * @param code string to parse + * @return the time system + * @exception OrekitIllegalArgumentException if the string does not correspond to a time system key + */ + public static TimeSystem parseOneLetterCode(final String code) + throws OrekitIllegalArgumentException { + final TimeSystem timeSystem = OLC_MAP.get(code); + if (timeSystem == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.UNKNOWN_TIME_SYSTEM, code); + } + return timeSystem; + } + /** Get the time scale corresponding to time system. * @param timeScales the set of time scales to use * @return the time scale corresponding to time system in the set of time scales */ public TimeScale getTimeScale(final TimeScales timeScales) { - - TimeScale timeScale = null; - switch (this) { - case GPS: - timeScale = timeScales.getGPS(); - break; - - case GALILEO: - timeScale = timeScales.getGST(); - break; - - case GLONASS: - timeScale = timeScales.getGLONASS(); - break; - - case QZSS: - timeScale = timeScales.getQZSS(); - break; - - case TAI: - timeScale = timeScales.getTAI(); - break; - - case UTC: - timeScale = timeScales.getUTC(); - break; - - case BEIDOU: - timeScale = timeScales.getBDT(); - break; - - case IRNSS: - timeScale = timeScales.getIRNSS(); - break; - - // Default value is GPS time scale, even in unknown case. - default: - timeScale = timeScales.getGPS(); - break; - } - - return timeScale; + return timeScaleProvider.apply(timeScales); } + } diff --git a/src/main/java/org/orekit/gnss/YUMAParser.java b/src/main/java/org/orekit/gnss/YUMAParser.java index 58da12acb4..ee51853179 100644 --- a/src/main/java/org/orekit/gnss/YUMAParser.java +++ b/src/main/java/org/orekit/gnss/YUMAParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -317,9 +317,8 @@ private GPSAlmanac getAlmanac(final List> entries, final St // If all expected fields have been read if (readOK(checks)) { // Returns a GPSAlmanac built from the entries - final AbsoluteDate date = - new GNSSDate(almanac.getWeek(), almanac.getTime() * 1000, SatelliteSystem.GPS, timeScales) - .getDate(); + final AbsoluteDate date = new GNSSDate(almanac.getWeek(), almanac.getTime(), SatelliteSystem.GPS, timeScales). + getDate(); almanac.setDate(date); // Add default values to missing keys diff --git a/src/main/java/org/orekit/gnss/antenna/Antenna.java b/src/main/java/org/orekit/gnss/antenna/Antenna.java index 67ea7bb9a5..64d664deb0 100644 --- a/src/main/java/org/orekit/gnss/antenna/Antenna.java +++ b/src/main/java/org/orekit/gnss/antenna/Antenna.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -103,7 +103,7 @@ public double getPhaseCenterVariation(final Frequency frequency, final Vector3D * @param frequency frequency of the signal to consider * @return pattern for this frequency */ - private FrequencyPattern getPattern(final Frequency frequency) { + public FrequencyPattern getPattern(final Frequency frequency) { final FrequencyPattern pattern = patterns.get(frequency); if (pattern == null) { throw new OrekitException(OrekitMessages.UNSUPPORTED_FREQUENCY_FOR_ANTENNA, diff --git a/src/main/java/org/orekit/gnss/antenna/AntexLoader.java b/src/main/java/org/orekit/gnss/antenna/AntexLoader.java index 11a482ecdf..963e649031 100644 --- a/src/main/java/org/orekit/gnss/antenna/AntexLoader.java +++ b/src/main/java/org/orekit/gnss/antenna/AntexLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,7 @@ */ package org.orekit.gnss.antenna; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -29,12 +30,14 @@ import java.util.Optional; import java.util.regex.Pattern; +import org.hipparchus.exception.DummyLocalizable; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; import org.orekit.data.DataLoader; import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; @@ -84,7 +87,7 @@ public AntexLoader(final String supportedNames) { } /** - * Construct a loader by specifying the source of ANTEX auxiliary data files. + * Construct a loader by specifying a {@link DataProvidersManager}. * * @param supportedNames regular expression for supported files names * @param dataProvidersManager provides access to auxiliary data. @@ -100,6 +103,27 @@ public AntexLoader(final String supportedNames, dataProvidersManager.feed(supportedNames, new Parser()); } + /** + * Construct a loader by specifying the source of ANTEX auxiliary data files. + * + * @param source source for the ANTEX data + * @param gps the GPS time scale to use when loading the ANTEX files. + * @since 12.0 + */ + public AntexLoader(final DataSource source, final TimeScale gps) { + try { + this.gps = gps; + satellitesAntennas = new ArrayList<>(); + receiversAntennas = new ArrayList<>(); + try (InputStream is = source.getOpener().openStreamOnce(); + BufferedInputStream bis = new BufferedInputStream(is)) { + new Parser().loadData(bis, source.getName()); + } + } catch (IOException ioe) { + throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage())); + } + } + /** Add a satellite antenna. * @param antenna satellite antenna to add */ diff --git a/src/main/java/org/orekit/gnss/antenna/FrequencyPattern.java b/src/main/java/org/orekit/gnss/antenna/FrequencyPattern.java index 32a849ad6c..c2df151136 100644 --- a/src/main/java/org/orekit/gnss/antenna/FrequencyPattern.java +++ b/src/main/java/org/orekit/gnss/antenna/FrequencyPattern.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,7 @@ package org.orekit.gnss.antenna; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; /** * Pattern for GNSS antenna model on one frequency. @@ -29,18 +29,24 @@ */ public class FrequencyPattern { + /** Pattern with zero correction (i.e. zero eccentricities and no variations). + * @since 12.0 + */ + public static final FrequencyPattern ZERO_CORRECTION = new FrequencyPattern(Vector3D.ZERO, null); + /** Phase center eccentricities (m). */ private final Vector3D eccentricities; - /** Phase center variation function. */ + /** Phase center variation function (may be null if phase center does not depend on signal direction). */ private final PhaseCenterVariationFunction phaseCenterVariationFunction; /** Simple constructor. * @param eccentricities phase center eccentricities (m) * @param phaseCenterVariationFunction phase center variation function + * (may be null if phase center does not depend on signal direction) */ - protected FrequencyPattern(final Vector3D eccentricities, - final PhaseCenterVariationFunction phaseCenterVariationFunction) { + public FrequencyPattern(final Vector3D eccentricities, + final PhaseCenterVariationFunction phaseCenterVariationFunction) { this.eccentricities = eccentricities; this.phaseCenterVariationFunction = phaseCenterVariationFunction; } @@ -52,13 +58,25 @@ public Vector3D getEccentricities() { return eccentricities; } + /** Get the phase center variation function. + * @return phase center variation function (may be null if phase center does not depend on signal direction) + * @since 12.0 + */ + public PhaseCenterVariationFunction getPhaseCenterVariationFunction() { + return phaseCenterVariationFunction; + } + /** Get the value of the phase center variation in a signal direction. * @param direction signal direction in antenna reference frame * @return value of the phase center variation */ public double getPhaseCenterVariation(final Vector3D direction) { - return phaseCenterVariationFunction.value(0.5 * FastMath.PI - direction.getDelta(), - direction.getAlpha()); + if (phaseCenterVariationFunction == null) { + return 0.0; + } else { + return phaseCenterVariationFunction.value(MathUtils.SEMI_PI - direction.getDelta(), + direction.getAlpha()); + } } } diff --git a/src/main/java/org/orekit/gnss/antenna/OneDVariation.java b/src/main/java/org/orekit/gnss/antenna/OneDVariation.java index 238256a285..214e6670cc 100644 --- a/src/main/java/org/orekit/gnss/antenna/OneDVariation.java +++ b/src/main/java/org/orekit/gnss/antenna/OneDVariation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,7 @@ * @author Luc Maisonobe * @since 9.2 */ -class OneDVariation implements PhaseCenterVariationFunction { +public class OneDVariation implements PhaseCenterVariationFunction { /** Start polar angle. */ private final double polarStart; @@ -40,7 +40,7 @@ class OneDVariation implements PhaseCenterVariationFunction { * @param polarStep between grid points * @param variations sampled phase center variations */ - OneDVariation(final double polarStart, final double polarStep, final double[] variations) { + public OneDVariation(final double polarStart, final double polarStep, final double[] variations) { this.polarStart = polarStart; this.polarStep = polarStep; this.variations = variations.clone(); diff --git a/src/main/java/org/orekit/gnss/antenna/PhaseCenterVariationFunction.java b/src/main/java/org/orekit/gnss/antenna/PhaseCenterVariationFunction.java index 6ff9f4c713..0b0069df0d 100644 --- a/src/main/java/org/orekit/gnss/antenna/PhaseCenterVariationFunction.java +++ b/src/main/java/org/orekit/gnss/antenna/PhaseCenterVariationFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/antenna/ReceiverAntenna.java b/src/main/java/org/orekit/gnss/antenna/ReceiverAntenna.java index 1492c1b68a..5594e71d1f 100644 --- a/src/main/java/org/orekit/gnss/antenna/ReceiverAntenna.java +++ b/src/main/java/org/orekit/gnss/antenna/ReceiverAntenna.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/antenna/SatelliteAntenna.java b/src/main/java/org/orekit/gnss/antenna/SatelliteAntenna.java index 44a9106f91..17e78c77c8 100644 --- a/src/main/java/org/orekit/gnss/antenna/SatelliteAntenna.java +++ b/src/main/java/org/orekit/gnss/antenna/SatelliteAntenna.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/antenna/SatelliteType.java b/src/main/java/org/orekit/gnss/antenna/SatelliteType.java index 00beec5f12..061b2a1efa 100644 --- a/src/main/java/org/orekit/gnss/antenna/SatelliteType.java +++ b/src/main/java/org/orekit/gnss/antenna/SatelliteType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -94,6 +94,90 @@ public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validitySta } }, + /** BeiDou-3. */ + BEIDOU_3SI_SECM("BEIDOU-3SI-SECM") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // it seems Beidou III satellites use Galileo mode + return new Galileo(Galileo.DEFAULT_YAW_RATE, + validityStart, validityEnd, sun, inertialFrame); + } + }, + + /** BeiDou-3. */ + BEIDOU_3SI_CAST("BEIDOU-3SI-CAST") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // it seems Beidou III satellites use Galileo mode + return new Galileo(Galileo.DEFAULT_YAW_RATE, + validityStart, validityEnd, sun, inertialFrame); + } + }, + + /** BeiDou-3. */ + BEIDOU_3M_CAST("BEIDOU-3M-CAST") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // it seems Beidou III satellites use Galileo mode + return new Galileo(Galileo.DEFAULT_YAW_RATE, + validityStart, validityEnd, sun, inertialFrame); + } + }, + + /** BeiDou-3. */ + BEIDOU_3SM_CAST("BEIDOU-3SM-CAST") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // it seems Beidou III satellites use Galileo mode + return new Galileo(Galileo.DEFAULT_YAW_RATE, + validityStart, validityEnd, sun, inertialFrame); + } + }, + + /** BeiDou-3. */ + BEIDOU_3M_SECM("BEIDOU-3M-SECM") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // it seems Beidou III satellites use Galileo mode + return new Galileo(Galileo.DEFAULT_YAW_RATE, + validityStart, validityEnd, sun, inertialFrame); + } + }, + + /** BeiDou-3. */ + BEIDOU_3G_CAST("BEIDOU-3G-CAST") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // it seems Beidou III satellites use Galileo mode + return new Galileo(Galileo.DEFAULT_YAW_RATE, + validityStart, validityEnd, sun, inertialFrame); + } + }, + /** GPS Block I : SVN 01-11. */ BLOCK_I("BLOCK I") { /** {@inheritDoc} */ @@ -347,6 +431,19 @@ public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validitySta } }, + /** QZSS Block II (Michibiki-2). */ + QZSS_2A("QZSS-2A") { + /** {@inheritDoc} */ + @Override + public GNSSAttitudeProvider buildAttitudeProvider(final AbsoluteDate validityStart, + final AbsoluteDate validityEnd, + final ExtendedPVCoordinatesProvider sun, + final Frame inertialFrame, final int prnNumber) { + // we don't have yet a specific mode for QZSS, we use generic GNSS (simple yaw steering) + return new GenericGNSS(validityStart, validityEnd, sun, inertialFrame); + } + }, + /** QZSS Block II IGSO (Michibiki-2,4). */ QZSS_2I("QZSS-2I") { /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/gnss/antenna/TwoDVariation.java b/src/main/java/org/orekit/gnss/antenna/TwoDVariation.java index 92e4eae975..3975707dc1 100644 --- a/src/main/java/org/orekit/gnss/antenna/TwoDVariation.java +++ b/src/main/java/org/orekit/gnss/antenna/TwoDVariation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,7 +25,7 @@ * @author Luc Maisonobe * @since 9.2 */ -class TwoDVariation implements PhaseCenterVariationFunction { +public class TwoDVariation implements PhaseCenterVariationFunction { /** Start polar angle. */ private final double polarStart; @@ -45,8 +45,8 @@ class TwoDVariation implements PhaseCenterVariationFunction { * @param azimuthStep step between grid points * @param variations sampled phase center variations */ - TwoDVariation(final double polarStart, final double polarStep, - final double azimuthStep, final double[][] variations) { + public TwoDVariation(final double polarStart, final double polarStep, + final double azimuthStep, final double[][] variations) { this.polarStart = polarStart; this.polarStep = polarStep; this.azimuthStep = azimuthStep; diff --git a/src/main/java/org/orekit/gnss/antenna/package-info.java b/src/main/java/org/orekit/gnss/antenna/package-info.java index 01b4539e77..d2a37d77fe 100644 --- a/src/main/java/org/orekit/gnss/antenna/package-info.java +++ b/src/main/java/org/orekit/gnss/antenna/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/AbstractGNSSAttitudeProvider.java b/src/main/java/org/orekit/gnss/attitude/AbstractGNSSAttitudeProvider.java index 403c6754a1..0a0a0f2075 100644 --- a/src/main/java/org/orekit/gnss/attitude/AbstractGNSSAttitudeProvider.java +++ b/src/main/java/org/orekit/gnss/attitude/AbstractGNSSAttitudeProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -114,8 +114,8 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, /** {@inheritDoc} */ @Override public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { // compute yaw correction final FieldTurnSpan turnSpan = getTurnSpan(date); @@ -182,6 +182,21 @@ private > FieldTurnSpan getTurnSpan(final F } + /** Get provider for Sun position. + * @return provider for Sun position + * @since 12.0 + */ + protected ExtendedPVCoordinatesProvider getSun() { + return sun; + } + + /** Get inertial frame where velocity are computed. + * @return inertial frame where velocity are computed + */ + protected Frame getInertialFrame() { + return inertialFrame; + } + /** Select the /** Compute GNSS attitude with midnight/noon yaw turn correction. * @param context context data for attitude computation diff --git a/src/main/java/org/orekit/gnss/attitude/BeidouGeo.java b/src/main/java/org/orekit/gnss/attitude/BeidouGeo.java index fb5239f93f..b0b94e778f 100644 --- a/src/main/java/org/orekit/gnss/attitude/BeidouGeo.java +++ b/src/main/java/org/orekit/gnss/attitude/BeidouGeo.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/BeidouIGSO.java b/src/main/java/org/orekit/gnss/attitude/BeidouIGSO.java index 6a52f3f4f3..2eead44cb5 100644 --- a/src/main/java/org/orekit/gnss/attitude/BeidouIGSO.java +++ b/src/main/java/org/orekit/gnss/attitude/BeidouIGSO.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/BeidouMeo.java b/src/main/java/org/orekit/gnss/attitude/BeidouMeo.java index 5b41e3e6c7..58f438263c 100644 --- a/src/main/java/org/orekit/gnss/attitude/BeidouMeo.java +++ b/src/main/java/org/orekit/gnss/attitude/BeidouMeo.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/FieldTurnSpan.java b/src/main/java/org/orekit/gnss/attitude/FieldTurnSpan.java index 2ab19d733d..78178d2846 100644 --- a/src/main/java/org/orekit/gnss/attitude/FieldTurnSpan.java +++ b/src/main/java/org/orekit/gnss/attitude/FieldTurnSpan.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeContext.java b/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeContext.java index 106c0bd53e..7451850ee1 100644 --- a/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeContext.java +++ b/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,7 @@ package org.orekit.gnss.attitude; import org.hipparchus.analysis.UnivariateFunction; -import org.hipparchus.analysis.differentiation.DSFactory; -import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.analysis.solvers.UnivariateSolverUtils; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -53,12 +52,6 @@ */ class GNSSAttitudeContext implements TimeStamped { - /** Derivation order. */ - private static final int ORDER = 2; - - /** Time derivation factory. */ - private static final DSFactory FACTORY = new DSFactory(1, ORDER); - /** Constant Y axis. */ private static final PVCoordinates PLUS_Y_PV = new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO); @@ -73,26 +66,40 @@ class GNSSAttitudeContext implements TimeStamped { /** Current date. */ private final AbsoluteDate date; - /** Current date. */ - private final FieldAbsoluteDate dateDS; - /** Provider for Sun position. */ private final ExtendedPVCoordinatesProvider sun; /** Provider for spacecraft position. */ private final PVCoordinatesProvider pvProv; + /** Spacecraft position at central date. + * @since 12.0 + */ + private final TimeStampedPVCoordinates svPV; + + /** Sun position at central date. + * @since 12.0 + */ + private final TimeStampedPVCoordinates sunPV; + /** Inertial frame where velocity are computed. */ private final Frame inertialFrame; /** Cosine of the angle between spacecraft and Sun direction. */ - private final DerivativeStructure svbCos; + private final double svbCos; /** Morning/Evening half orbit indicator. */ private final boolean morning; - /** Relative orbit angle to turn center. */ - private final DerivativeStructure delta; + /** Relative orbit angle to turn center. + * @since 12.0 + */ + private UnivariateDerivative2 delta; + + /** Sun elevation at center. + * @since 12.0 + */ + private UnivariateDerivative2 beta; /** Spacecraft angular velocity. */ private double muRate; @@ -119,60 +126,44 @@ class GNSSAttitudeContext implements TimeStamped { final Frame inertialFrame, final TurnSpan turnSpan) { this.date = date; - this.dateDS = addDerivatives(date); this.sun = sun; this.pvProv = pvProv; this.inertialFrame = inertialFrame; - final FieldPVCoordinates sunPVDS = sun.getPVCoordinates(dateDS, inertialFrame); - - final TimeStampedPVCoordinates svPV = pvProv.getPVCoordinates(date, inertialFrame); - final FieldPVCoordinates svPVDS = svPV.toDerivativeStructurePV(FACTORY.getCompiler().getOrder()); - this.svbCos = FieldVector3D.dotProduct(sunPVDS.getPosition(), svPVDS.getPosition()). - divide(sunPVDS.getPosition().getNorm().multiply(svPVDS.getPosition().getNorm())); - this.morning = Vector3D.dotProduct(svPV.getVelocity(), sunPVDS.getPosition().toVector3D()) >= 0.0; - - this.muRate = svPV.getAngularVelocity().getNorm(); - - this.turnSpan = turnSpan; - - final DerivativeStructure absDelta; - if (svbCos.getValue() <= 0) { + this.sunPV = sun.getPVCoordinates(date, inertialFrame); + this.svPV = pvProv.getPVCoordinates(date, inertialFrame); + this.morning = Vector3D.dotProduct(svPV.getVelocity(), sunPV.getPosition()) >= 0.0; + this.muRate = svPV.getAngularVelocity().getNorm(); + this.turnSpan = turnSpan; + + final FieldPVCoordinates sunPVD2 = sunPV.toUnivariateDerivative2PV(); + final FieldPVCoordinates svPVD2 = svPV.toUnivariateDerivative2PV(); + final UnivariateDerivative2 svbCosD2 = FieldVector3D.dotProduct(sunPVD2.getPosition(), svPVD2.getPosition()). + divide(sunPVD2.getPosition().getNorm().multiply(svPVD2.getPosition().getNorm())); + svbCos = svbCosD2.getValue(); + + beta = FieldVector3D.angle(sunPVD2.getPosition(), svPVD2.getMomentum()).negate().add(0.5 * FastMath.PI); + + final UnivariateDerivative2 absDelta; + if (svbCos <= 0) { // night side - absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos().negate().add(FastMath.PI)); + absDelta = FastMath.acos(svbCosD2.negate().divide(FastMath.cos(beta))); } else { // Sun side - absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos()); + absDelta = FastMath.acos(svbCosD2.divide(FastMath.cos(beta))); } delta = FastMath.copySign(absDelta, -absDelta.getPartialDerivative(1)); } - /** Convert a date, removing derivatives. - * @param d date to convert - * @return date without derivatives - */ - private AbsoluteDate removeDerivatives(final FieldAbsoluteDate d) { - return d.toAbsoluteDate(); - } - - /** Convert a date, adding derivatives. - * @param d date to convert - * @return date without derivatives - */ - private FieldAbsoluteDate addDerivatives(final AbsoluteDate d) { - return new FieldAbsoluteDate<>(FACTORY.getDerivativeField(), d). - shiftedBy(FACTORY.variable(0, 0.0)); - } - /** Compute nominal yaw steering. * @param d computation date * @return nominal yaw steering */ public TimeStampedAngularCoordinates nominalYaw(final AbsoluteDate d) { - final PVCoordinates svPV = pvProv.getPVCoordinates(d, inertialFrame); + final PVCoordinates pv = pvProv.getPVCoordinates(d, inertialFrame); return new TimeStampedAngularCoordinates(d, - svPV.normalize(), - PVCoordinates.crossProduct(sun.getPVCoordinates(d, inertialFrame), svPV).normalize(), + pv.normalize(), + PVCoordinates.crossProduct(sun.getPVCoordinates(d, inertialFrame), pv).normalize(), MINUS_Z_PV, PLUS_Y_PV, 1.0e-9); @@ -183,27 +174,15 @@ public TimeStampedAngularCoordinates nominalYaw(final AbsoluteDate d) { * @return Sun elevation */ public double beta(final AbsoluteDate d) { - final TimeStampedPVCoordinates svPV = pvProv.getPVCoordinates(d, inertialFrame); - return 0.5 * FastMath.PI - Vector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPV.getMomentum()); - } - - /** Compute Sun elevation. - * @param d computation date - * @return Sun elevation - */ - private DerivativeStructure betaDS(final FieldAbsoluteDate d) { - final TimeStampedPVCoordinates svPV = pvProv.getPVCoordinates(removeDerivatives(d), inertialFrame); - final FieldPVCoordinates svPVDS = svPV.toDerivativeStructurePV(FACTORY.getCompiler().getOrder()); - return FieldVector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPVDS.getMomentum()). - negate(). - add(0.5 * FastMath.PI); + final TimeStampedPVCoordinates pv = pvProv.getPVCoordinates(d, inertialFrame); + return 0.5 * FastMath.PI - Vector3D.angle(sun.getPosition(d, inertialFrame), pv.getMomentum()); } /** Compute Sun elevation. * @return Sun elevation */ - public DerivativeStructure betaDS() { - return betaDS(dateDS); + public UnivariateDerivative2 betaD2() { + return beta; } /** {@inheritDoc} */ @@ -223,7 +202,7 @@ public TurnSpan getTurnSpan() { * @return cosine of the angle between spacecraft and Sun direction. */ public double getSVBcos() { - return svbCos.getValue(); + return svbCos; } /** Get a Sun elevation angle that does not change sign within the turn. @@ -237,10 +216,9 @@ public double getSVBcos() { * @see #betaDS(FieldAbsoluteDate) */ public double getSecuredBeta() { - final double beta = beta(getDate()); - return FastMath.abs(beta) < BETA_SIGN_CHANGE_PROTECTION ? + return FastMath.abs(beta.getValue()) < BETA_SIGN_CHANGE_PROTECTION ? beta(turnSpan.getTurnStartDate()) : - beta; + beta.getValue(); } /** Check if a linear yaw model is still active or if we already reached target yaw. @@ -252,7 +230,7 @@ public boolean linearModelStillActive(final double linearPhi, final double phiDo final double dt0 = turnSpan.getTurnEndDate().durationFrom(date); final UnivariateFunction yawReached = dt -> { final AbsoluteDate t = date.shiftedBy(dt); - final Vector3D pSun = sun.getPVCoordinates(t, inertialFrame).getPosition(); + final Vector3D pSun = sun.getPosition(t, inertialFrame); final PVCoordinates pv = pvProv.getPVCoordinates(t, inertialFrame); final Vector3D pSat = pv.getPosition(); final Vector3D targetX = Vector3D.crossProduct(pSat, Vector3D.crossProduct(pSun, pSat)).normalize(); @@ -294,7 +272,7 @@ public boolean setUpTurnRegion(final double cosNight, final double cosNoon) { this.cNight = cosNight; this.cNoon = cosNoon; - if (svbCos.getValue() < cNight || svbCos.getValue() > cNoon) { + if (svbCos < cNight || svbCos > cNoon) { // we are within turn triggering zone return true; } else { @@ -308,7 +286,7 @@ public boolean setUpTurnRegion(final double cosNight, final double cosNoon) { /** Get the relative orbit angle to turn center. * @return relative orbit angle to turn center */ - public DerivativeStructure getDeltaDS() { + public UnivariateDerivative2 getDeltaDS() { return delta; } @@ -316,7 +294,7 @@ public DerivativeStructure getDeltaDS() { * @return orbit angle since solar midnight */ public double getOrbitAngleSinceMidnight() { - final double absAngle = inOrbitPlaneAbsoluteAngle(FastMath.PI - FastMath.acos(svbCos.getValue())); + final double absAngle = inOrbitPlaneAbsoluteAngle(FastMath.PI - FastMath.acos(svbCos)); return morning ? absAngle : -absAngle; } @@ -324,7 +302,7 @@ public double getOrbitAngleSinceMidnight() { * @return true if spacecraft is in the half orbit closest to Sun */ public boolean inSunSide() { - return svbCos.getValue() > 0; + return svbCos > 0; } /** Get yaw at turn start. @@ -335,7 +313,7 @@ public boolean inSunSide() { */ public double getYawStart(final double sunBeta) { final double halfSpan = 0.5 * turnSpan.getTurnDuration() * muRate; - return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue())); + return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos)); } /** Get yaw at turn end. @@ -346,7 +324,7 @@ public double getYawStart(final double sunBeta) { */ public double getYawEnd(final double sunBeta) { final double halfSpan = 0.5 * turnSpan.getTurnDuration() * muRate; - return computePhi(sunBeta, FastMath.copySign(halfSpan, -svbCos.getValue())); + return computePhi(sunBeta, FastMath.copySign(halfSpan, -svbCos)); } /** Compute yaw rate. @@ -366,19 +344,6 @@ public double getMuRate() { return muRate; } - /** Project a spacecraft/Sun angle into orbital plane. - *

        - * This method is intended to find the limits of the noon and midnight - * turns in orbital plane. The return angle is signed, depending on the - * spacecraft being before or after turn middle point. - *

        - * @param angle spacecraft/Sun angle (or spacecraft/opposite-of-Sun) - * @return angle projected into orbital plane, always positive - */ - private DerivativeStructure inOrbitPlaneAbsoluteAngle(final DerivativeStructure angle) { - return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta(getDate())))); - } - /** Project a spacecraft/Sun angle into orbital plane. *

        * This method is intended to find the limits of the noon and midnight @@ -443,17 +408,16 @@ public double timeSinceTurnStart() { * @return attitude with specified yaw */ public TimeStampedAngularCoordinates turnCorrectedAttitude(final double yaw, final double yawDot) { - return turnCorrectedAttitude(FACTORY.build(yaw, yawDot, 0.0)); + return turnCorrectedAttitude(new UnivariateDerivative2(yaw, yawDot, 0.0)); } /** Generate an attitude with turn-corrected yaw. * @param yaw yaw value to apply * @return attitude with specified yaw */ - public TimeStampedAngularCoordinates turnCorrectedAttitude(final DerivativeStructure yaw) { + public TimeStampedAngularCoordinates turnCorrectedAttitude(final UnivariateDerivative2 yaw) { // Earth pointing (Z aligned with position) with linear yaw (momentum with known cos/sin in the X/Y plane) - final PVCoordinates svPV = pvProv.getPVCoordinates(date, inertialFrame); final Vector3D p = svPV.getPosition(); final Vector3D v = svPV.getVelocity(); final Vector3D a = svPV.getAcceleration(); @@ -463,9 +427,9 @@ public TimeStampedAngularCoordinates turnCorrectedAttitude(final DerivativeStruc final PVCoordinates velocity = new PVCoordinates(v, a, keplerianJerk); final PVCoordinates momentum = PVCoordinates.crossProduct(svPV, velocity); - final FieldSinCos sc = FastMath.sinCos(yaw); - final DerivativeStructure c = sc.cos().negate(); - final DerivativeStructure s = sc.sin().negate(); + final FieldSinCos sc = FastMath.sinCos(yaw); + final UnivariateDerivative2 c = sc.cos().negate(); + final UnivariateDerivative2 s = sc.sin().negate(); final Vector3D m0 = new Vector3D(s.getValue(), c.getValue(), 0.0); final Vector3D m1 = new Vector3D(s.getPartialDerivative(1), c.getPartialDerivative(1), 0.0); final Vector3D m2 = new Vector3D(s.getPartialDerivative(2), c.getPartialDerivative(2), 0.0); diff --git a/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeProvider.java b/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeProvider.java index f2c7817149..b9d9fbe725 100644 --- a/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeProvider.java +++ b/src/main/java/org/orekit/gnss/attitude/GNSSAttitudeProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/GNSSFieldAttitudeContext.java b/src/main/java/org/orekit/gnss/attitude/GNSSFieldAttitudeContext.java index 59a39b4c96..c5eacfe638 100644 --- a/src/main/java/org/orekit/gnss/attitude/GNSSFieldAttitudeContext.java +++ b/src/main/java/org/orekit/gnss/attitude/GNSSFieldAttitudeContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,11 +16,10 @@ */ package org.orekit.gnss.attitude; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.UnivariateFunction; -import org.hipparchus.analysis.differentiation.FDSFactory; -import org.hipparchus.analysis.differentiation.FieldDerivativeStructure; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.analysis.solvers.UnivariateSolverUtils; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -57,9 +56,6 @@ */ class GNSSFieldAttitudeContext> implements FieldTimeStamped { - /** Derivation order. */ - private static final int ORDER = 2; - /** Constant Y axis. */ private static final PVCoordinates PLUS_Y_PV = new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO); @@ -71,9 +67,6 @@ class GNSSFieldAttitudeContext> implements Fie /** Limit value below which we shoud use replace beta by betaIni. */ private static final double BETA_SIGN_CHANGE_PROTECTION = FastMath.toRadians(0.07); - /** Time derivation factory. */ - private final FDSFactory factory; - /** Constant Y axis. */ private final FieldPVCoordinates plusY; @@ -86,26 +79,38 @@ class GNSSFieldAttitudeContext> implements Fie /** Current date. */ private final FieldAbsoluteDate date; - /** Current date. */ - private final FieldAbsoluteDate> dateDS; - /** Provider for Sun position. */ private final ExtendedPVCoordinatesProvider sun; /** Provider for spacecraft position. */ private final FieldPVCoordinatesProvider pvProv; + /** Spacecraft position at central date. + * @since 12.0 + */ + private final TimeStampedFieldPVCoordinates svPV; + + /** Sun position at central date. + * @since 12.0 + */ + private final TimeStampedFieldPVCoordinates sunPV; + /** Inertial frame where velocity are computed. */ private final Frame inertialFrame; /** Cosine of the angle between spacecraft and Sun direction. */ - private final FieldDerivativeStructure svbCos; + private final T svbCos; /** Morning/Evening half orbit indicator. */ private final boolean morning; /** Relative orbit angle to turn center. */ - private final FieldDerivativeStructure delta; + private final FieldUnivariateDerivative2 delta; + + /** Sun elevation at center. + * @since 12.0 + */ + private final FieldUnivariateDerivative2 beta; /** Spacecraft angular velocity. */ private T muRate; @@ -132,70 +137,49 @@ class GNSSFieldAttitudeContext> implements Fie final Frame inertialFrame, final FieldTurnSpan turnSpan) { final Field field = date.getField(); - factory = new FDSFactory<>(field, 1, ORDER); plusY = new FieldPVCoordinates<>(field, PLUS_Y_PV); minusZ = new FieldPVCoordinates<>(field, MINUS_Z_PV); this.dateDouble = date.toAbsoluteDate(); this.date = date; - this.dateDS = addDerivatives(date); this.sun = sun; this.pvProv = pvProv; this.inertialFrame = inertialFrame; - final FieldPVCoordinates> sunPVDS = sun.getPVCoordinates(dateDS, inertialFrame); - - final TimeStampedFieldPVCoordinates svPV = pvProv.getPVCoordinates(date, inertialFrame); - final FieldPVCoordinates> svPVDS = - svPV.toDerivativeStructurePV(factory.getCompiler().getOrder()); - this.svbCos = FieldVector3D.dotProduct(sunPVDS.getPosition(), svPVDS.getPosition()). - divide(sunPVDS.getPosition().getNorm().multiply(svPVDS.getPosition().getNorm())); - this.morning = Vector3D.dotProduct(svPV.getVelocity().toVector3D(), sunPVDS.getPosition().toVector3D()) >= 0.0; - - this.muRate = svPV.getAngularVelocity().getNorm(); - - this.turnSpan = turnSpan; - - final FieldDerivativeStructure absDelta; - if (svbCos.getValue().getReal() <= 0) { - // in eclipse turn mode - absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos().negate().add(svbCos.getPi())); + this.sunPV = sun.getPVCoordinates(date, inertialFrame); + this.svPV = pvProv.getPVCoordinates(date, inertialFrame); + this.morning = Vector3D.dotProduct(svPV.getVelocity().toVector3D(), sunPV.getPosition().toVector3D()) >= 0.0; + this.muRate = svPV.getAngularVelocity().getNorm(); + this.turnSpan = turnSpan; + + final FieldPVCoordinates> sunPVD2 = sunPV.toUnivariateDerivative2PV(); + final FieldPVCoordinates> svPVD2 = svPV.toUnivariateDerivative2PV(); + final FieldUnivariateDerivative2 svbCosD2 = FieldVector3D.dotProduct(sunPVD2.getPosition(), svPVD2.getPosition()). + divide(sunPVD2.getPosition().getNorm().multiply(svPVD2.getPosition().getNorm())); + svbCos = svbCosD2.getValue(); + + beta = FieldVector3D.angle(sunPVD2.getPosition(), svPVD2.getMomentum()).negate().add(0.5 * FastMath.PI); + + final FieldUnivariateDerivative2 absDelta; + if (svbCos.getReal() <= 0) { + // night side + absDelta = FastMath.acos(svbCosD2.negate().divide(FastMath.cos(beta))); } else { - // in noon turn mode - absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos()); + // Sun side + absDelta = FastMath.acos(svbCosD2.divide(FastMath.cos(beta))); } delta = absDelta.copySign(absDelta.getPartialDerivative(1).negate()); } - /** Convert a date, removing derivatives. - * @param d date to convert - * @return date without derivatives - */ - private FieldAbsoluteDate removeDerivatives(final FieldAbsoluteDate> d) { - final AbsoluteDate dd = d.toAbsoluteDate(); - final FieldDerivativeStructure offset = d.durationFrom(dd); - return new FieldAbsoluteDate<>(date.getField(), dd).shiftedBy(offset.getValue()); - } - - /** Convert a date, adding derivatives. - * @param d date to convert - * @return date without derivatives - */ - private FieldAbsoluteDate> addDerivatives(final FieldAbsoluteDate d) { - final AbsoluteDate dd = d.toAbsoluteDate(); - final T offset = d.durationFrom(dd); - return new FieldAbsoluteDate<>(factory.getDerivativeField(), dd).shiftedBy(factory.variable(0, offset)); - } - /** Compute nominal yaw steering. * @param d computation date * @return nominal yaw steering */ public TimeStampedFieldAngularCoordinates nominalYaw(final FieldAbsoluteDate d) { - final TimeStampedFieldPVCoordinates svPV = pvProv.getPVCoordinates(d, inertialFrame); + final TimeStampedFieldPVCoordinates pv = pvProv.getPVCoordinates(d, inertialFrame); return new TimeStampedFieldAngularCoordinates<>(d, - svPV.normalize(), - sun.getPVCoordinates(d, inertialFrame).crossProduct(svPV).normalize(), + pv.normalize(), + sun.getPVCoordinates(d, inertialFrame).crossProduct(pv).normalize(), minusZ, plusY, 1.0e-9); @@ -206,21 +190,8 @@ public TimeStampedFieldAngularCoordinates nominalYaw(final FieldAbsoluteDate< * @return Sun elevation */ public T beta(final FieldAbsoluteDate d) { - final TimeStampedFieldPVCoordinates svPV = pvProv.getPVCoordinates(d, inertialFrame); - return FieldVector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPV.getMomentum()). - negate(). - add(svPV.getPosition().getX().getPi().multiply(0.5)); - } - - /** Compute Sun elevation. - * @param d computation date - * @return Sun elevation - */ - private FieldDerivativeStructure betaDS(final FieldAbsoluteDate> d) { - final FieldPVCoordinates> svPV = - pvProv.getPVCoordinates(removeDerivatives(d), inertialFrame). - toDerivativeStructurePV(d.getField().getZero().getOrder()); - return FieldVector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPV.getMomentum()). + final TimeStampedFieldPVCoordinates pv = pvProv.getPVCoordinates(d, inertialFrame); + return FieldVector3D.angle(sun.getPosition(d, inertialFrame), pv.getMomentum()). negate(). add(svPV.getPosition().getX().getPi().multiply(0.5)); } @@ -228,8 +199,8 @@ private FieldDerivativeStructure betaDS(final FieldAbsoluteDate betaDS() { - return betaDS(dateDS); + public FieldUnivariateDerivative2 betaD2() { + return beta; } /** {@inheritDoc} */ @@ -249,7 +220,7 @@ public FieldTurnSpan getTurnSpan() { * @return cosine of the angle between spacecraft and Sun direction. */ public T getSVBcos() { - return svbCos.getValue(); + return svbCos; } /** Get a Sun elevation angle that does not change sign within the turn. @@ -263,10 +234,9 @@ public T getSVBcos() { * @see #betaDS(FieldAbsoluteDate) */ public T getSecuredBeta() { - final T beta = beta(getDate()); - return FastMath.abs(beta.getReal()) < BETA_SIGN_CHANGE_PROTECTION ? + return FastMath.abs(beta.getValue().getReal()) < BETA_SIGN_CHANGE_PROTECTION ? beta(turnSpan.getTurnStartDate()) : - beta; + beta.getValue(); } /** Check if a linear yaw model is still active or if we already reached target yaw. @@ -279,7 +249,7 @@ public boolean linearModelStillActive(final T linearPhi, final T phiDot) { final double dt0 = turnSpan.getTurnEndDate().durationFrom(date).getReal(); final UnivariateFunction yawReached = dt -> { final AbsoluteDate t = absDate.shiftedBy(dt); - final Vector3D pSun = sun.getPVCoordinates(t, inertialFrame).getPosition(); + final Vector3D pSun = sun.getPosition(t, inertialFrame); final PVCoordinates pv = pvProv.getPVCoordinates(date.shiftedBy(dt), inertialFrame).toPVCoordinates(); final Vector3D pSat = pv.getPosition(); final Vector3D targetX = Vector3D.crossProduct(pSat, Vector3D.crossProduct(pSun, pSat)).normalize(); @@ -321,7 +291,7 @@ public boolean setUpTurnRegion(final double cosNight, final double cosNoon) { this.cNight = cosNight; this.cNoon = cosNoon; - if (svbCos.getValue().getReal() < cNight || svbCos.getValue().getReal() > cNoon) { + if (svbCos.getReal() < cNight || svbCos.getReal() > cNoon) { // we are within turn triggering zone return true; } else { @@ -335,7 +305,7 @@ public boolean setUpTurnRegion(final double cosNight, final double cosNoon) { /** Get the relative orbit angle to turn center. * @return relative orbit angle to turn center */ - public FieldDerivativeStructure getDeltaDS() { + public FieldUnivariateDerivative2 getDeltaDS() { return delta; } @@ -343,7 +313,7 @@ public FieldDerivativeStructure getDeltaDS() { * @return orbit angle since solar midnight */ public T getOrbitAngleSinceMidnight() { - final T absAngle = inOrbitPlaneAbsoluteAngle(FastMath.acos(svbCos.getValue()).negate().add(svbCos.getValue().getPi())); + final T absAngle = inOrbitPlaneAbsoluteAngle(FastMath.acos(svbCos).negate().add(svbCos.getPi())); return morning ? absAngle : absAngle.negate(); } @@ -351,7 +321,7 @@ public T getOrbitAngleSinceMidnight() { * @return true if spacecraft is in the half orbit closest to Sun */ public boolean inSunSide() { - return svbCos.getValue().getReal() > 0; + return svbCos.getReal() > 0; } /** Get yaw at turn start. @@ -362,7 +332,7 @@ public boolean inSunSide() { */ public T getYawStart(final T sunBeta) { final T halfSpan = turnSpan.getTurnDuration().multiply(muRate).multiply(0.5); - return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue())); + return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos)); } /** Get yaw at turn end. @@ -373,7 +343,7 @@ public T getYawStart(final T sunBeta) { */ public T getYawEnd(final T sunBeta) { final T halfSpan = turnSpan.getTurnDuration().multiply(muRate).multiply(0.5); - return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue().negate())); + return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.negate())); } /** Compute yaw rate. @@ -393,19 +363,6 @@ public T getMuRate() { return muRate; } - /** Project a spacecraft/Sun angle into orbital plane. - *

        - * This method is intended to find the limits of the noon and midnight - * turns in orbital plane. The return angle is signed, depending on the - * spacecraft being before or after turn middle point. - *

        - * @param angle spacecraft/Sun angle (or spacecraft/opposite-of-Sun) - * @return angle projected into orbital plane, always positive - */ - private FieldDerivativeStructure inOrbitPlaneAbsoluteAngle(final FieldDerivativeStructure angle) { - return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta(getDate())))); - } - /** Project a spacecraft/Sun angle into orbital plane. *

        * This method is intended to find the limits of the noon and midnight @@ -468,17 +425,16 @@ public T timeSinceTurnStart() { * @return attitude with specified yaw */ public TimeStampedFieldAngularCoordinates turnCorrectedAttitude(final T yaw, final T yawDot) { - return turnCorrectedAttitude(factory.build(yaw, yawDot, yaw.getField().getZero())); + return turnCorrectedAttitude(new FieldUnivariateDerivative2<>(yaw, yawDot, yaw.getField().getZero())); } /** Generate an attitude with turn-corrected yaw. * @param yaw yaw value to apply * @return attitude with specified yaw */ - public TimeStampedFieldAngularCoordinates turnCorrectedAttitude(final FieldDerivativeStructure yaw) { + public TimeStampedFieldAngularCoordinates turnCorrectedAttitude(final FieldUnivariateDerivative2 yaw) { // Earth pointing (Z aligned with position) with linear yaw (momentum with known cos/sin in the X/Y plane) - final FieldPVCoordinates svPV = pvProv.getPVCoordinates(date, inertialFrame); final FieldVector3D p = svPV.getPosition(); final FieldVector3D v = svPV.getVelocity(); final FieldVector3D a = svPV.getAcceleration(); @@ -489,10 +445,10 @@ public TimeStampedFieldAngularCoordinates turnCorrectedAttitude(final FieldDe final FieldPVCoordinates velocity = new FieldPVCoordinates<>(v, a, keplerianJerk); final FieldPVCoordinates momentum = svPV.crossProduct(velocity); - final FieldSinCos> sc = FastMath.sinCos(yaw); - final FieldDerivativeStructure c = sc.cos().negate(); - final FieldDerivativeStructure s = sc.sin().negate(); - final T z = yaw.getFactory().getValueField().getZero(); + final FieldSinCos> sc = FastMath.sinCos(yaw); + final FieldUnivariateDerivative2 c = sc.cos().negate(); + final FieldUnivariateDerivative2 s = sc.sin().negate(); + final T z = yaw.getValueField().getZero(); final FieldVector3D m0 = new FieldVector3D<>(s.getValue(), c.getValue(), z); final FieldVector3D m1 = new FieldVector3D<>(s.getPartialDerivative(1), c.getPartialDerivative(1), z); final FieldVector3D m2 = new FieldVector3D<>(s.getPartialDerivative(2), c.getPartialDerivative(2), z); diff --git a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java index ac5d870db5..eb71b14e22 100644 --- a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java +++ b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java index e1185d4acb..15bda16b27 100644 --- a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java +++ b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java index 5a41df659d..736e2908db 100644 --- a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java +++ b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/Galileo.java b/src/main/java/org/orekit/gnss/attitude/Galileo.java index 153fad84b7..e5f3fd0a1b 100644 --- a/src/main/java/org/orekit/gnss/attitude/Galileo.java +++ b/src/main/java/org/orekit/gnss/attitude/Galileo.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,10 +16,10 @@ */ package org.orekit.gnss.attitude; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.analysis.differentiation.FieldDerivativeStructure; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; +import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.orekit.frames.Frame; @@ -92,18 +92,18 @@ protected TimeStampedAngularCoordinates correctedYaw(final GNSSAttitudeContext c if (context.inTurnTimeRange()) { // handling both noon and midnight turns at once - final DerivativeStructure beta = context.betaDS(); - final FieldSinCos scBeta = FastMath.sinCos(beta); - final DerivativeStructure cosBeta = scBeta.cos(); - final DerivativeStructure sinBeta = scBeta.sin(); - final double sinY = FastMath.copySign(FastMath.sin(beta0), context.getSecuredBeta()); - final DerivativeStructure sd = FastMath.sin(context.getDeltaDS()). - multiply(FastMath.copySign(1.0, -context.getSVBcos() * context.getDeltaDS().getPartialDerivative(1))); - final DerivativeStructure c = sd.multiply(cosBeta); - final DerivativeStructure shy = sinBeta.negate().subtract(sinY). - add(sinBeta.subtract(sinY).multiply(c.abs().multiply(c.getPi().divide(FastMath.sin(BETA_X))).cos())). - multiply(0.5); - final DerivativeStructure phi = FastMath.atan2(shy, c); + final UnivariateDerivative2 beta = context.betaD2(); + final FieldSinCos scBeta = FastMath.sinCos(beta); + final UnivariateDerivative2 cosBeta = scBeta.cos(); + final UnivariateDerivative2 sinBeta = scBeta.sin(); + final double sinY = FastMath.copySign(FastMath.sin(beta0), context.getSecuredBeta()); + final UnivariateDerivative2 sd = FastMath.sin(context.getDeltaDS()). + multiply(FastMath.copySign(1.0, -context.getSVBcos() * context.getDeltaDS().getPartialDerivative(1))); + final UnivariateDerivative2 c = sd.multiply(cosBeta); + final UnivariateDerivative2 shy = sinBeta.negate().subtract(sinY). + add(sinBeta.subtract(sinY).multiply(c.abs().multiply(c.getPi().divide(FastMath.sin(BETA_X))).cos())). + multiply(0.5); + final UnivariateDerivative2 phi = FastMath.atan2(shy, c); return context.turnCorrectedAttitude(phi); @@ -135,18 +135,18 @@ protected > TimeStampedFieldAngularCoordinates if (context.inTurnTimeRange()) { // handling both noon and midnight turns at once - final FieldDerivativeStructure beta = context.betaDS(); - final FieldSinCos> scBeta = FastMath.sinCos(beta); - final FieldDerivativeStructure cosBeta = scBeta.cos(); - final FieldDerivativeStructure sinBeta = scBeta.sin(); - final T sinY = FastMath.sin(field.getZero().add(beta0)).copySign(context.getSecuredBeta()); - final FieldDerivativeStructure sd = FastMath.sin(context.getDeltaDS()). - multiply(FastMath.copySign(1.0, -context.getSVBcos().getReal() * context.getDeltaDS().getPartialDerivative(1).getReal())); - final FieldDerivativeStructure c = sd.multiply(cosBeta); - final FieldDerivativeStructure shy = sinBeta.negate().subtract(sinY). - add(sinBeta.subtract(sinY).multiply(c.abs().multiply(c.getPi().divide(FastMath.sin(BETA_X))).cos())). - multiply(0.5); - final FieldDerivativeStructure phi = FastMath.atan2(shy, c); + final FieldUnivariateDerivative2 beta = context.betaD2(); + final FieldSinCos> scBeta = FastMath.sinCos(beta); + final FieldUnivariateDerivative2 cosBeta = scBeta.cos(); + final FieldUnivariateDerivative2 sinBeta = scBeta.sin(); + final T sinY = FastMath.sin(field.getZero().add(beta0)).copySign(context.getSecuredBeta()); + final FieldUnivariateDerivative2 sd = FastMath.sin(context.getDeltaDS()). + multiply(FastMath.copySign(1.0, -context.getSVBcos().getReal() * context.getDeltaDS().getPartialDerivative(1).getReal())); + final FieldUnivariateDerivative2 c = sd.multiply(cosBeta); + final FieldUnivariateDerivative2 shy = sinBeta.negate().subtract(sinY). + add(sinBeta.subtract(sinY).multiply(c.abs().multiply(c.getPi().divide(FastMath.sin(BETA_X))).cos())). + multiply(0.5); + final FieldUnivariateDerivative2 phi = FastMath.atan2(shy, c); return context.turnCorrectedAttitude(phi); diff --git a/src/main/java/org/orekit/gnss/attitude/GenericGNSS.java b/src/main/java/org/orekit/gnss/attitude/GenericGNSS.java index 83aa2c4d76..fac5b6ee95 100644 --- a/src/main/java/org/orekit/gnss/attitude/GenericGNSS.java +++ b/src/main/java/org/orekit/gnss/attitude/GenericGNSS.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/Glonass.java b/src/main/java/org/orekit/gnss/attitude/Glonass.java index c52f0a3093..bcdf39dcc8 100644 --- a/src/main/java/org/orekit/gnss/attitude/Glonass.java +++ b/src/main/java/org/orekit/gnss/attitude/Glonass.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/TurnSpan.java b/src/main/java/org/orekit/gnss/attitude/TurnSpan.java index d0787de078..f7266366bb 100644 --- a/src/main/java/org/orekit/gnss/attitude/TurnSpan.java +++ b/src/main/java/org/orekit/gnss/attitude/TurnSpan.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/attitude/package-info.java b/src/main/java/org/orekit/gnss/attitude/package-info.java index 26933d5940..74a36c9498 100644 --- a/src/main/java/org/orekit/gnss/attitude/package-info.java +++ b/src/main/java/org/orekit/gnss/attitude/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ParsedMessage.java b/src/main/java/org/orekit/gnss/metric/messages/ParsedMessage.java index 3288719ac6..2437ac595d 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ParsedMessage.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ParsedMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/AccuracyProvider.java b/src/main/java/org/orekit/gnss/metric/messages/common/AccuracyProvider.java similarity index 92% rename from src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/AccuracyProvider.java rename to src/main/java/org/orekit/gnss/metric/messages/common/AccuracyProvider.java index 8791ea7ed7..9c73c3c9e8 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/AccuracyProvider.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/AccuracyProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.rtcm.ephemeris.utils; +package org.orekit.gnss.metric.messages.common; /** * This interface represents an accuracy providerused to validate RTCM ephemeris messages. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/ClockCorrection.java b/src/main/java/org/orekit/gnss/metric/messages/common/ClockCorrection.java similarity index 96% rename from src/main/java/org/orekit/gnss/metric/messages/ssr/igm/ClockCorrection.java rename to src/main/java/org/orekit/gnss/metric/messages/common/ClockCorrection.java index 4d3d708087..755586affe 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/ClockCorrection.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/ClockCorrection.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.ssr.igm; +package org.orekit.gnss.metric.messages.common; /** * Container for SSR clock correction data. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/CodeBias.java b/src/main/java/org/orekit/gnss/metric/messages/common/CodeBias.java similarity index 95% rename from src/main/java/org/orekit/gnss/metric/messages/ssr/igm/CodeBias.java rename to src/main/java/org/orekit/gnss/metric/messages/common/CodeBias.java index e7f8bb6d56..b150046902 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/CodeBias.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/CodeBias.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.ssr.igm; +package org.orekit.gnss.metric.messages.common; /** * Container for code bias data. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/GlonassUserRangeAccuracy.java b/src/main/java/org/orekit/gnss/metric/messages/common/GlonassUserRangeAccuracy.java similarity index 95% rename from src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/GlonassUserRangeAccuracy.java rename to src/main/java/org/orekit/gnss/metric/messages/common/GlonassUserRangeAccuracy.java index 4a2ffd232a..bb71fdfc58 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/GlonassUserRangeAccuracy.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/GlonassUserRangeAccuracy.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.rtcm.ephemeris.utils; +package org.orekit.gnss.metric.messages.common; /** Enumerate for GLONASS User Range Accuracy. * @see "ICD L1, L2 GLONASS, Edition 5.1, Table 4.4, 2008" diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/OrbitCorrection.java b/src/main/java/org/orekit/gnss/metric/messages/common/OrbitCorrection.java similarity index 98% rename from src/main/java/org/orekit/gnss/metric/messages/ssr/igm/OrbitCorrection.java rename to src/main/java/org/orekit/gnss/metric/messages/common/OrbitCorrection.java index 6fe9c7a48e..b65e63e4c5 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/OrbitCorrection.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/OrbitCorrection.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.ssr.igm; +package org.orekit.gnss.metric.messages.common; /** * Container for SSR orbit correction data. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/PhaseBias.java b/src/main/java/org/orekit/gnss/metric/messages/common/PhaseBias.java similarity index 97% rename from src/main/java/org/orekit/gnss/metric/messages/ssr/igm/PhaseBias.java rename to src/main/java/org/orekit/gnss/metric/messages/common/PhaseBias.java index a2ee754cd3..bc7edb9e74 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/PhaseBias.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/PhaseBias.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.ssr.igm; +package org.orekit.gnss.metric.messages.common; /** diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/PythonAccuracyProvider.java b/src/main/java/org/orekit/gnss/metric/messages/common/PythonAccuracyProvider.java similarity index 100% rename from src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/PythonAccuracyProvider.java rename to src/main/java/org/orekit/gnss/metric/messages/common/PythonAccuracyProvider.java diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/SignalInSpaceAccuracy.java b/src/main/java/org/orekit/gnss/metric/messages/common/SignalInSpaceAccuracy.java similarity index 95% rename from src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/SignalInSpaceAccuracy.java rename to src/main/java/org/orekit/gnss/metric/messages/common/SignalInSpaceAccuracy.java index 1872bf8dc3..5427b3ce30 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/SignalInSpaceAccuracy.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/SignalInSpaceAccuracy.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.rtcm.ephemeris.utils; +package org.orekit.gnss.metric.messages.common; /** Signal-In-Space Accuracy (SISA). * @see "Galileo OS Signal-In-Space Interface Control Document, Issue 1.3, December 2016, Table 76" diff --git a/src/main/java/org/orekit/gnss/metric/messages/common/SsrUpdateInterval.java b/src/main/java/org/orekit/gnss/metric/messages/common/SsrUpdateInterval.java new file mode 100644 index 0000000000..95d787f73b --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/common/SsrUpdateInterval.java @@ -0,0 +1,65 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.common; + +/** SSR Update interval. + *

        + * Using the indicator parsed in the RTCM message, this + * class provides the SSR update interval in seconds. + *

        + * @author Bryan Cazabonne + * @since 12.0 + */ +public class SsrUpdateInterval { + + /** SSR update interval indicator. */ + private final int indicator; + + /** + * Constructor. + * @param indicator indicator read in the RTCM message + */ + public SsrUpdateInterval(final int indicator) { + this.indicator = indicator; + } + + /** + * Get the update interval. + * @return the update interval in seconds + */ + public double getUpdateInterval() { + switch (indicator) { + case 0 : return 1.0; + case 1 : return 2.0; + case 2 : return 5.0; + case 3 : return 10.0; + case 4 : return 15.0; + case 5 : return 30.0; + case 6 : return 60.0; + case 7 : return 120.0; + case 8 : return 240.0; + case 9 : return 300.0; + case 10 : return 600.0; + case 11 : return 900.0; + case 12 : return 1800.0; + case 13 : return 3600.0; + case 14 : return 7200.0; + default : return 10800.0; + } + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/UserRangeAccuracy.java b/src/main/java/org/orekit/gnss/metric/messages/common/UserRangeAccuracy.java similarity index 95% rename from src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/UserRangeAccuracy.java rename to src/main/java/org/orekit/gnss/metric/messages/common/UserRangeAccuracy.java index 9325bb0ca4..ec4c935d28 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/UserRangeAccuracy.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/UserRangeAccuracy.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.gnss.metric.messages.rtcm.ephemeris.utils; +package org.orekit.gnss.metric.messages.common; import org.hipparchus.util.FastMath; diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/common/package-info.java similarity index 90% rename from src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/package-info.java rename to src/main/java/org/orekit/gnss/metric/messages/common/package-info.java index 64678d5e25..887fefd7d7 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/utils/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/common/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,4 +21,4 @@ * @author Bryan Cazabonne * @since 11.0 */ -package org.orekit.gnss.metric.messages.rtcm.ephemeris.utils; +package org.orekit.gnss.metric.messages.common; diff --git a/src/main/java/org/orekit/gnss/metric/messages/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/package-info.java index 6bc15b8979..61dca52860 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmData.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmData.java index 970a200e19..033d18468c 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmData.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,4 +25,15 @@ public class RtcmData { // No common data + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public RtcmData() { + // nothing to do + } + } diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmMessage.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmMessage.java index 924c6a90dc..51702c6f62 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmMessage.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/RtcmMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,6 +27,7 @@ * @author Bryan Cazabonne * @since 11.0 * + * @param type of the data */ public class RtcmMessage extends ParsedMessage { diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1057.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1057.java new file mode 100644 index 0000000000..5674f827c2 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1057.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1057 message: GPS Orbit Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1057 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1057(final int typeCode, final RtcmOrbitCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GPS, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1058.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1058.java new file mode 100644 index 0000000000..80172b4590 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1058.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1057 message: GPS Clock Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1058 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1058(final int typeCode, final RtcmCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GPS, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1060.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1060.java new file mode 100644 index 0000000000..12b09d1133 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1060.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1060 message: GPS Combined Orbit and Clock Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1060 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1060(final int typeCode, final RtcmOrbitCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GPS, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1063.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1063.java new file mode 100644 index 0000000000..61666ac28c --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1063.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1063 message: GLONASS Orbit Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1063 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1063(final int typeCode, final RtcmOrbitCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GLONASS, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1064.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1064.java new file mode 100644 index 0000000000..7b54b68002 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1064.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1064 message: GLONASS Clock Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1064 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1064(final int typeCode, final RtcmCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GLONASS, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1066.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1066.java new file mode 100644 index 0000000000..8ee4407a21 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1066.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1066 message: GLONASS Combined Orbit and Clock Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1066 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1066(final int typeCode, final RtcmOrbitCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GLONASS, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1240.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1240.java new file mode 100644 index 0000000000..99d8c53ec8 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1240.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1240 message: Galileo Orbit Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1240 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1240(final int typeCode, final RtcmOrbitCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GALILEO, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1241.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1241.java new file mode 100644 index 0000000000..05867ffa8c --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1241.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1241 message: Galileo Clock Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1241 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1241(final int typeCode, final RtcmCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GALILEO, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1243.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1243.java new file mode 100644 index 0000000000..ea5af21518 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/Rtcm1243.java @@ -0,0 +1,41 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.List; + +import org.orekit.gnss.SatelliteSystem; + +/** + * RTCM 1242 message: Combined Galileo Clock Correction Message. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class Rtcm1243 extends RtcmCorrectionMessage { + + /** + * Constructor. + * @param typeCode message number + * @param header message header + * @param data message data + */ + public Rtcm1243(final int typeCode, final RtcmOrbitCorrectionHeader header, + final List data) { + super(typeCode, SatelliteSystem.GALILEO, header, data); + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmClockCorrectionData.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmClockCorrectionData.java new file mode 100644 index 0000000000..73509c02af --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmClockCorrectionData.java @@ -0,0 +1,52 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import org.orekit.gnss.metric.messages.common.ClockCorrection; + +/** + * Container for common data in RTCM clock correction message type. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class RtcmClockCorrectionData extends RtcmCorrectionData { + + /** Container for clock correction data. */ + private ClockCorrection clockCorrection; + + /** Constructor. */ + public RtcmClockCorrectionData() { + // Nothing to do ... + } + + /** + * Get the clock correction data. + * @return the clock correction data + */ + public ClockCorrection getClockCorrection() { + return clockCorrection; + } + + /** + * Set the clock correction data. + * @param clockCorrection the data to set + */ + public void setClockCorrection(final ClockCorrection clockCorrection) { + this.clockCorrection = clockCorrection; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCombinedCorrectionData.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCombinedCorrectionData.java new file mode 100644 index 0000000000..c9adc8fab3 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCombinedCorrectionData.java @@ -0,0 +1,95 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import org.orekit.gnss.metric.messages.common.ClockCorrection; +import org.orekit.gnss.metric.messages.common.OrbitCorrection; + +/** + * Container for common data in RTCM combined corrections message type. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class RtcmCombinedCorrectionData extends RtcmCorrectionData { + + /** GNSS IOD. */ + private int gnssIod; + + /** Container for SSR orbit correction data. */ + private OrbitCorrection orbitCorrection; + + /** Container for clock correction data. */ + private ClockCorrection clockCorrection; + + /** Constructor. */ + public RtcmCombinedCorrectionData() { + // Nothing to do ... + } + + /** + * Get the GNSS IOD. + *

        + * Users have to interpret the IOD value depending the + * satellite system of the current message. + *

        + * @return the GNSS IOD + */ + public int getGnssIod() { + return gnssIod; + } + + /** + * Set the GNSS IOD. + * @param gnssIod the GNSS IOD to set + */ + public void setGnssIod(final int gnssIod) { + this.gnssIod = gnssIod; + } + + /** + * Get the orbit correction data. + * @return the orbit correction data + */ + public OrbitCorrection getOrbitCorrection() { + return orbitCorrection; + } + + /** + * Set the orbit correction data. + * @param orbitCorrection the data to set + */ + public void setOrbitCorrection(final OrbitCorrection orbitCorrection) { + this.orbitCorrection = orbitCorrection; + } + + /** + * Get the clock correction data. + * @return the clock correction data + */ + public ClockCorrection getClockCorrection() { + return clockCorrection; + } + + /** + * Set the clock correction data. + * @param clockCorrection the data to set + */ + public void setClockCorrection(final ClockCorrection clockCorrection) { + this.clockCorrection = clockCorrection; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionData.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionData.java new file mode 100644 index 0000000000..d70a649850 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionData.java @@ -0,0 +1,52 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import org.orekit.gnss.metric.messages.rtcm.RtcmData; + +/** + * Container for common data in RTCM corrections message type. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class RtcmCorrectionData extends RtcmData { + + /** GNSS satellite ID. */ + private int satelliteID; + + /** Constructor. */ + public RtcmCorrectionData() { + // Nothing to do ... + } + + /** + * Get the satellite ID. + * @return the satellite ID + */ + public int getSatelliteID() { + return satelliteID; + } + + /** + * Set the satellite ID. + * @param satelliteID the ID to set + */ + public void setSatelliteID(final int satelliteID) { + this.satelliteID = satelliteID; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionHeader.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionHeader.java new file mode 100644 index 0000000000..c0a625d2fb --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionHeader.java @@ -0,0 +1,177 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import org.orekit.gnss.metric.messages.common.SsrUpdateInterval; + +/** + * Container for common data in RTCM Correction Message type header. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class RtcmCorrectionHeader { + + /** SSR Epoch Time 1s. */ + private double epochTime1s; + + /** SSR Update Interval. */ + private SsrUpdateInterval ssrUpdateInterval; + + /** Multiple Message Indicator. */ + private int multipleMessageIndicator; + + /** IOD SSR. */ + private int iodSsr; + + /** SSR Provider ID. */ + private int ssrProviderId; + + /** SSR Solution ID. */ + private int ssrSolutionId; + + /** Number of satellites. */ + private int numberOfSatellites; + + /** Constructor. */ + public RtcmCorrectionHeader() { + // Nothing to do ... + } + + /** + * Get the Epoch Time 1s. + *

        + * Full seconds since the beginning of the GNSS week for + * or full seconds since the beginning of GLONASS day + *

        + * @return the Epoch Time 1s in seconds + */ + public double getEpochTime1s() { + return epochTime1s; + } + + /** + * Set the Epoch Time 1s. + * @param epochTime1s the Epoch Time 1s to set + */ + public void setEpochTime1s(final double epochTime1s) { + this.epochTime1s = epochTime1s; + } + + /** + * Get the SSR Update Interval. + * @return the SSR Update Interval + */ + public SsrUpdateInterval getSsrUpdateInterval() { + return ssrUpdateInterval; + } + + /** + * Set the SSR Update Interval. + * @param ssrUpdateInterval the SSR Update Interval to set + */ + public void setSsrUpdateInterval(final SsrUpdateInterval ssrUpdateInterval) { + this.ssrUpdateInterval = ssrUpdateInterval; + } + + /** + * Get the Multiple Message Indicator. + *

        + * 0 - Last message of a sequence. 1 - Multiple message transmitted + *

        + * @return the SSR Multiple Message Indicator + */ + public int getMultipleMessageIndicator() { + return multipleMessageIndicator; + } + + /** + * Set the Multiple Message Indicator. + * @param multipleMessageIndicator the Multiple Message Indicator to set + */ + public void setMultipleMessageIndicator(final int multipleMessageIndicator) { + this.multipleMessageIndicator = multipleMessageIndicator; + } + + /** + * Get the IOD SSR. + *

        + * A change of Issue of Data SSR is used to + * indicate a change in the SSR generating configuration. + *

        + * @return the IOD SSR + */ + public int getIodSsr() { + return iodSsr; + } + + /** + * Set the IOD SSR. + * @param iodSsr the IOF SSR to set + */ + public void setIodSsr(final int iodSsr) { + this.iodSsr = iodSsr; + } + + /** + * Get the SSR Provider ID. + * @return the SSR Provider ID + */ + public int getSsrProviderId() { + return ssrProviderId; + } + + /** + * Set the SSR Provider ID. + * @param ssrProviderId the SSR Provider ID to set + */ + public void setSsrProviderId(final int ssrProviderId) { + this.ssrProviderId = ssrProviderId; + } + + /** + * Get the SSR Solution ID. + * @return the SSR Solution ID + */ + public int getSsrSolutionId() { + return ssrSolutionId; + } + + /** + * Set the SSR Solution ID. + * @param ssrSolutionId the SSR Solution ID to set + */ + public void setSsrSolutionId(final int ssrSolutionId) { + this.ssrSolutionId = ssrSolutionId; + } + + /** + * Get the number of satellites for the current IGM message. + * @return the number of satellites for the current IGM message + */ + public int getNumberOfSatellites() { + return numberOfSatellites; + } + + /** + * Set the number of satellites for the current IGM message. + * @param numberOfSatellites the number of satellites to set + */ + public void setNumberOfSatellites(final int numberOfSatellites) { + this.numberOfSatellites = numberOfSatellites; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionMessage.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionMessage.java new file mode 100644 index 0000000000..5c7092f1ba --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmCorrectionMessage.java @@ -0,0 +1,103 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.orekit.gnss.SatelliteSystem; +import org.orekit.gnss.metric.messages.rtcm.RtcmMessage; + +/** + * The RTCM Correction Message types provide elements + * to calculate GNSS satellite corrections. + * Corrections are orbit and clock corrections. + * + * @author Bryan Cazabonne + * @since 12.0 + * @param type of the header + * @param type of the data + * + */ +public class RtcmCorrectionMessage extends RtcmMessage { + + /** Message header. */ + private final H header; + + /** Satellite system. */ + private final SatelliteSystem system; + + /** + * Constructor. + * @param system satellite system associated to the message + * @param typeCode message number + * @param header message header + * @param data message data + */ + public RtcmCorrectionMessage(final int typeCode, final SatelliteSystem system, + final H header, final List data) { + super(typeCode, data); + this.header = header; + this.system = system; + } + + /** + * Get the header. + * @return header + */ + public H getHeader() { + return header; + } + + /** + * Get the satellite system associated to the message. + * @return the satellite system + */ + public SatelliteSystem getSatelliteSystem() { + return system; + } + + /** + * Get the all data parsed in the RTCM correction message. + *

        + * Key: satellite identifier (e.g. "G01") + *

        + * @return the all data for the parsed message + */ + public Map> getDataMap() { + + // Initialize map + final Map> data = new HashMap<>(); + + // Loop on parsed data and fill map + for (final D currentData : getData()) { + final int satId = currentData.getSatelliteID(); + final String idString = satId < 10 ? "0" + String.valueOf(satId) : String.valueOf(satId); + final String id = getSatelliteSystem().getKey() + idString; + data.putIfAbsent(id, new ArrayList<>()); + data.get(id).add(currentData); + } + + // Return an unmodifiable map of the parsed data + return Collections.unmodifiableMap(data); + + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmOrbitCorrectionData.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmOrbitCorrectionData.java new file mode 100644 index 0000000000..6503cd1329 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmOrbitCorrectionData.java @@ -0,0 +1,75 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +import org.orekit.gnss.metric.messages.common.OrbitCorrection; + +/** + * Container for common data in RTCM orbit correction message type. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class RtcmOrbitCorrectionData extends RtcmCorrectionData { + + /** GNSS IOD. */ + private int gnssIod; + + /** Container for SSR orbit correction data. */ + private OrbitCorrection orbitCorrection; + + /** Constructor. */ + public RtcmOrbitCorrectionData() { + // Nothing to do ... + } + + /** + * Get the GNSS IOD. + *

        + * Users have to interpret the IOD value depending the + * satellite system of the current message. + *

        + * @return the GNSS IOD + */ + public int getGnssIod() { + return gnssIod; + } + + /** + * Set the GNSS IOD. + * @param gnssIod the GNSS IOD to set + */ + public void setGnssIod(final int gnssIod) { + this.gnssIod = gnssIod; + } + + /** + * Get the orbit correction data. + * @return the orbit correction data + */ + public OrbitCorrection getOrbitCorrection() { + return orbitCorrection; + } + + /** + * Set the orbit correction data. + * @param orbitCorrection the data to set + */ + public void setOrbitCorrection(final OrbitCorrection orbitCorrection) { + this.orbitCorrection = orbitCorrection; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmOrbitCorrectionHeader.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmOrbitCorrectionHeader.java new file mode 100644 index 0000000000..7c39d38769 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/RtcmOrbitCorrectionHeader.java @@ -0,0 +1,54 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.messages.rtcm.correction; + +/** + * Container for common data in RTCM Orbit Correction Message type header. + * @author Bryan Cazabonne + * @since 12.0 + */ +public class RtcmOrbitCorrectionHeader extends RtcmCorrectionHeader { + + /** Satellite reference datum. */ + private int satelliteReferenceDatum; + + /** Constructor. */ + public RtcmOrbitCorrectionHeader() { + // Nothing to do ... + } + + /** + * Get the satellite reference datum. + *

        + * Orbit corrections refer to Satellite Reference Datum: + * 0 - ITRF. 1 - Regional + *

        + * @return the indicator of the satellite reference datum + */ + public int getSatelliteReferenceDatum() { + return satelliteReferenceDatum; + } + + /** + * Set the satellite reference datum. + * @param satelliteReferenceDatum the satellite reference datum to set + */ + public void setSatelliteReferenceDatum(final int satelliteReferenceDatum) { + this.satelliteReferenceDatum = satelliteReferenceDatum; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/package-info.java new file mode 100644 index 0000000000..89d1de8722 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/correction/package-info.java @@ -0,0 +1,25 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package provides all supported RTCM correction + * {@link org.orekit.gnss.metric.messages.ParsedMessage messages}. + * + * @author Bryan Cazabonne + * @since 12.0 + */ +package org.orekit.gnss.metric.messages.rtcm.correction; diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019.java index 12eda6f0eb..c558df66c3 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019Data.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019Data.java index 66e67a33f7..a485ee8e8e 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1019Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import org.orekit.data.DataContext; import org.orekit.gnss.SatelliteSystem; import org.orekit.propagation.analytical.gnss.GNSSPropagator; -import org.orekit.propagation.analytical.gnss.data.GPSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GPSLegacyNavigationMessage; import org.orekit.time.GNSSDate; import org.orekit.time.TimeScales; @@ -32,7 +32,7 @@ public class Rtcm1019Data extends RtcmEphemerisData { /** GPS navigation message. */ - private GPSNavigationMessage gpsNavigationMessage; + private GPSLegacyNavigationMessage gpsNavigationMessage; /** GPS Time of clock. */ private double gpsToc; @@ -63,7 +63,7 @@ public Rtcm1019Data() { * @return the GPS navigation message */ @DefaultDataContext - public GPSNavigationMessage getGpsNavigationMessage() { + public GPSLegacyNavigationMessage getGpsNavigationMessage() { return getGpsNavigationMessage(DataContext.getDefault().getTimeScales()); } @@ -78,7 +78,7 @@ public GPSNavigationMessage getGpsNavigationMessage() { * @param timeScales time scales to use for initializing epochs * @return the GPS navigation message */ - public GPSNavigationMessage getGpsNavigationMessage(final TimeScales timeScales) { + public GPSLegacyNavigationMessage getGpsNavigationMessage(final TimeScales timeScales) { // Satellite system final SatelliteSystem system = SatelliteSystem.GPS; @@ -88,8 +88,8 @@ public GPSNavigationMessage getGpsNavigationMessage(final TimeScales timeScales) final double toe = gpsNavigationMessage.getTime(); // Set the ephemeris reference data - gpsNavigationMessage.setDate(new GNSSDate(week, SEC_TO_MILLI * toe, system, timeScales).getDate()); - gpsNavigationMessage.setEpochToc(new GNSSDate(week, SEC_TO_MILLI * gpsToc, system, timeScales).getDate()); + gpsNavigationMessage.setDate(new GNSSDate(week, toe, system, timeScales).getDate()); + gpsNavigationMessage.setEpochToc(new GNSSDate(week, gpsToc, system, timeScales).getDate()); // Return the navigation message return gpsNavigationMessage; @@ -100,7 +100,7 @@ public GPSNavigationMessage getGpsNavigationMessage(final TimeScales timeScales) * Set the GPS navigation message. * @param gpsNavigationMessage the GPS navigation message to set */ - public void setGpsNavigationMessage(final GPSNavigationMessage gpsNavigationMessage) { + public void setGpsNavigationMessage(final GPSLegacyNavigationMessage gpsNavigationMessage) { this.gpsNavigationMessage = gpsNavigationMessage; } diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020.java index 18326f4e9e..690c1bd5a1 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020Data.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020Data.java index 2eb17590f4..ed33887b3b 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1020Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042.java index 259a0a7704..27171e2d0b 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042Data.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042Data.java index a10f8f591b..bc2dd327b5 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1042Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import org.orekit.data.DataContext; import org.orekit.gnss.SatelliteSystem; import org.orekit.propagation.analytical.gnss.GNSSPropagator; -import org.orekit.propagation.analytical.gnss.data.BeidouNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.BeidouLegacyNavigationMessage; import org.orekit.time.GNSSDate; import org.orekit.time.TimeScales; @@ -32,7 +32,7 @@ public class Rtcm1042Data extends RtcmEphemerisData { /** Beidou navigation message. */ - private BeidouNavigationMessage beidouNavigationMessage; + private BeidouLegacyNavigationMessage beidouNavigationMessage; /** Beidou Time of clock. */ private double beidouToc; @@ -57,7 +57,7 @@ public Rtcm1042Data() { * @return the Beidou navigation message */ @DefaultDataContext - public BeidouNavigationMessage getBeidouNavigationMessage() { + public BeidouLegacyNavigationMessage getBeidouNavigationMessage() { return getBeidouNavigationMessage(DataContext.getDefault().getTimeScales()); } @@ -72,7 +72,7 @@ public BeidouNavigationMessage getBeidouNavigationMessage() { * @param timeScales time scales to use for initializing epochs * @return the Beidou navigation message */ - public BeidouNavigationMessage getBeidouNavigationMessage(final TimeScales timeScales) { + public BeidouLegacyNavigationMessage getBeidouNavigationMessage(final TimeScales timeScales) { // Satellite system final SatelliteSystem system = SatelliteSystem.BEIDOU; @@ -82,8 +82,8 @@ public BeidouNavigationMessage getBeidouNavigationMessage(final TimeScales timeS final double toe = beidouNavigationMessage.getTime(); // Set the ephemeris reference data - beidouNavigationMessage.setDate(new GNSSDate(week, SEC_TO_MILLI * toe, system, timeScales).getDate()); - beidouNavigationMessage.setEpochToc(new GNSSDate(week, SEC_TO_MILLI * beidouToc, system, timeScales).getDate()); + beidouNavigationMessage.setDate(new GNSSDate(week, toe, system, timeScales).getDate()); + beidouNavigationMessage.setEpochToc(new GNSSDate(week, beidouToc, system, timeScales).getDate()); // Return the navigation message return beidouNavigationMessage; @@ -94,7 +94,7 @@ public BeidouNavigationMessage getBeidouNavigationMessage(final TimeScales timeS * Set the Beidou navigation message. * @param beidouNavigationMessage the Beidou navigation message to set */ - public void setBeidouNavigationMessage(final BeidouNavigationMessage beidouNavigationMessage) { + public void setBeidouNavigationMessage(final BeidouLegacyNavigationMessage beidouNavigationMessage) { this.beidouNavigationMessage = beidouNavigationMessage; } diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044.java index 3651b95090..4cc08fc5db 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044Data.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044Data.java index 9224ed0eaf..e613f989b1 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1044Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ import org.orekit.data.DataContext; import org.orekit.gnss.SatelliteSystem; import org.orekit.propagation.analytical.gnss.GNSSPropagator; -import org.orekit.propagation.analytical.gnss.data.QZSSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.QZSSLegacyNavigationMessage; import org.orekit.time.GNSSDate; import org.orekit.time.TimeScales; @@ -32,7 +32,7 @@ public class Rtcm1044Data extends RtcmEphemerisData { /** QZSS navigation message. */ - private QZSSNavigationMessage qzssNavigationMessage; + private QZSSLegacyNavigationMessage qzssNavigationMessage; /** QZSS Time of clock. */ private double qzssToc; @@ -60,7 +60,7 @@ public Rtcm1044Data() { * @return the QZSS navigation message */ @DefaultDataContext - public QZSSNavigationMessage getQzssNavigationMessage() { + public QZSSLegacyNavigationMessage getQzssNavigationMessage() { return getQzssNavigationMessage(DataContext.getDefault().getTimeScales()); } @@ -75,7 +75,7 @@ public QZSSNavigationMessage getQzssNavigationMessage() { * @param timeScales time scales to use for initializing epochs * @return the QZSS navigation message */ - public QZSSNavigationMessage getQzssNavigationMessage(final TimeScales timeScales) { + public QZSSLegacyNavigationMessage getQzssNavigationMessage(final TimeScales timeScales) { // Satellite system final SatelliteSystem system = SatelliteSystem.QZSS; @@ -85,8 +85,8 @@ public QZSSNavigationMessage getQzssNavigationMessage(final TimeScales timeScale final double toe = qzssNavigationMessage.getTime(); // Set the ephemeris reference data - qzssNavigationMessage.setDate(new GNSSDate(week, SEC_TO_MILLI * toe, system, timeScales).getDate()); - qzssNavigationMessage.setEpochToc(new GNSSDate(week, SEC_TO_MILLI * qzssToc, system, timeScales).getDate()); + qzssNavigationMessage.setDate(new GNSSDate(week, toe, system, timeScales).getDate()); + qzssNavigationMessage.setEpochToc(new GNSSDate(week, qzssToc, system, timeScales).getDate()); // Return the navigation message return qzssNavigationMessage; @@ -97,7 +97,7 @@ public QZSSNavigationMessage getQzssNavigationMessage(final TimeScales timeScale * Set the QZSS navigation message. * @param qzssNavigationMessage the QZSS navigation message to set */ - public void setQzssNavigationMessage(final QZSSNavigationMessage qzssNavigationMessage) { + public void setQzssNavigationMessage(final QZSSLegacyNavigationMessage qzssNavigationMessage) { this.qzssNavigationMessage = qzssNavigationMessage; } diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045.java index b61deb5848..fad9b0ce1d 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045Data.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045Data.java index dc603712ce..62bc8b91f3 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/Rtcm1045Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -82,8 +82,8 @@ public GalileoNavigationMessage getGalileoNavigationMessage(final TimeScales tim final double toe = galileoNavigationMessage.getTime(); // Set the ephemeris reference data - galileoNavigationMessage.setDate(new GNSSDate(week, SEC_TO_MILLI * toe, system, timeScales).getDate()); - galileoNavigationMessage.setEpochToc(new GNSSDate(week, SEC_TO_MILLI * galileoToc, system, timeScales).getDate()); + galileoNavigationMessage.setDate(new GNSSDate(week, toe, system, timeScales).getDate()); + galileoNavigationMessage.setEpochToc(new GNSSDate(week, galileoToc, system, timeScales).getDate()); // Return the navigation message return galileoNavigationMessage; diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisData.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisData.java index c64f21fbe2..6c1a341990 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisData.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,8 @@ */ package org.orekit.gnss.metric.messages.rtcm.ephemeris; +import org.orekit.gnss.metric.messages.common.AccuracyProvider; import org.orekit.gnss.metric.messages.rtcm.RtcmData; -import org.orekit.gnss.metric.messages.rtcm.ephemeris.utils.AccuracyProvider; /** * Container for common data in RTCM ephemeris message type. @@ -26,9 +26,6 @@ */ public class RtcmEphemerisData extends RtcmData { - /** Seconds to milliseconds converter. */ - protected static final double SEC_TO_MILLI = 1000.0; - /** Satellite ID. */ private int rtcmSatelliteId; diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisMessage.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisMessage.java index d581cf2396..1b8b2c84c6 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisMessage.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/RtcmEphemerisMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,6 +24,7 @@ * Base class for RTCM ephemeris messages. * @author Bryan Cazabonne * @since 11.0 + * @param type of the data */ public class RtcmEphemerisMessage extends RtcmMessage { diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/package-info.java index 30c490b9b1..c57b26f233 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/ephemeris/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/rtcm/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/rtcm/package-info.java index 2d5529078b..04f90b7e21 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/rtcm/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/rtcm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrData.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrData.java index 72d5959684..f538c6e750 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrData.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,4 +25,15 @@ public class SsrData { // No common data + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public SsrData() { + // nothing to do + } + } diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrHeader.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrHeader.java index be6d3f4216..d09066de61 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrHeader.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -41,6 +41,17 @@ public class SsrHeader { /** SSR Solution ID. */ private int ssrSolutionId; + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public SsrHeader() { + // nothing to do + } + /** * Get the SSR Epoch Time 1s. *

        diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrMessage.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrMessage.java index 82082edd07..c56c7c5dc1 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrMessage.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/SsrMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,7 +26,8 @@ * * @author Bryan Cazabonne * @since 11.0 - * + * @param type of the header + * @param type of the data */ public class SsrMessage extends ParsedMessage { diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01.java index c13bedca64..39f69df29c 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Data.java index 434cb4fe34..0000da25e3 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,8 @@ */ package org.orekit.gnss.metric.messages.ssr.igm; +import org.orekit.gnss.metric.messages.common.OrbitCorrection; + /** * Container for SSR IGM01 data. * @author Bryan Cazabonne diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Header.java index 9532344430..b5266434d8 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm01Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02.java index 20e64d02af..1ac8e5d9c8 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Data.java index d18891014b..9fbc53b3ae 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,8 @@ */ package org.orekit.gnss.metric.messages.ssr.igm; +import org.orekit.gnss.metric.messages.common.ClockCorrection; + /** * Container for SSR IGM02 data. * @author Bryan Cazabonne diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Header.java index aaef0c5e11..8275672d31 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm02Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03.java index a8c0320b06..d2758ed1cd 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Data.java index 180842462e..1eeeb00a9b 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,9 @@ */ package org.orekit.gnss.metric.messages.ssr.igm; +import org.orekit.gnss.metric.messages.common.ClockCorrection; +import org.orekit.gnss.metric.messages.common.OrbitCorrection; + /** * Container for SSR IGM03 data. * @author Bryan Cazabonne diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Header.java index e193a7d8d2..768d19531d 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm03Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04.java index 99e1080dec..bef8a2ef63 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Data.java index 1054760d81..e7f2cbd2cb 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Header.java index 8ae5644c00..ee51ae046f 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm04Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05.java index 5a234d0962..d385aaff88 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Data.java index c60010f65c..a606382117 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.Map; +import org.orekit.gnss.metric.messages.common.CodeBias; + /** * Container for SSR IGM05 data. * @author Bryan Cazabonne diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Header.java index 4185e36c33..cfeab6e7b6 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm05Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06.java index a697dd34f0..544009befc 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Data.java index b9ccc0e7f0..b965ef2065 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.Map; +import org.orekit.gnss.metric.messages.common.PhaseBias; + /** * Container for SSR IGM06 data. * @author Bryan Cazabonne diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Header.java index 8794b460b9..057e19aef9 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm06Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07.java index 15a330cceb..e1f5e9fc1d 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Data.java index f8bc606f31..fd9639a9e0 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Header.java index 01bcaeba68..87989eb300 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgm07Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmData.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmData.java index 054890a9c9..817c191613 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmData.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmHeader.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmHeader.java index ed0e2609df..fd86ff2ea2 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmHeader.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmHeader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmMessage.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmMessage.java index 678096e039..0647960145 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmMessage.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/SsrIgmMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,7 +29,8 @@ * * @author Bryan Cazabonne * @since 11.0 - * + * @param type of the header + * @param type of the data */ public class SsrIgmMessage extends SsrMessage { diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/package-info.java index 27bf71b0c1..52eeec3fc4 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/igm/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/package-info.java index 01864c5d61..2a9d27f1c5 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201.java index b6ec76acb3..5b75d8d130 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Data.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Data.java index 6928d78a4a..4b590f3eab 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Data.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Data.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Header.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Header.java index 1695ee3ced..60917e2fcb 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Header.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/SsrIm201Header.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/package-info.java b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/package-info.java index 3cc5b44402..98582c1ea0 100644 --- a/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/messages/ssr/subtype/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/Authentication.java b/src/main/java/org/orekit/gnss/metric/ntrip/Authentication.java index 4fb59e14bf..74998f455a 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/Authentication.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/Authentication.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/CarrierPhase.java b/src/main/java/org/orekit/gnss/metric/ntrip/CarrierPhase.java index 09e58c27a3..9e17f287e8 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/CarrierPhase.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/CarrierPhase.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/CasterRecord.java b/src/main/java/org/orekit/gnss/metric/ntrip/CasterRecord.java index f8a90688c4..6f034f61b2 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/CasterRecord.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/CasterRecord.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/DataFormat.java b/src/main/java/org/orekit/gnss/metric/ntrip/DataFormat.java index 5e4af6b8d1..e0996cba39 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/DataFormat.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/DataFormat.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/DataStreamRecord.java b/src/main/java/org/orekit/gnss/metric/ntrip/DataStreamRecord.java index 779feab08d..d52795cfee 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/DataStreamRecord.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/DataStreamRecord.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/GnssData.java b/src/main/java/org/orekit/gnss/metric/ntrip/GnssData.java index f9d013c8e2..476a65525b 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/GnssData.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/GnssData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/MessageObserver.java b/src/main/java/org/orekit/gnss/metric/ntrip/MessageObserver.java index a8ca67bc7e..f7a7acd9ff 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/MessageObserver.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/MessageObserver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java b/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java index 149480baf7..eaccc4555a 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/NetworkRecord.java b/src/main/java/org/orekit/gnss/metric/ntrip/NetworkRecord.java index 68eb14eee3..2cdc72cfb3 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/NetworkRecord.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/NetworkRecord.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/NtripClient.java b/src/main/java/org/orekit/gnss/metric/ntrip/NtripClient.java index c520a72cbc..d357152eef 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/NtripClient.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/NtripClient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,6 +27,8 @@ import java.net.Proxy; import java.net.Proxy.Type; import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; @@ -370,8 +372,8 @@ public SourceTable getSourceTable() { sourceTable = table; return table; - } catch (IOException ioe) { - throw new OrekitException(ioe, OrekitMessages.CANNOT_PARSE_SOURCETABLE, host); + } catch (IOException | URISyntaxException e) { + throw new OrekitException(e, OrekitMessages.CANNOT_PARSE_SOURCETABLE, host); } } @@ -471,13 +473,14 @@ public void stopStreaming(final int time) { * @param mountPoint mount point (empty for getting sourcetable) * @return performed connection * @throws IOException if an I/O exception occurs during connection + * @throws URISyntaxException if the built URI is invalid */ HttpURLConnection connect(final String mountPoint) - throws IOException { + throws IOException, URISyntaxException { // set up connection - final String protocol = "http"; - final URL casterURL = new URL(protocol, host, port, "/" + mountPoint); + final String scheme = "http"; + final URL casterURL = new URI(scheme, null, host, port, "/" + mountPoint, null, null).toURL(); final HttpURLConnection connection = (HttpURLConnection) casterURL.openConnection(proxy); connection.setConnectTimeout(timeout); connection.setReadTimeout(timeout); diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/Record.java b/src/main/java/org/orekit/gnss/metric/ntrip/Record.java index 099bf52308..49bd406c27 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/Record.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/Record.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/RecordType.java b/src/main/java/org/orekit/gnss/metric/ntrip/RecordType.java index c05147aa6a..48e3b5084d 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/RecordType.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/RecordType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/SourceTable.java b/src/main/java/org/orekit/gnss/metric/ntrip/SourceTable.java index a2fd7a2390..b3d185a54a 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/SourceTable.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/SourceTable.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java b/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java index f49655dc1f..dcc743d1e4 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -33,14 +34,14 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.gnss.metric.messages.ParsedMessage; -import org.orekit.gnss.metric.parser.AbstractEncodedMessages; +import org.orekit.gnss.metric.parser.AbstractEncodedMessage; import org.orekit.gnss.metric.parser.MessagesParser; /** Monitor for retrieving streamed data from one mount point. * @author Luc Maisonobe * @since 11.0 */ -public class StreamMonitor extends AbstractEncodedMessages implements Runnable { +public class StreamMonitor extends AbstractEncodedMessage implements Runnable { /** GGA header key. */ private static final String GGA_HEADER_KEY = "Ntrip-GGA"; @@ -306,8 +307,8 @@ public void run() { } } catch (SocketTimeoutException ste) { // ignore exception, it will be handled by reconnection attempt below - } catch (IOException ioe) { - throw new OrekitException(ioe, OrekitMessages.CANNOT_PARSE_GNSS_DATA, client.getHost()); + } catch (IOException | URISyntaxException e) { + throw new OrekitException(e, OrekitMessages.CANNOT_PARSE_GNSS_DATA, client.getHost()); } // manage reconnection @@ -439,6 +440,9 @@ private int computeCRC(final int length) { return crc; } + /** Extract the used messages. + * @return the extracted messages + */ private List extractUsedMessages() { synchronized (observers) { diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/StreamedMessage.java b/src/main/java/org/orekit/gnss/metric/ntrip/StreamedMessage.java index 84e64b499a..a1a1d590c9 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/StreamedMessage.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/StreamedMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/Type.java b/src/main/java/org/orekit/gnss/metric/ntrip/Type.java index e704abd0c4..c7bbf20a0c 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/Type.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/Type.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/package-info.java b/src/main/java/org/orekit/gnss/metric/ntrip/package-info.java index 2e40195e92..ef5baa4b74 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/package-info.java b/src/main/java/org/orekit/gnss/metric/package-info.java index 791d8d5106..baff80d3be 100644 --- a/src/main/java/org/orekit/gnss/metric/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/AbstractEncodedMessages.java b/src/main/java/org/orekit/gnss/metric/parser/AbstractEncodedMessage.java similarity index 78% rename from src/main/java/org/orekit/gnss/metric/parser/AbstractEncodedMessages.java rename to src/main/java/org/orekit/gnss/metric/parser/AbstractEncodedMessage.java index 1f8b9eb625..7546383cd9 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/AbstractEncodedMessages.java +++ b/src/main/java/org/orekit/gnss/metric/parser/AbstractEncodedMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,10 +21,16 @@ import org.orekit.errors.OrekitMessages; /** Encoded messages as a sequence of bytes. + *

        + * Note that only full bytes are supported. This means that for example + * the 300 bits message from GPS sub-frames must be completed with 4 zero + * bits to reach 304 bits = 38 bytes, even if only the first 300 bits + * will be decoded and the 4 extra bits in the last byte will be ignored. + *

        * @author Luc Maisonobe * @since 11.0 */ -public abstract class AbstractEncodedMessages implements EncodedMessage { +public abstract class AbstractEncodedMessage implements EncodedMessage { /** Current byte (as an int). */ private int current; @@ -32,6 +38,17 @@ public abstract class AbstractEncodedMessages implements EncodedMessage { /** Remaining bits in current byte. */ private int remaining; + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public AbstractEncodedMessage() { + // nothing to do + } + /** {@inheritDoc} */ @Override public void start() { diff --git a/src/main/java/org/orekit/gnss/metric/parser/ByteArrayEncodedMessages.java b/src/main/java/org/orekit/gnss/metric/parser/ByteArrayEncodedMessage.java similarity index 87% rename from src/main/java/org/orekit/gnss/metric/parser/ByteArrayEncodedMessages.java rename to src/main/java/org/orekit/gnss/metric/parser/ByteArrayEncodedMessage.java index d2310b9d9e..18c25bc759 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/ByteArrayEncodedMessages.java +++ b/src/main/java/org/orekit/gnss/metric/parser/ByteArrayEncodedMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,11 +16,11 @@ */ package org.orekit.gnss.metric.parser; -/** Encoded messages as a byte array. +/** Encoded message as a byte array. * @author Luc Maisonobe * @since 11.0 */ -public class ByteArrayEncodedMessages extends AbstractEncodedMessages { +public class ByteArrayEncodedMessage extends AbstractEncodedMessage { /** Byte array containing the message. */ private final byte[] message; @@ -31,7 +31,7 @@ public class ByteArrayEncodedMessages extends AbstractEncodedMessages { /** Simple constructor. * @param message byte array containing the message */ - public ByteArrayEncodedMessages(final byte[] message) { + public ByteArrayEncodedMessage(final byte[] message) { this.message = message.clone(); } diff --git a/src/main/java/org/orekit/gnss/metric/parser/DataField.java b/src/main/java/org/orekit/gnss/metric/parser/DataField.java index dd94238320..ebd1e6bf55 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/DataField.java +++ b/src/main/java/org/orekit/gnss/metric/parser/DataField.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/DataType.java b/src/main/java/org/orekit/gnss/metric/parser/DataType.java index 8a702801ec..512cd284b1 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/DataType.java +++ b/src/main/java/org/orekit/gnss/metric/parser/DataType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ * @author Luc Maisonobe * @since 11.0 */ -enum DataType { +public enum DataType { /** 1 bit. */ BIT_1(m -> m.extractBits(1)), @@ -367,7 +367,7 @@ enum DataType { * @param message encoded message providing the bits to decode * @return data decoded as a Long object, or null if data not available */ - Long decode(final EncodedMessage message) { + public Long decode(final EncodedMessage message) { return decoder.apply(message); } diff --git a/src/main/java/org/orekit/gnss/metric/parser/EncodedMessage.java b/src/main/java/org/orekit/gnss/metric/parser/EncodedMessage.java index 5fe3f83d86..c682aeb215 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/EncodedMessage.java +++ b/src/main/java/org/orekit/gnss/metric/parser/EncodedMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/HexadecimalSequenceEncodedMessage.java b/src/main/java/org/orekit/gnss/metric/parser/HexadecimalSequenceEncodedMessage.java new file mode 100644 index 0000000000..0814506892 --- /dev/null +++ b/src/main/java/org/orekit/gnss/metric/parser/HexadecimalSequenceEncodedMessage.java @@ -0,0 +1,59 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.metric.parser; + +/** Encoded message as an hexadecimal characters sequence. + * @author Luc Maisonobe + * @since 11.0 + */ +public class HexadecimalSequenceEncodedMessage extends AbstractEncodedMessage { + + /** Hexadecimal radix. */ + private static final int HEXA = 16; + + /** Characters sequence containing the message. */ + private final CharSequence message; + + /** Index of current character in array. */ + private int charIndex; + + /** Simple constructor. + * @param message characters sequence containing the message + */ + public HexadecimalSequenceEncodedMessage(final CharSequence message) { + this.message = message; + } + + /** {@inheritDoc} */ + @Override + public void start() { + super.start(); + this.charIndex = -1; + } + + /** {@inheritDoc} */ + @Override + protected int fetchByte() { + if (charIndex + 2 >= message.length()) { + return -1; + } + final int high = Character.digit(message.charAt(++charIndex), HEXA); + final int low = Character.digit(message.charAt(++charIndex), HEXA); + return high << 4 | low; + } + +} diff --git a/src/main/java/org/orekit/gnss/metric/parser/IgsSsrDataField.java b/src/main/java/org/orekit/gnss/metric/parser/IgsSsrDataField.java index 6f18644719..dbfe05ab1b 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/IgsSsrDataField.java +++ b/src/main/java/org/orekit/gnss/metric/parser/IgsSsrDataField.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessageType.java b/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessageType.java index 54912b56e7..7655df3833 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessageType.java +++ b/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessageType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,10 +27,10 @@ import org.orekit.errors.OrekitMessages; import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.metric.messages.ParsedMessage; -import org.orekit.gnss.metric.messages.ssr.igm.ClockCorrection; -import org.orekit.gnss.metric.messages.ssr.igm.CodeBias; -import org.orekit.gnss.metric.messages.ssr.igm.OrbitCorrection; -import org.orekit.gnss.metric.messages.ssr.igm.PhaseBias; +import org.orekit.gnss.metric.messages.common.ClockCorrection; +import org.orekit.gnss.metric.messages.common.CodeBias; +import org.orekit.gnss.metric.messages.common.OrbitCorrection; +import org.orekit.gnss.metric.messages.common.PhaseBias; import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm01; import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm01Data; import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm01Header; diff --git a/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessagesParser.java b/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessagesParser.java index 0166d95aeb..2a52d533a3 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessagesParser.java +++ b/src/main/java/org/orekit/gnss/metric/parser/IgsSsrMessagesParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/InputStreamEncodedMessages.java b/src/main/java/org/orekit/gnss/metric/parser/InputStreamEncodedMessage.java similarity index 89% rename from src/main/java/org/orekit/gnss/metric/parser/InputStreamEncodedMessages.java rename to src/main/java/org/orekit/gnss/metric/parser/InputStreamEncodedMessage.java index e9f4779b68..e861a823d8 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/InputStreamEncodedMessages.java +++ b/src/main/java/org/orekit/gnss/metric/parser/InputStreamEncodedMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,7 +26,7 @@ * @author Luc Maisonobe * @since 11.0 */ -public class InputStreamEncodedMessages extends AbstractEncodedMessages { +public class InputStreamEncodedMessage extends AbstractEncodedMessage { /** Input stream providing the message. */ private final InputStream stream; @@ -34,7 +34,7 @@ public class InputStreamEncodedMessages extends AbstractEncodedMessages { /** Simple constructor. * @param stream input stream providing the message */ - public InputStreamEncodedMessages(final InputStream stream) { + public InputStreamEncodedMessage(final InputStream stream) { this.stream = stream; } diff --git a/src/main/java/org/orekit/gnss/metric/parser/MessageType.java b/src/main/java/org/orekit/gnss/metric/parser/MessageType.java index 20f4f68ccd..8a2ff6615b 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/MessageType.java +++ b/src/main/java/org/orekit/gnss/metric/parser/MessageType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/MessagesParser.java b/src/main/java/org/orekit/gnss/metric/parser/MessagesParser.java index 19ad329e4d..3b4cf433cd 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/MessagesParser.java +++ b/src/main/java/org/orekit/gnss/metric/parser/MessagesParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/RtcmDataField.java b/src/main/java/org/orekit/gnss/metric/parser/RtcmDataField.java index 8ec3516860..7ff2009199 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/RtcmDataField.java +++ b/src/main/java/org/orekit/gnss/metric/parser/RtcmDataField.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -65,6 +65,15 @@ public int intValue(final EncodedMessage message) { } }, + /** GNSS satellite ID. */ + DF068 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_6.decode(message).intValue(); + } + }, + /** GPS IODE (Issue Of Data, Ephemeris). */ DF071 { /** {@inheritDoc} */ @@ -106,7 +115,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).doubleValue(), -43)); } }, @@ -115,7 +124,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_16.decode(message).intValue() * 16.0; + return DataType.U_INT_16.decode(message).doubleValue() * 16.0; } }, @@ -124,7 +133,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_8.decode(message).intValue(), -55); + return FastMath.scalb(DataType.INT_8.decode(message).doubleValue(), -55); } }, @@ -133,7 +142,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -43); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -43); } }, @@ -142,7 +151,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_22.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_22.decode(message).doubleValue(), -31); } }, @@ -160,7 +169,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -5); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -5); } }, @@ -169,7 +178,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -43)); } }, @@ -178,7 +187,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -187,7 +196,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -196,7 +205,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.U_INT_32.decode(message).longValue(), -33); + return FastMath.scalb(DataType.U_INT_32.decode(message).doubleValue(), -33); } }, @@ -205,7 +214,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -214,7 +223,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.U_INT_32.decode(message).longValue(), -19); + return FastMath.scalb(DataType.U_INT_32.decode(message).doubleValue(), -19); } }, @@ -223,7 +232,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_16.decode(message).intValue() * 16.0; + return DataType.U_INT_16.decode(message).doubleValue() * 16.0; } }, @@ -232,7 +241,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -241,7 +250,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -250,7 +259,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -259,7 +268,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -268,7 +277,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -5); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -5); } }, @@ -277,7 +286,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -286,7 +295,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).doubleValue(), -43)); } }, @@ -295,7 +304,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_8.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_8.decode(message).doubleValue(), -31); } }, @@ -357,7 +366,7 @@ public int intValue(final EncodedMessage message) { public double doubleValue(final EncodedMessage message) { final int hours = DataType.U_INT_5.decode(message).intValue(); final int minutes = DataType.U_INT_6.decode(message).intValue(); - final double secondes = DataType.BIT_1.decode(message).intValue() * 30.0; + final double secondes = DataType.BIT_1.decode(message).doubleValue() * 30.0; return hours * 3600.0 + minutes * 60.0 + secondes; } }, @@ -385,7 +394,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Unit.MINUTE.toSI(DataType.U_INT_7.decode(message).intValue() * 15.0); + return Unit.MINUTE.toSI(DataType.U_INT_7.decode(message).doubleValue() * 15.0); } }, @@ -394,7 +403,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.KM_PER_S.toSI(FastMath.scalb(DataType.INT_S_24.decode(message).intValue(), -20)); + return Units.KM_PER_S.toSI(FastMath.scalb(DataType.INT_S_24.decode(message).doubleValue(), -20)); } }, @@ -403,7 +412,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Unit.KILOMETRE.toSI(FastMath.scalb(DataType.INT_S_27.decode(message).intValue(), -11)); + return Unit.KILOMETRE.toSI(FastMath.scalb(DataType.INT_S_27.decode(message).doubleValue(), -11)); } }, @@ -412,7 +421,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.KM_PER_S2.toSI(FastMath.scalb(DataType.INT_S_5.decode(message).intValue(), -30)); + return Units.KM_PER_S2.toSI(FastMath.scalb(DataType.INT_S_5.decode(message).doubleValue(), -30)); } }, @@ -421,7 +430,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.KM_PER_S.toSI(FastMath.scalb(DataType.INT_S_24.decode(message).intValue(), -20)); + return Units.KM_PER_S.toSI(FastMath.scalb(DataType.INT_S_24.decode(message).doubleValue(), -20)); } }, @@ -430,7 +439,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Unit.KILOMETRE.toSI(FastMath.scalb(DataType.INT_S_27.decode(message).intValue(), -11)); + return Unit.KILOMETRE.toSI(FastMath.scalb(DataType.INT_S_27.decode(message).doubleValue(), -11)); } }, @@ -439,7 +448,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.KM_PER_S2.toSI(FastMath.scalb(DataType.INT_S_5.decode(message).intValue(), -30)); + return Units.KM_PER_S2.toSI(FastMath.scalb(DataType.INT_S_5.decode(message).doubleValue(), -30)); } }, @@ -448,7 +457,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.KM_PER_S.toSI(FastMath.scalb(DataType.INT_S_24.decode(message).intValue(), -20)); + return Units.KM_PER_S.toSI(FastMath.scalb(DataType.INT_S_24.decode(message).doubleValue(), -20)); } }, @@ -457,7 +466,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Unit.KILOMETRE.toSI(FastMath.scalb(DataType.INT_S_27.decode(message).intValue(), -11)); + return Unit.KILOMETRE.toSI(FastMath.scalb(DataType.INT_S_27.decode(message).doubleValue(), -11)); } }, @@ -466,7 +475,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.KM_PER_S2.toSI(FastMath.scalb(DataType.INT_S_5.decode(message).intValue(), -30)); + return Units.KM_PER_S2.toSI(FastMath.scalb(DataType.INT_S_5.decode(message).doubleValue(), -30)); } }, @@ -489,7 +498,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_S_11.decode(message).intValue(), -40); + return FastMath.scalb(DataType.INT_S_11.decode(message).doubleValue(), -40); } }, @@ -516,7 +525,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_S_22.decode(message).intValue(), -30); + return FastMath.scalb(DataType.INT_S_22.decode(message).doubleValue(), -30); } }, @@ -525,7 +534,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_S_5.decode(message).intValue(), -30); + return FastMath.scalb(DataType.INT_S_5.decode(message).doubleValue(), -30); } }, @@ -597,7 +606,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_S_32.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_S_32.decode(message).doubleValue(), -31); } }, @@ -615,7 +624,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_S_22.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_S_22.decode(message).doubleValue(), -31); } }, @@ -678,7 +687,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).doubleValue(), -43)); } }, @@ -687,7 +696,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_14.decode(message).intValue() * 60.0; + return DataType.U_INT_14.decode(message).doubleValue() * 60.0; } }, @@ -696,7 +705,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_6.decode(message).intValue(), -59); + return FastMath.scalb(DataType.INT_6.decode(message).doubleValue(), -59); } }, @@ -705,7 +714,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_21.decode(message).intValue(), -46); + return FastMath.scalb(DataType.INT_21.decode(message).doubleValue(), -46); } }, @@ -714,7 +723,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_31.decode(message).intValue(), -34); + return FastMath.scalb(DataType.INT_31.decode(message).doubleValue(), -34); } }, @@ -723,7 +732,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -5); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -5); } }, @@ -732,7 +741,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -43)); } }, @@ -741,7 +750,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -750,7 +759,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -768,7 +777,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -786,7 +795,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_14.decode(message).intValue() * 60.0; + return DataType.U_INT_14.decode(message).doubleValue() * 60.0; } }, @@ -795,7 +804,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -804,7 +813,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -813,7 +822,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -822,7 +831,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -831,7 +840,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -5); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -5); } }, @@ -840,7 +849,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -849,7 +858,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).doubleValue(), -43)); } }, @@ -858,7 +867,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_10.decode(message).intValue(), -32); + return FastMath.scalb(DataType.INT_10.decode(message).doubleValue(), -32); } }, @@ -867,7 +876,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_10.decode(message).intValue(), -32); + return FastMath.scalb(DataType.INT_10.decode(message).doubleValue(), -32); } }, @@ -907,6 +916,186 @@ public int intValue(final EncodedMessage message) { } }, + /** Delta Orbit Radial (m). */ + DF365 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM.toSI(DataType.INT_22.decode(message).doubleValue() * 0.1); + } + }, + + /** Delta Along-Track (m). */ + DF366 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM.toSI(DataType.INT_20.decode(message).doubleValue() * 0.4); + } + }, + + /** Delta Cross-Track (m). */ + DF367 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM.toSI(DataType.INT_20.decode(message).doubleValue() * 0.4); + } + }, + + /** Dot Delta Radial (m/s). */ + DF368 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM_PER_S.toSI(DataType.INT_21.decode(message).doubleValue() * 0.001); + } + }, + + /** Dot Delta Along-Track (m/s). */ + DF369 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM_PER_S.toSI(DataType.INT_19.decode(message).doubleValue() * 0.004); + } + }, + + /** Dot Delta Cross-Track (m/s). */ + DF370 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM_PER_S.toSI(DataType.INT_19.decode(message).doubleValue() * 0.004); + } + }, + + /** Satellite Reference Datum. */ + DF375 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.BIT_1.decode(message).byteValue(); + } + }, + + /** Delta Clock C0. */ + DF376 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM.toSI(DataType.INT_22.decode(message).doubleValue() * 0.1); + } + }, + + /** Delta Clock C1. */ + DF377 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM_PER_S.toSI(DataType.INT_21.decode(message).doubleValue() * 0.001); + } + }, + + /** Delta Clock C2. */ + DF378 { + /** {@inheritDoc} */ + @Override + public double doubleValue(final EncodedMessage message) { + return Units.MM_PER_S2.toSI(DataType.INT_27.decode(message).doubleValue() * 0.00002); + } + }, + + /** GLONASS Satellite ID. */ + DF384 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_5.decode(message).byteValue(); + } + }, + + /** GPS Epoch Time 1s. */ + DF385 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_20.decode(message).intValue(); + } + }, + + /** GLONASS Epoch Time 1s. */ + DF386 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_17.decode(message).intValue(); + } + }, + + /** No. of Satellites. */ + DF387 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_6.decode(message).byteValue(); + } + }, + + /** Multiple Message Indicator. */ + DF388 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.BIT_1.decode(message).byteValue(); + } + }, + + /** SSR Update Interval. */ + DF391 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.BIT_4.decode(message).byteValue(); + } + }, + + /** GLONASS Issue Of Date (IOD). */ + DF392 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.BIT_8.decode(message).intValue(); + } + }, + + /** IOD SSR. */ + DF413 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_4.decode(message).byteValue(); + } + }, + + /** SSR Provider ID. */ + DF414 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_16.decode(message).intValue(); + } + }, + + /** SSR Solution ID. */ + DF415 { + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_4.decode(message).byteValue(); + } + }, + /** QZSS Satellite ID. */ DF429 { /** {@inheritDoc} */ @@ -922,7 +1111,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_16.decode(message).intValue() * 16.0; + return DataType.U_INT_16.decode(message).doubleValue() * 16.0; } }, @@ -931,7 +1120,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_8.decode(message).intValue(), -55); + return FastMath.scalb(DataType.INT_8.decode(message).doubleValue(), -55); } }, @@ -940,7 +1129,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -43); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -43); } }, @@ -949,7 +1138,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_22.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_22.decode(message).doubleValue(), -31); } }, @@ -967,7 +1156,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -5); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -5); } }, @@ -976,7 +1165,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -43)); } }, @@ -985,7 +1174,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -994,7 +1183,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -1012,7 +1201,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -1030,7 +1219,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_16.decode(message).intValue() * 16.0; + return DataType.U_INT_16.decode(message).doubleValue() * 16.0; } }, @@ -1039,7 +1228,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -1048,7 +1237,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1057,7 +1246,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -29); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -29); } }, @@ -1066,7 +1255,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1075,7 +1264,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_16.decode(message).intValue(), -5); + return FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -5); } }, @@ -1084,7 +1273,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1093,7 +1282,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).doubleValue(), -43)); } }, @@ -1102,7 +1291,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).doubleValue(), -43)); } }, @@ -1147,7 +1336,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_8.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_8.decode(message).doubleValue(), -31); } }, @@ -1169,6 +1358,19 @@ public int intValue(final EncodedMessage message) { } }, + /** Galileo Epoch Time 1s. */ + DF458 { + // Ref: 1°/ "RTCM SPECIAL COMMITTEE NO.104, RTCM Paper 107-2014-SC104-818, + // Proposal of new RTCM SSR Messages SSR Stage 1: Galileo, QZSS, SBAS, BDS for RTCM STANDARD 10403.2" + // 2°/ "Interface Specification for MADOCA-SEAD, Japan Aerospace Exploration Agency, + // October 2016, rev February 2017" + /** {@inheritDoc} */ + @Override + public int intValue(final EncodedMessage message) { + return DataType.U_INT_20.decode(message).intValue(); + } + }, + /** BDS Satellite ID. */ DF488 { /** {@inheritDoc} */ @@ -1201,7 +1403,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_14.decode(message).doubleValue(), -43)); } }, @@ -1219,7 +1421,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_17.decode(message).intValue() * 8.0; + return DataType.U_INT_17.decode(message).doubleValue() * 8.0; } }, @@ -1228,7 +1430,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_11.decode(message).intValue(), -66); + return FastMath.scalb(DataType.INT_11.decode(message).doubleValue(), -66); } }, @@ -1237,7 +1439,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_22.decode(message).intValue(), -50); + return FastMath.scalb(DataType.INT_22.decode(message).doubleValue(), -50); } }, @@ -1246,7 +1448,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_24.decode(message).intValue(), -33); + return FastMath.scalb(DataType.INT_24.decode(message).doubleValue(), -33); } }, @@ -1264,7 +1466,7 @@ public int intValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_18.decode(message).intValue(), -6); + return FastMath.scalb(DataType.INT_18.decode(message).doubleValue(), -6); } }, @@ -1273,7 +1475,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_16.decode(message).doubleValue(), -43)); } }, @@ -1282,7 +1484,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1291,7 +1493,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_18.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_18.decode(message).doubleValue(), -31); } }, @@ -1309,7 +1511,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_18.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_18.decode(message).doubleValue(), -31); } }, @@ -1327,7 +1529,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return DataType.U_INT_17.decode(message).intValue() * 8.0; + return DataType.U_INT_17.decode(message).doubleValue() * 8.0; } }, @@ -1336,7 +1538,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_18.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_18.decode(message).doubleValue(), -31); } }, @@ -1345,7 +1547,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1354,7 +1556,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_18.decode(message).intValue(), -31); + return FastMath.scalb(DataType.INT_18.decode(message).doubleValue(), -31); } }, @@ -1363,7 +1565,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1372,7 +1574,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return FastMath.scalb(DataType.INT_18.decode(message).intValue(), -6); + return FastMath.scalb(DataType.INT_18.decode(message).doubleValue(), -6); } }, @@ -1381,7 +1583,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).intValue(), -31)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_32.decode(message).doubleValue(), -31)); } }, @@ -1390,7 +1592,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).intValue(), -43)); + return Units.SEMI_CIRCLE.toSI(FastMath.scalb(DataType.INT_24.decode(message).doubleValue(), -43)); } }, @@ -1399,7 +1601,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.NS.toSI(DataType.INT_10.decode(message).intValue() * 0.1); + return Units.NS.toSI(DataType.INT_10.decode(message).doubleValue() * 0.1); } }, @@ -1408,7 +1610,7 @@ public double doubleValue(final EncodedMessage message) { /** {@inheritDoc} */ @Override public double doubleValue(final EncodedMessage message) { - return Units.NS.toSI(DataType.INT_10.decode(message).intValue() * 0.1); + return Units.NS.toSI(DataType.INT_10.decode(message).doubleValue() * 0.1); } }, diff --git a/src/main/java/org/orekit/gnss/metric/parser/RtcmMessageType.java b/src/main/java/org/orekit/gnss/metric/parser/RtcmMessageType.java index bb48e3a7e4..2a07aff9b7 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/RtcmMessageType.java +++ b/src/main/java/org/orekit/gnss/metric/parser/RtcmMessageType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,9 @@ */ package org.orekit.gnss.metric.parser; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -24,6 +26,27 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.gnss.metric.messages.ParsedMessage; +import org.orekit.gnss.metric.messages.common.AccuracyProvider; +import org.orekit.gnss.metric.messages.common.ClockCorrection; +import org.orekit.gnss.metric.messages.common.GlonassUserRangeAccuracy; +import org.orekit.gnss.metric.messages.common.OrbitCorrection; +import org.orekit.gnss.metric.messages.common.SignalInSpaceAccuracy; +import org.orekit.gnss.metric.messages.common.SsrUpdateInterval; +import org.orekit.gnss.metric.messages.common.UserRangeAccuracy; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1057; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1058; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1060; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1063; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1064; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1066; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1240; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1241; +import org.orekit.gnss.metric.messages.rtcm.correction.Rtcm1243; +import org.orekit.gnss.metric.messages.rtcm.correction.RtcmClockCorrectionData; +import org.orekit.gnss.metric.messages.rtcm.correction.RtcmCombinedCorrectionData; +import org.orekit.gnss.metric.messages.rtcm.correction.RtcmCorrectionHeader; +import org.orekit.gnss.metric.messages.rtcm.correction.RtcmOrbitCorrectionData; +import org.orekit.gnss.metric.messages.rtcm.correction.RtcmOrbitCorrectionHeader; import org.orekit.gnss.metric.messages.rtcm.ephemeris.Rtcm1019; import org.orekit.gnss.metric.messages.rtcm.ephemeris.Rtcm1019Data; import org.orekit.gnss.metric.messages.rtcm.ephemeris.Rtcm1020; @@ -34,15 +57,11 @@ import org.orekit.gnss.metric.messages.rtcm.ephemeris.Rtcm1044Data; import org.orekit.gnss.metric.messages.rtcm.ephemeris.Rtcm1045; import org.orekit.gnss.metric.messages.rtcm.ephemeris.Rtcm1045Data; -import org.orekit.gnss.metric.messages.rtcm.ephemeris.utils.AccuracyProvider; -import org.orekit.gnss.metric.messages.rtcm.ephemeris.utils.GlonassUserRangeAccuracy; -import org.orekit.gnss.metric.messages.rtcm.ephemeris.utils.SignalInSpaceAccuracy; -import org.orekit.gnss.metric.messages.rtcm.ephemeris.utils.UserRangeAccuracy; -import org.orekit.propagation.analytical.gnss.data.BeidouNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.BeidouLegacyNavigationMessage; import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GPSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.GPSLegacyNavigationMessage; import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.QZSSNavigationMessage; +import org.orekit.propagation.analytical.gnss.data.QZSSLegacyNavigationMessage; /** Enum containing the supported RTCM messages types. * @@ -65,7 +84,7 @@ public ParsedMessage parse(final EncodedMessage encodedMessage, // Initialize data container and navigation message final Rtcm1019Data rtcm1019Data = new Rtcm1019Data(); - final GPSNavigationMessage gpsNavMessage = new GPSNavigationMessage(); + final GPSLegacyNavigationMessage gpsNavMessage = new GPSLegacyNavigationMessage(); // Set the satellite ID final int gpsId = RtcmDataField.DF009.intValue(encodedMessage); @@ -203,7 +222,7 @@ public ParsedMessage parse(final EncodedMessage encodedMessage, // Initialize data container and navigation message final Rtcm1042Data rtcm1042Data = new Rtcm1042Data(); - final BeidouNavigationMessage beidouNavMessage = new BeidouNavigationMessage(); + final BeidouLegacyNavigationMessage beidouNavMessage = new BeidouLegacyNavigationMessage(); // Set the satellite ID final int beidouId = RtcmDataField.DF488.intValue(encodedMessage); @@ -266,7 +285,7 @@ public ParsedMessage parse(final EncodedMessage encodedMessage, // Initialize data container and navigation message final Rtcm1044Data rtcm1044Data = new Rtcm1044Data(); - final QZSSNavigationMessage qzssNavMessage = new QZSSNavigationMessage(); + final QZSSLegacyNavigationMessage qzssNavMessage = new QZSSLegacyNavigationMessage(); // Set the satellite ID final int qzssId = RtcmDataField.DF429.intValue(encodedMessage); @@ -389,6 +408,543 @@ public ParsedMessage parse(final EncodedMessage encodedMessage, } + }, + + /** GPS Orbit Correction. */ + RTCM_1057("1057") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmOrbitCorrectionHeader rtcm1057Header = new RtcmOrbitCorrectionHeader(); + rtcm1057Header.setEpochTime1s(RtcmDataField.DF385.intValue(encodedMessage)); + rtcm1057Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1057Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1057Header.setSatelliteReferenceDatum(RtcmDataField.DF375.intValue(encodedMessage)); + rtcm1057Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1057Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1057Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1057Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1057Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1057SatId = RtcmDataField.DF068.intValue(encodedMessage); + + // GNSS IOD + final int rtcm1057Iod = RtcmDataField.DF071.intValue(encodedMessage); + + // Orbit correction + final OrbitCorrection rtcm1057OrbitCorr = + new OrbitCorrection(RtcmDataField.DF365.doubleValue(encodedMessage), + RtcmDataField.DF366.doubleValue(encodedMessage), + RtcmDataField.DF367.doubleValue(encodedMessage), + RtcmDataField.DF368.doubleValue(encodedMessage), + RtcmDataField.DF369.doubleValue(encodedMessage), + RtcmDataField.DF370.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmOrbitCorrectionData currentRtcm1057Data = new RtcmOrbitCorrectionData(); + currentRtcm1057Data.setSatelliteID(rtcm1057SatId); + currentRtcm1057Data.setGnssIod(rtcm1057Iod); + currentRtcm1057Data.setOrbitCorrection(rtcm1057OrbitCorr); + + // Update the list + rtcm1057Data.add(currentRtcm1057Data); + + } + + // Return the parsed message + return new Rtcm1057(messageNumber, rtcm1057Header, rtcm1057Data); + + } + + }, + + /** GPS Clock Correction. */ + RTCM_1058("1058") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmCorrectionHeader rtcm1058Header = new RtcmCorrectionHeader(); + rtcm1058Header.setEpochTime1s(RtcmDataField.DF385.intValue(encodedMessage)); + rtcm1058Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1058Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1058Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1058Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1058Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1058Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1058Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1058SatId = RtcmDataField.DF068.intValue(encodedMessage); + + // Clock correction + final ClockCorrection rtcm1058ClockCorr = + new ClockCorrection(RtcmDataField.DF376.doubleValue(encodedMessage), + RtcmDataField.DF377.doubleValue(encodedMessage), + RtcmDataField.DF378.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmClockCorrectionData currentRtcm1058Data = new RtcmClockCorrectionData(); + currentRtcm1058Data.setSatelliteID(rtcm1058SatId); + currentRtcm1058Data.setClockCorrection(rtcm1058ClockCorr); + + // Update the list + rtcm1058Data.add(currentRtcm1058Data); + + } + + // Return the parsed message + return new Rtcm1058(messageNumber, rtcm1058Header, rtcm1058Data); + + } + + }, + + /** GPS Combined Orbit and Clock Correction. */ + RTCM_1060("1060") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmOrbitCorrectionHeader rtcm1060Header = new RtcmOrbitCorrectionHeader(); + rtcm1060Header.setEpochTime1s(RtcmDataField.DF385.intValue(encodedMessage)); + rtcm1060Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1060Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1060Header.setSatelliteReferenceDatum(RtcmDataField.DF375.intValue(encodedMessage)); + rtcm1060Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1060Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1060Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1060Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1060Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1060SatId = RtcmDataField.DF068.intValue(encodedMessage); + + // GNSS IOD + final int rtcm1060Iod = RtcmDataField.DF071.intValue(encodedMessage); + + // Orbit correction + final OrbitCorrection rtcm1060OrbitCorr = + new OrbitCorrection(RtcmDataField.DF365.doubleValue(encodedMessage), + RtcmDataField.DF366.doubleValue(encodedMessage), + RtcmDataField.DF367.doubleValue(encodedMessage), + RtcmDataField.DF368.doubleValue(encodedMessage), + RtcmDataField.DF369.doubleValue(encodedMessage), + RtcmDataField.DF370.doubleValue(encodedMessage)); + + // Clock correction + final ClockCorrection rtcm1060ClockCorr = + new ClockCorrection(RtcmDataField.DF376.doubleValue(encodedMessage), + RtcmDataField.DF377.doubleValue(encodedMessage), + RtcmDataField.DF378.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmCombinedCorrectionData currentRtcm1060Data = new RtcmCombinedCorrectionData(); + currentRtcm1060Data.setSatelliteID(rtcm1060SatId); + currentRtcm1060Data.setGnssIod(rtcm1060Iod); + currentRtcm1060Data.setOrbitCorrection(rtcm1060OrbitCorr); + currentRtcm1060Data.setClockCorrection(rtcm1060ClockCorr); + + // Update the list + rtcm1060Data.add(currentRtcm1060Data); + + } + + // Return the parsed message + return new Rtcm1060(messageNumber, rtcm1060Header, rtcm1060Data); + + } + + }, + + /** GLONASS Orbit Correction. */ + RTCM_1063("1063") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmOrbitCorrectionHeader rtcm1063Header = new RtcmOrbitCorrectionHeader(); + rtcm1063Header.setEpochTime1s(RtcmDataField.DF386.intValue(encodedMessage)); + rtcm1063Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1063Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1063Header.setSatelliteReferenceDatum(RtcmDataField.DF375.intValue(encodedMessage)); + rtcm1063Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1063Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1063Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1063Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1063Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1063SatId = RtcmDataField.DF384.intValue(encodedMessage); + + // GNSS IOD + final int rtcm1063Iod = RtcmDataField.DF392.intValue(encodedMessage); + + // Orbit correction + final OrbitCorrection rtcm1063OrbitCorr = + new OrbitCorrection(RtcmDataField.DF365.doubleValue(encodedMessage), + RtcmDataField.DF366.doubleValue(encodedMessage), + RtcmDataField.DF367.doubleValue(encodedMessage), + RtcmDataField.DF368.doubleValue(encodedMessage), + RtcmDataField.DF369.doubleValue(encodedMessage), + RtcmDataField.DF370.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmOrbitCorrectionData currentRtcm1063Data = new RtcmOrbitCorrectionData(); + currentRtcm1063Data.setSatelliteID(rtcm1063SatId); + currentRtcm1063Data.setGnssIod(rtcm1063Iod); + currentRtcm1063Data.setOrbitCorrection(rtcm1063OrbitCorr); + + // Update the list + rtcm1063Data.add(currentRtcm1063Data); + + } + + // Return the parsed message + return new Rtcm1063(messageNumber, rtcm1063Header, rtcm1063Data); + + } + + }, + + /** GLONASS Clock Correction. */ + RTCM_1064("1064") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmCorrectionHeader rtcm1064Header = new RtcmCorrectionHeader(); + rtcm1064Header.setEpochTime1s(RtcmDataField.DF386.intValue(encodedMessage)); + rtcm1064Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1064Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1064Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1064Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1064Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1064Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1064Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1064SatId = RtcmDataField.DF384.intValue(encodedMessage); + + // Clock correction + final ClockCorrection rtcm1064ClockCorr = + new ClockCorrection(RtcmDataField.DF376.doubleValue(encodedMessage), + RtcmDataField.DF377.doubleValue(encodedMessage), + RtcmDataField.DF378.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmClockCorrectionData currentRtcm1058Data = new RtcmClockCorrectionData(); + currentRtcm1058Data.setSatelliteID(rtcm1064SatId); + currentRtcm1058Data.setClockCorrection(rtcm1064ClockCorr); + + // Update the list + rtcm1064Data.add(currentRtcm1058Data); + + } + + // Return the parsed message + return new Rtcm1064(messageNumber, rtcm1064Header, rtcm1064Data); + + } + + }, + + /** GLONASS Combined Orbit and Clock Correction. */ + RTCM_1066("1066") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmOrbitCorrectionHeader rtcm1066Header = new RtcmOrbitCorrectionHeader(); + rtcm1066Header.setEpochTime1s(RtcmDataField.DF386.intValue(encodedMessage)); + rtcm1066Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1066Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1066Header.setSatelliteReferenceDatum(RtcmDataField.DF375.intValue(encodedMessage)); + rtcm1066Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1066Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1066Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1066Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1066Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1066SatId = RtcmDataField.DF384.intValue(encodedMessage); + + // GNSS IOD + final int rtcm1066Iod = RtcmDataField.DF392.intValue(encodedMessage); + + // Orbit correction + final OrbitCorrection rtcm1066OrbitCorr = + new OrbitCorrection(RtcmDataField.DF365.doubleValue(encodedMessage), + RtcmDataField.DF366.doubleValue(encodedMessage), + RtcmDataField.DF367.doubleValue(encodedMessage), + RtcmDataField.DF368.doubleValue(encodedMessage), + RtcmDataField.DF369.doubleValue(encodedMessage), + RtcmDataField.DF370.doubleValue(encodedMessage)); + + // Clock correction + final ClockCorrection rtcm1066ClockCorr = + new ClockCorrection(RtcmDataField.DF376.doubleValue(encodedMessage), + RtcmDataField.DF377.doubleValue(encodedMessage), + RtcmDataField.DF378.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmCombinedCorrectionData currentRtcm1066Data = new RtcmCombinedCorrectionData(); + currentRtcm1066Data.setSatelliteID(rtcm1066SatId); + currentRtcm1066Data.setGnssIod(rtcm1066Iod); + currentRtcm1066Data.setOrbitCorrection(rtcm1066OrbitCorr); + currentRtcm1066Data.setClockCorrection(rtcm1066ClockCorr); + + // Update the list + rtcm1066Data.add(currentRtcm1066Data); + + } + + // Return the parsed message + return new Rtcm1066(messageNumber, rtcm1066Header, rtcm1066Data); + + } + + }, + + /** Galileo Orbit Correction Message. */ + RTCM_1240("1240") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmOrbitCorrectionHeader rtcm1240Header = new RtcmOrbitCorrectionHeader(); + rtcm1240Header.setEpochTime1s(RtcmDataField.DF458.intValue(encodedMessage)); + rtcm1240Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1240Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1240Header.setSatelliteReferenceDatum(RtcmDataField.DF375.intValue(encodedMessage)); + rtcm1240Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1240Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1240Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1240Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1240Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1240SatId = RtcmDataField.DF252.intValue(encodedMessage); + + // GNSS IOD + final int rtcm1240Iod = RtcmDataField.DF290.intValue(encodedMessage); + + // Orbit correction + final OrbitCorrection rtcm1240OrbitCorr = + new OrbitCorrection(RtcmDataField.DF365.doubleValue(encodedMessage), + RtcmDataField.DF366.doubleValue(encodedMessage), + RtcmDataField.DF367.doubleValue(encodedMessage), + RtcmDataField.DF368.doubleValue(encodedMessage), + RtcmDataField.DF369.doubleValue(encodedMessage), + RtcmDataField.DF370.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmOrbitCorrectionData currentRtcm1240Data = new RtcmOrbitCorrectionData(); + currentRtcm1240Data.setSatelliteID(rtcm1240SatId); + currentRtcm1240Data.setGnssIod(rtcm1240Iod); + currentRtcm1240Data.setOrbitCorrection(rtcm1240OrbitCorr); + + // Update the list + rtcm1240Data.add(currentRtcm1240Data); + + } + + // Return the parsed message + return new Rtcm1240(messageNumber, rtcm1240Header, rtcm1240Data); + + } + + }, + + /** Galileo Clock Correction Message. */ + RTCM_1241("1241") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmCorrectionHeader rtcm1241Header = new RtcmCorrectionHeader(); + rtcm1241Header.setEpochTime1s(RtcmDataField.DF458.intValue(encodedMessage)); + rtcm1241Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1241Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1241Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1241Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1241Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1241Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1241Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1241SatId = RtcmDataField.DF252.intValue(encodedMessage); + + // Clock correction + final ClockCorrection rtcm1241ClockCorr = + new ClockCorrection(RtcmDataField.DF376.doubleValue(encodedMessage), + RtcmDataField.DF377.doubleValue(encodedMessage), + RtcmDataField.DF378.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmClockCorrectionData currentRtcm1241Data = new RtcmClockCorrectionData(); + currentRtcm1241Data.setSatelliteID(rtcm1241SatId); + currentRtcm1241Data.setClockCorrection(rtcm1241ClockCorr); + + // Update the list + rtcm1241Data.add(currentRtcm1241Data); + + } + + // Return the parsed message + return new Rtcm1241(messageNumber, rtcm1241Header, rtcm1241Data); + + } + + }, + + /** Galileo Combined Orbit and Clock Correction Message. */ + RTCM_1243("1243") { + + /** {@inheritDoc} */ + @Override + public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) { + + // Header data + final RtcmOrbitCorrectionHeader rtcm1243Header = new RtcmOrbitCorrectionHeader(); + rtcm1243Header.setEpochTime1s(RtcmDataField.DF458.intValue(encodedMessage)); + rtcm1243Header.setSsrUpdateInterval(new SsrUpdateInterval(RtcmDataField.DF391.intValue(encodedMessage))); + rtcm1243Header.setMultipleMessageIndicator(RtcmDataField.DF388.intValue(encodedMessage)); + rtcm1243Header.setSatelliteReferenceDatum(RtcmDataField.DF375.intValue(encodedMessage)); + rtcm1243Header.setIodSsr(RtcmDataField.DF413.intValue(encodedMessage)); + rtcm1243Header.setSsrProviderId(RtcmDataField.DF414.intValue(encodedMessage)); + rtcm1243Header.setSsrSolutionId(RtcmDataField.DF415.intValue(encodedMessage)); + + // Number of satellites + final int satNumber = RtcmDataField.DF387.intValue(encodedMessage); + rtcm1243Header.setNumberOfSatellites(satNumber); + + // Initialize list of data + final List rtcm1243Data = new ArrayList<>(); + + // Loop on satellites and fill data + for (int index = 0; index < satNumber; index++) { + + // Satellite ID + final int rtcm1243SatId = RtcmDataField.DF252.intValue(encodedMessage); + + // GNSS IOD + final int rtcm1243Iod = RtcmDataField.DF290.intValue(encodedMessage); + + // Orbit correction + final OrbitCorrection rtcm1243OrbitCorr = + new OrbitCorrection(RtcmDataField.DF365.doubleValue(encodedMessage), + RtcmDataField.DF366.doubleValue(encodedMessage), + RtcmDataField.DF367.doubleValue(encodedMessage), + RtcmDataField.DF368.doubleValue(encodedMessage), + RtcmDataField.DF369.doubleValue(encodedMessage), + RtcmDataField.DF370.doubleValue(encodedMessage)); + + // Clock correction + final ClockCorrection rtcm1243ClockCorr = + new ClockCorrection(RtcmDataField.DF376.doubleValue(encodedMessage), + RtcmDataField.DF377.doubleValue(encodedMessage), + RtcmDataField.DF378.doubleValue(encodedMessage)); + + // Initialize a new container and fill data + final RtcmCombinedCorrectionData currentRtcm1243Data = new RtcmCombinedCorrectionData(); + currentRtcm1243Data.setSatelliteID(rtcm1243SatId); + currentRtcm1243Data.setGnssIod(rtcm1243Iod); + currentRtcm1243Data.setOrbitCorrection(rtcm1243OrbitCorr); + currentRtcm1243Data.setClockCorrection(rtcm1243ClockCorr); + + // Update the list + rtcm1243Data.add(currentRtcm1243Data); + + } + + // Return the parsed message + return new Rtcm1243(messageNumber, rtcm1243Header, rtcm1243Data); + + } + }; /** Codes map. */ diff --git a/src/main/java/org/orekit/gnss/metric/parser/RtcmMessagesParser.java b/src/main/java/org/orekit/gnss/metric/parser/RtcmMessagesParser.java index e6439d6364..b076af7657 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/RtcmMessagesParser.java +++ b/src/main/java/org/orekit/gnss/metric/parser/RtcmMessagesParser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/Units.java b/src/main/java/org/orekit/gnss/metric/parser/Units.java index 07e9de4c0c..803ced3ddc 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/Units.java +++ b/src/main/java/org/orekit/gnss/metric/parser/Units.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/metric/parser/package-info.java b/src/main/java/org/orekit/gnss/metric/parser/package-info.java index 315f6535c9..8a0dcb0e2a 100644 --- a/src/main/java/org/orekit/gnss/metric/parser/package-info.java +++ b/src/main/java/org/orekit/gnss/metric/parser/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ * * This package provides the top level {@link org.orekit.gnss.metric.parser.MessagesParser * MessagesParser} class that can extract {@link org.orekit.gnss.metric.messages.ParsedMessage - * messages} from binary containers like {@link org.orekit.gnss.metric.parser.ByteArrayEncodedMessages + * messages} from binary containers like {@link org.orekit.gnss.metric.parser.ByteArrayEncodedMessage * ByteArrayEncodedMessages} or {link org.orekit.metric.gnss.ssr.parser.InputStreamEncodedMessages * InputStreamEncodedMessages}. * diff --git a/src/main/java/org/orekit/gnss/navigation/RinexNavigation.java b/src/main/java/org/orekit/gnss/navigation/RinexNavigation.java deleted file mode 100644 index 4267630a91..0000000000 --- a/src/main/java/org/orekit/gnss/navigation/RinexNavigation.java +++ /dev/null @@ -1,697 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.gnss.navigation; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.orekit.gnss.SatelliteSystem; -import org.orekit.models.earth.ionosphere.KlobucharIonoModel; -import org.orekit.models.earth.ionosphere.NeQuickModel; -import org.orekit.propagation.analytical.gnss.data.BeidouNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GPSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.IRNSSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.QZSSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.SBASNavigationMessage; -import org.orekit.time.AbsoluteDate; - -/** - * Represents a parsed RINEX navigation messages files. - * @author Bryan Cazabonne - * @since 11.0 - */ -public class RinexNavigation { - - /** Format version. */ - private double formatVersion; - - /** File type ('N' for navigation data). */ - private String fileType; - - /** Satellite system. */ - private SatelliteSystem satelliteSystem; - - /** Name of the program creating current file. */ - private String programName; - - /** Name of the agency creating the current file. */ - private String agencyName; - - /** Date of the file creation as a string. */ - private String creationDateString; - - /** Time of the file creation as a string. */ - private String creationTimeString; - - /** Time zone of the file creation as a string. */ - private String creationTimeZoneString; - - /** Creation date as absolute date. */ - private AbsoluteDate creationDate; - - /** Comments. */ - private String comments; - - /** Ionospheric correction type. */ - private String ionosphericCorrectionType; - - /** The 4 Klobuchar coefficients of a cubic equation representing the amplitude of the vertical delay. */ - private double[] klobucharAlpha; - - /** The 4 coefficients of a cubic equation representing the period of the model. */ - private double[] klobucharBeta; - - /** The three ionospheric coefficients broadcast in the Galileo navigation message. */ - private double[] neQuickAlpha; - - /** List of time system corrections. */ - private List timeSystemCorrections; - - /** Current number of leap seconds. */ - private int numberOfLeapSeconds; - - /** A map containing the GPS navigation messages. */ - private Map> gpsData; - - /** A map containing the Galileo navigation messages. */ - private Map> galileoData; - - /** A map containing the Beidou navigation messages. */ - private Map> beidouData; - - /** A map containing the QZSS navigation messages. */ - private Map> qzssData; - - /** A map containing the IRNSS navigation messages. */ - private Map> irnssData; - - /** A map containing the GLONASS navigation messages. */ - private Map> glonassData; - - /** A map containing the SBAS navigation messages. */ - private Map> sbasData; - - /** Constructor. */ - public RinexNavigation() { - this.comments = ""; - this.timeSystemCorrections = new ArrayList<>(); - this.gpsData = new HashMap<>(); - this.galileoData = new HashMap<>(); - this.beidouData = new HashMap<>(); - this.qzssData = new HashMap<>(); - this.irnssData = new HashMap<>(); - this.glonassData = new HashMap<>(); - this.sbasData = new HashMap<>(); - } - - /** - * Getter for the format version. - * @return the format version - */ - public double getFormatVersion() { - return formatVersion; - } - - /** - * Setter for the format version. - * @param formatVersion the format version to set - */ - public void setFormatVersion(final double formatVersion) { - this.formatVersion = formatVersion; - } - - /** - * Get the file type. - * @return 'N' for navigation data. - */ - public String getFileType() { - return fileType; - } - - /** - * Setter for the file type. - * @param fileType must be 'N' for navigation data - */ - public void setFileType(final String fileType) { - this.fileType = fileType; - } - - /** - * Getter for the satellite system. - *

        - * Not specified for RINEX 2.X versions (value is null). - *

        - * @return the satellite system - */ - public SatelliteSystem getSatelliteSystem() { - return satelliteSystem; - } - - /** - * Setter for the satellite system. - * @param satelliteSystem the satellite system to set - */ - public void setSatelliteSystem(final SatelliteSystem satelliteSystem) { - this.satelliteSystem = satelliteSystem; - } - - /** - * Getter for the program name. - * @return the program name - */ - public String getProgramName() { - return programName; - } - - /** - * Setter for the program name. - * @param programName the program name to set - */ - public void setProgramName(final String programName) { - this.programName = programName; - } - - /** - * Getter for the agency name. - * @return the agencyName - */ - public String getAgencyName() { - return agencyName; - } - - /** - * Setter for the agency name. - * @param agencyName the agency name to set - */ - public void setAgencyName(final String agencyName) { - this.agencyName = agencyName; - } - - /** - * Getter for the creation date of the file as a string. - * @return the creation date as a string - */ - public String getCreationDateString() { - return creationDateString; - } - - /** - * Setter for the creation date as a string. - * @param creationDateString the creation date as a string to set - */ - public void setCreationDateString(final String creationDateString) { - this.creationDateString = creationDateString; - } - - /** - * Getter for the creation time of the file as a string. - * @return the creation time as a string - */ - public String getCreationTimeString() { - return creationTimeString; - } - - /** - * Setter for the creation time as a string. - * @param creationTimeString the creation time as a string to set - */ - public void setCreationTimeString(final String creationTimeString) { - this.creationTimeString = creationTimeString; - } - - /** - * Getter for the creation time zone of the file as a string. - * @return the creation time zone as a string - */ - public String getCreationTimeZoneString() { - return creationTimeZoneString; - } - - /** - * Setter for the creation time zone. - * @param creationTimeZoneString the creation time zone as a string to set - */ - public void setCreationTimeZoneString(final String creationTimeZoneString) { - this.creationTimeZoneString = creationTimeZoneString; - } - - /** - * Getter for the creation date. - * @return the creation date - */ - public AbsoluteDate getCreationDate() { - return creationDate; - } - - /** - * Setter for the creation date. - * @param creationDate the creation date to set - */ - public void setCreationDate(final AbsoluteDate creationDate) { - this.creationDate = creationDate; - } - - /** - * Getter for the comments. - * @return the comments - */ - public String getComments() { - return comments; - } - - /** - * Add a comment line. - * @param comment the comment line to add - */ - public void addComment(final String comment) { - this.comments = comments.concat(comment); - } - - /** - * Getter for the ionospheric correction type. - *

        - * Only the three first characters are given (e.g. GAL, GPS, QZS, BDS, or IRN) - *

        - * @return the ionospheric correction type - */ - public String getIonosphericCorrectionType() { - return ionosphericCorrectionType; - } - - /** - * Setter for the ionospheric correction type. - * @param ionosphericCorrectionType the ionospheric correction type to set - */ - public void setIonosphericCorrectionType(final String ionosphericCorrectionType) { - this.ionosphericCorrectionType = ionosphericCorrectionType; - } - - /** - * Get the "alpha" ionospheric parameters. - *

        - * They are used to initialize the {@link KlobucharIonoModel}. - *

        - * @return the "alpha" ionospheric parameters - */ - public double[] getKlobucharAlpha() { - return klobucharAlpha.clone(); - } - - /** - * Set the "alpha" ionspheric parameters. - * @param klobucharAlpha the "alpha" ionspheric parameters to set - */ - public void setKlobucharAlpha(final double[] klobucharAlpha) { - this.klobucharAlpha = klobucharAlpha.clone(); - } - - /** - * Get the "beta" ionospheric parameters. - *

        - * They are used to initialize the {@link KlobucharIonoModel}. - *

        - * @return the "beta" ionospheric parameters - */ - public double[] getKlobucharBeta() { - return klobucharBeta.clone(); - } - - /** - * Set the "beta" ionospheric parameters. - * @param klobucharBeta the "beta" ionospheric parameters to set - */ - public void setKlobucharBeta(final double[] klobucharBeta) { - this.klobucharBeta = klobucharBeta.clone(); - } - - /** - * Get the "alpha" ionospheric parameters. - *

        - * They are used to initialize the {@link NeQuickModel}. - *

        - * @return the "alpha" ionospheric parameters - */ - public double[] getNeQuickAlpha() { - return neQuickAlpha.clone(); - } - - /** - * Set the "alpha" ionospheric parameters. - * @param neQuickAlpha the "alpha" ionospheric parameters to set - */ - public void setNeQuickAlpha(final double[] neQuickAlpha) { - this.neQuickAlpha = neQuickAlpha.clone(); - } - - /** - * Getter for the time system corrections contained in the file header. - *

        - * Corrections to transform the system time to UTC or oter time system. - *

        - * @return the list of time system corrections - */ - public List getTimeSystemCorrections() { - return timeSystemCorrections; - } - - /** - * Add a time system correction to the list. - * @param timeSystemCorrection the element to add - */ - public void addTimeSystemCorrections(final TimeSystemCorrection timeSystemCorrection) { - this.timeSystemCorrections.add(timeSystemCorrection); - } - - /** - * Getter for the current number of leap seconds. - * @return the current number of leap seconds - */ - public int getNumberOfLeapSeconds() { - return numberOfLeapSeconds; - } - - /** - * Setter for the current number of leap seconds. - * @param numberOfLeapSeconds the number of leap seconds to set - */ - public void setNumberOfLeapSeconds(final int numberOfLeapSeconds) { - this.numberOfLeapSeconds = numberOfLeapSeconds; - } - - /** - * Get all the GPS navigation messages contained in the file. - * @return an unmodifiable list of GPS navigation messages - */ - public Map> getGPSNavigationMessages() { - return Collections.unmodifiableMap(gpsData); - } - - /** - * Get the GPS navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. G) + satellite number) - * @return an unmodifiable list of GPS navigation messages - */ - public List getGPSNavigationMessages(final String satId) { - return Collections.unmodifiableList(gpsData.get(satId)); - } - - /** - * Add a GPS navigation message to the list. - * @param message message to add - */ - public void addGPSNavigationMessage(final GPSNavigationMessage message) { - final int gpsPRN = message.getPRN(); - final String prnString = gpsPRN < 10 ? "0" + String.valueOf(gpsPRN) : String.valueOf(gpsPRN); - final String satId = SatelliteSystem.GPS.getKey() + prnString; - gpsData.putIfAbsent(satId, new ArrayList()); - gpsData.get(satId).add(message); - } - - /** - * Get all the Galileo navigation messages contained in the file. - * @return an unmodifiable list of Galileo navigation messages - */ - public Map> getGalileoNavigationMessages() { - return Collections.unmodifiableMap(galileoData); - } - - /** - * Get the Galileo navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. E) + satellite number) - * @return an unmodifiable list of Galileo navigation messages - */ - public List getGalileoNavigationMessages(final String satId) { - return Collections.unmodifiableList(galileoData.get(satId)); - } - - /** - * Add a Galileo navigation message to the list. - * @param message message to add - */ - public void addGalileoNavigationMessage(final GalileoNavigationMessage message) { - final int galPRN = message.getPRN(); - final String prnString = galPRN < 10 ? "0" + String.valueOf(galPRN) : String.valueOf(galPRN); - final String satId = SatelliteSystem.GALILEO.getKey() + prnString; - galileoData.putIfAbsent(satId, new ArrayList()); - galileoData.get(satId).add(message); - } - - /** - * Get all the Beidou navigation messages contained in the file. - * @return an unmodifiable list of Beidou navigation messages - */ - public Map> getBeidouNavigationMessages() { - return Collections.unmodifiableMap(beidouData); - } - - /** - * Get the Beidou navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. C) + satellite number) - * @return an unmodifiable list of Beidou navigation messages - */ - public List getBeidouNavigationMessages(final String satId) { - return Collections.unmodifiableList(beidouData.get(satId)); - } - - /** - * Add a Beidou navigation message to the list. - * @param message message to add - */ - public void addBeidouNavigationMessage(final BeidouNavigationMessage message) { - final int bdtPRN = message.getPRN(); - final String prnString = bdtPRN < 10 ? "0" + String.valueOf(bdtPRN) : String.valueOf(bdtPRN); - final String satId = SatelliteSystem.BEIDOU.getKey() + prnString; - beidouData.putIfAbsent(satId, new ArrayList()); - beidouData.get(satId).add(message); - } - - /** - * Get all the QZSS navigation messages contained in the file. - * @return an unmodifiable list of QZSS navigation messages - */ - public Map> getQZSSNavigationMessages() { - return Collections.unmodifiableMap(qzssData); - } - - /** - * Get the QZSS navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. J) + satellite number) - * @return an unmodifiable list of QZSS navigation messages - */ - public List getQZSSNavigationMessages(final String satId) { - return Collections.unmodifiableList(qzssData.get(satId)); - } - - /** - * Add a QZSS navigation message to the list. - * @param message message to add - */ - public void addQZSSNavigationMessage(final QZSSNavigationMessage message) { - final int qzsPRN = message.getPRN(); - final String prnString = qzsPRN < 10 ? "0" + String.valueOf(qzsPRN) : String.valueOf(qzsPRN); - final String satId = SatelliteSystem.QZSS.getKey() + prnString; - qzssData.putIfAbsent(satId, new ArrayList()); - qzssData.get(satId).add(message); - } - - /** - * Get all the IRNSS navigation messages contained in the file. - * @return an unmodifiable list of IRNSS navigation messages - */ - public Map> getIRNSSNavigationMessages() { - return Collections.unmodifiableMap(irnssData); - } - - /** - * Get the IRNSS navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. I) + satellite number) - * @return an unmodifiable list of IRNSS navigation messages - */ - public List getIRNSSNavigationMessages(final String satId) { - return Collections.unmodifiableList(irnssData.get(satId)); - } - - /** - * Add a IRNSS navigation message to the list. - * @param message message to add - */ - public void addIRNSSNavigationMessage(final IRNSSNavigationMessage message) { - final int irsPRN = message.getPRN(); - final String prnString = irsPRN < 10 ? "0" + String.valueOf(irsPRN) : String.valueOf(irsPRN); - final String satId = SatelliteSystem.IRNSS.getKey() + prnString; - irnssData.putIfAbsent(satId, new ArrayList()); - irnssData.get(satId).add(message); - } - - /** - * Get all the Glonass navigation messages contained in the file. - * @return an unmodifiable list of Glonass navigation messages - */ - public Map> getGlonassNavigationMessages() { - return Collections.unmodifiableMap(glonassData); - } - - /** - * Get the Glonass navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. R) + satellite number) - * @return an unmodifiable list of Glonass navigation messages - */ - public List getGlonassNavigationMessages(final String satId) { - return Collections.unmodifiableList(glonassData.get(satId)); - } - - /** - * Add a Glonass navigation message to the list. - * @param message message to add - */ - public void addGlonassNavigationMessage(final GLONASSNavigationMessage message) { - final int gloPRN = message.getPRN(); - final String prnString = gloPRN < 10 ? "0" + String.valueOf(gloPRN) : String.valueOf(gloPRN); - final String satId = SatelliteSystem.GLONASS.getKey() + prnString; - glonassData.putIfAbsent(satId, new ArrayList()); - glonassData.get(satId).add(message); - } - - /** - * Get all the SBAS navigation messages contained in the file. - * @return an unmodifiable list of SBAS navigation messages - */ - public Map> getSBASNavigationMessages() { - return Collections.unmodifiableMap(sbasData); - } - - /** - * Get the SBAS navigation messages for the given satellite Id. - * @param satId satellite Id (i.e. Satellite System (e.g. S) + satellite number) - * @return an unmodifiable list of SBAS navigation messages - */ - public List getSBASNavigationMessages(final String satId) { - return Collections.unmodifiableList(sbasData.get(satId)); - } - - /** - * Add a SBAS navigation message to the list. - * @param message message to add - */ - public void addSBASNavigationMessage(final SBASNavigationMessage message) { - final int sbsPRN = message.getPRN(); - final String prnString = sbsPRN < 10 ? "0" + String.valueOf(sbsPRN) : String.valueOf(sbsPRN); - final String satId = SatelliteSystem.SBAS.getKey() + prnString; - sbasData.putIfAbsent(satId, new ArrayList()); - sbasData.get(satId).add(message); - } - - /** Container for time system corrections. */ - public static class TimeSystemCorrection { - - /** Time system correction type. */ - private String timeSystemCorrectionType; - - /** A0 coefficient of linear polynomial for time system correction. */ - private double timeSystemCorrectionA0; - - /** A1 coefficient of linear polynomial for time system correction. */ - private double timeSystemCorrectionA1; - - /** Reference time for time system correction (seconds into GNSS week). */ - private int timeSystemCorrectionSecOfWeek; - - /** Reference week number for time system correction. */ - private int timeSystemCorrectionWeekNumber; - - /** - * Constructor. - * @param timeSystemCorrectionType time system correction type - * @param timeSystemCorrectionA0 A0 coefficient of linear polynomial for time system correction - * @param timeSystemCorrectionA1 A1 coefficient of linear polynomial for time system correction - * @param timeSystemCorrectionSecOfWeek reference time for time system correction - * @param timeSystemCorrectionWeekNumber reference week number for time system correction - */ - public TimeSystemCorrection(final String timeSystemCorrectionType, - final double timeSystemCorrectionA0, - final double timeSystemCorrectionA1, - final int timeSystemCorrectionSecOfWeek, - final int timeSystemCorrectionWeekNumber) { - this.timeSystemCorrectionType = timeSystemCorrectionType; - this.timeSystemCorrectionA0 = timeSystemCorrectionA0; - this.timeSystemCorrectionA1 = timeSystemCorrectionA1; - this.timeSystemCorrectionSecOfWeek = timeSystemCorrectionSecOfWeek; - this.timeSystemCorrectionWeekNumber = timeSystemCorrectionWeekNumber; - } - - /** - * Getter for the time system correction type. - * @return the time system correction type - */ - public String getTimeSystemCorrectionType() { - return timeSystemCorrectionType; - } - - /** - * Getter for the A0 coefficient of the time system correction. - *

        - * deltaT = {@link #getTimeSystemCorrectionA0() A0} + - * {@link #getTimeSystemCorrectionA1() A1} * (t - tref) - *

        - * @return the A0 coefficient of the time system correction - */ - public double getTimeSystemCorrectionA0() { - return timeSystemCorrectionA0; - } - - /** - * Getter for the A1 coefficient of the time system correction. - *

        - * deltaT = {@link #getTimeSystemCorrectionA0() A0} + - * {@link #getTimeSystemCorrectionA1() A1} * (t - tref) - *

        - * @return the A1 coefficient of the time system correction - */ - public double getTimeSystemCorrectionA1() { - return timeSystemCorrectionA1; - } - - /** - * Getter for the reference time of the time system correction polynomial. - *

        - * Seconds into GNSS week - *

        - * @return the reference time of the time system correction polynomial - */ - public int getTimeSystemCorrectionSecOfWeek() { - return timeSystemCorrectionSecOfWeek; - } - - /** - * Getter for the reference week number of the time system correction polynomial. - *

        - * Continuous number since the reference epoch of the corresponding GNSS constellation - *

        - * @return the reference week number of the time system correction polynomial - */ - public int getTimeSystemCorrectionWeekNumber() { - return timeSystemCorrectionWeekNumber; - } - - } - -} diff --git a/src/main/java/org/orekit/gnss/navigation/RinexNavigationParser.java b/src/main/java/org/orekit/gnss/navigation/RinexNavigationParser.java deleted file mode 100644 index 225aba81ac..0000000000 --- a/src/main/java/org/orekit/gnss/navigation/RinexNavigationParser.java +++ /dev/null @@ -1,1455 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.gnss.navigation; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.util.Arrays; -import java.util.HashMap; -import java.util.InputMismatchException; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -import org.orekit.annotation.DefaultDataContext; -import org.orekit.data.DataContext; -import org.orekit.data.DataSource; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.gnss.SatelliteSystem; -import org.orekit.gnss.TimeSystem; -import org.orekit.gnss.navigation.RinexNavigation.TimeSystemCorrection; -import org.orekit.propagation.analytical.gnss.data.BeidouNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GPSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.IRNSSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.QZSSNavigationMessage; -import org.orekit.propagation.analytical.gnss.data.SBASNavigationMessage; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.DateComponents; -import org.orekit.time.GNSSDate; -import org.orekit.time.TimeComponents; -import org.orekit.time.TimeScale; -import org.orekit.time.TimeScales; -import org.orekit.utils.Constants; - -/** - * Parser for RINEX navigation messages files. - *

        - * This parser handles RINEX version from 3.01 to 3.05. It is not adapted for RINEX 2.10 and 2.11 versions. - *

        - * @see 3.01 navigation messages file format - * @see 3.02 navigation messages file format - * @see 3.03 navigation messages file format - * @see 3.04 navigation messages file format - * @see 3.05 navigation messages file format - * - * @author Bryan Cazabonne - * @since 11.0 - * - */ -public class RinexNavigationParser { - - /** Handled clock file format versions. */ - private static final List HANDLED_VERSIONS = Arrays.asList(3.01, 3.02, 3.03, 3.04, 3.05); - - /** File Type. */ - private static final String FILE_TYPE = "N"; - - /** Seconds to milliseconds converter. */ - private static final Double SEC_TO_MILLI = 1000.0; - - /** Kilometer to meters converter. */ - private static final Double KM_TO_M = 1000.0; - - /** Set of time scales. */ - private final TimeScales timeScales; - - /** - * Constructor. - *

        This constructor uses the {@link DataContext#getDefault() default data context}.

        - * @see #RinexNavigationParser(TimeScales) - * - */ - @DefaultDataContext - public RinexNavigationParser() { - this(DataContext.getDefault().getTimeScales()); - } - - /** - * Constructor. - * @param timeScales the set of time scales used for parsing dates. - */ - public RinexNavigationParser(final TimeScales timeScales) { - this.timeScales = timeScales; - } - - /** - * Parse RINEX navigation messages. - * @param source source providing the data to parse - * @return a parsed RINEX navigation messages file - * @throws IOException if {@code reader} throws one - */ - public RinexNavigation parse(final DataSource source) throws IOException { - - // initialize internal data structures - final ParseInfo pi = new ParseInfo(); - - int lineNumber = 0; - Stream candidateParsers = Stream.of(LineParser.HEADER_VERSION); - try (Reader reader = source.getOpener().openReaderOnce(); - BufferedReader br = new BufferedReader(reader)) { - for (String line = br.readLine(); line != null; line = br.readLine()) { - ++lineNumber; - final String l = line; - final Optional selected = candidateParsers.filter(p -> p.canHandle(l)).findFirst(); - if (selected.isPresent()) { - try { - selected.get().parse(line, pi); - } catch (StringIndexOutOfBoundsException | NumberFormatException | InputMismatchException e) { - throw new OrekitException(e, - OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, source.getName(), line); - } - candidateParsers = selected.get().allowedNext(); - } else { - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, source.getName(), line); - } - } - } - - return pi.file; - - } - - /** - * Parse a double value. - * @param line line to parse - * @param startIndex start index - * @param size size of the value - * @return the parsed value - */ - private static double parseDouble(final String line, final int startIndex, final int size) { - return Double.parseDouble(line.substring(startIndex, startIndex + size).replace('D', 'E').trim()); - } - - /** - * Parse an integer value. - * @param line line to parse - * @param startIndex start index - * @param size size of the value - * @return the parsed value - */ - private static int parseInt(final String line, final int startIndex, final int size) { - return Integer.parseInt(line.substring(startIndex, startIndex + size).trim()); - } - - /** - * Parse a string value. - * @param line line to parse - * @param startIndex start index - * @param size size of the value - * @return the parsed value - */ - private static String parseString(final String line, final int startIndex, final int size) { - return line.substring(startIndex, startIndex + size).trim(); - } - - /** Transient data used for parsing a RINEX navigation messages file. */ - private class ParseInfo { - - /** Set of time scales for parsing dates. */ - private final TimeScales timeScales; - - /** The corresponding navigation messages file object. */ - private RinexNavigation file; - - /** The version of the navigation file. */ - private double version; - - /** Flag indicating the distinction between "alpha" and "beta" ionospheric coefficients. */ - private boolean isIonosphereAlphaInitialized; - - /** Satellite system line parser. */ - private SatelliteSystemLineParser systemLineParser; - - /** Current line number of the navigation message. */ - private int lineNumber; - - /** Container for GPS navigation message. */ - private GPSNavigationMessage gpsNav; - - /** Container for Galileo navigation message. */ - private GalileoNavigationMessage galileoNav; - - /** Container for Beidou navigation message. */ - private BeidouNavigationMessage beidouNav; - - /** Container for QZSS navigation message. */ - private QZSSNavigationMessage qzssNav; - - /** Container for IRNSS navigation message. */ - private IRNSSNavigationMessage irnssNav; - - /** Container for GLONASS navigation message. */ - private GLONASSNavigationMessage glonassNav; - - /** Container for SBAS navigation message. */ - private SBASNavigationMessage sbasNav; - - /** Constructor, build the ParseInfo object. */ - ParseInfo() { - // Initialize default values for fields - this.timeScales = RinexNavigationParser.this.timeScales; - this.version = 1.0; - this.isIonosphereAlphaInitialized = false; - this.file = new RinexNavigation(); - this.systemLineParser = SatelliteSystemLineParser.GPS; - this.lineNumber = 0; - this.gpsNav = new GPSNavigationMessage(); - this.galileoNav = new GalileoNavigationMessage(); - this.beidouNav = new BeidouNavigationMessage(); - this.qzssNav = new QZSSNavigationMessage(); - this.irnssNav = new IRNSSNavigationMessage(); - this.glonassNav = new GLONASSNavigationMessage(); - this.sbasNav = new SBASNavigationMessage(); - } - - } - - /** Parsers for specific lines. */ - private enum LineParser { - - /** Parser for version, file type and satellite system. */ - HEADER_VERSION("^.+RINEX VERSION / TYPE( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - - // Rinex version - pi.version = parseDouble(line, 0, 9); - - // Throw exception if format version is not handled - if (!HANDLED_VERSIONS.contains(pi.version)) { - throw new OrekitException(OrekitMessages.NAVIGATION_FILE_UNSUPPORTED_VERSION, pi.version); - } - pi.file.setFormatVersion(pi.version); - - // File type - pi.file.setFileType(FILE_TYPE); - - // Satellite system - final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(parseString(line, 40, 1)); - pi.file.setSatelliteSystem(system); - - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(HEADER_PROGRAM); - } - - }, - - /** Parser for generating program and emiting agency. */ - HEADER_PROGRAM("^.+PGM / RUN BY / DATE( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - - // Name of the generating program - final String programName = parseString(line, 0, 20); - pi.file.setProgramName(programName); - - // Name of the emiting agency - final String agencyName = parseString(line, 20, 20); - pi.file.setAgencyName(agencyName); - - // Date and time of file creation - final String date = parseString(line, 40, 8); - final String time = parseString(line, 49, 6); - final String timeZone = parseString(line, 56, 4); - pi.file.setCreationDateString(date); - pi.file.setCreationTimeString(time); - pi.file.setCreationTimeZoneString(timeZone); - - // Convert date and time to an Orekit absolute date - final DateComponents dateComponents = new DateComponents(parseInt(date, 0, 4), - parseInt(date, 4, 2), - parseInt(date, 6, 2)); - final TimeComponents timeComponents = new TimeComponents(parseInt(time, 0, 2), - parseInt(time, 2, 2), - parseInt(time, 4, 2)); - pi.file.setCreationDate(new AbsoluteDate(dateComponents, - timeComponents, - TimeSystem.parseTimeSystem(timeZone).getTimeScale(pi.timeScales))); - - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END); - } - - }, - - /** Parser for comments. */ - HEADER_COMMENT("^.+COMMENT( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - pi.file.addComment(parseString(line, 0, 60)); - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END); - } - - }, - - /** Parser for ionospheric correction parameters. */ - HEADER_IONOSPHERIC("^.+IONOSPHERIC CORR( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - - // Satellite system - final String ionoType = parseString(line, 0, 3); - pi.file.setIonosphericCorrectionType(ionoType); - - // Read coefficients - final double[] parameters = new double[4]; - parameters[0] = parseDouble(line, 5, 12); - parameters[1] = parseDouble(line, 17, 12); - parameters[2] = parseDouble(line, 29, 12); - parameters[3] = parseDouble(line, 41, 12); - - // Verify if we are parsing Galileo ionospheric parameters - if ("GAL".equals(ionoType)) { - - // We are parsing Galileo ionospheric parameters - pi.file.setNeQuickAlpha(parameters); - - } else { - // We are parsing Klobuchar ionospheric parameters - - // Verify if we are parsing "alpha" or "beta" ionospheric parameters - if (pi.isIonosphereAlphaInitialized) { - - // Ionospheric "beta" parameters - pi.file.setKlobucharBeta(parameters); - - } else { - - // Ionospheric "alpha" parameters - pi.file.setKlobucharAlpha(parameters); - - // Set the flag to true - pi.isIonosphereAlphaInitialized = true; - - } - - } - - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END); - } - - }, - - /** Parser for corrections to transform the system time to UTC or to other time systems. */ - HEADER_TIME("^.+TIME SYSTEM CORR( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - - // Read fields - final String type = parseString(line, 0, 4); - final double a0 = parseDouble(line, 5, 17); - final double a1 = parseDouble(line, 22, 16); - final int refTime = parseInt(line, 38, 7); - final int refWeek = parseInt(line, 46, 5); - - // Add to the list - final TimeSystemCorrection tsc = new TimeSystemCorrection(type, a0, a1, refTime, refWeek); - pi.file.addTimeSystemCorrections(tsc); - - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(HEADER_COMMENT, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END); - } - - }, - - /** Parser for leap seconds. */ - HEADER_LEAP_SECONDS("^.+LEAP SECONDS( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - // Current number of leap seconds - pi.file.setNumberOfLeapSeconds(parseInt(line, 0, 6)); - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_END); - } - - }, - - /** Parser for the end of header. */ - HEADER_END("^.+END OF HEADER( )*$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - // do nothing... - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(NAVIGATION_MESSAGE_FIRST); - } - - }, - - /** Parser for navigation message first data line. */ - NAVIGATION_MESSAGE_FIRST("(^G|^R|^E|^C|^I|^J|^S).+$") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - - // Set the line number to 0 - pi.lineNumber = 0; - - // Current satellite system - final String key = parseString(line, 0, 1); - - // Initialize parser - pi.systemLineParser = SatelliteSystemLineParser.getSatelliteSystemLineParser(key); - - // Read first line - pi.systemLineParser.parseFirstLine(line, pi); - - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(NAVIGATION_BROADCAST_ORBIT); - } - - }, - - /** Parser for broadcast orbit. */ - NAVIGATION_BROADCAST_ORBIT("^ .+") { - - /** {@inheritDoc} */ - @Override - public void parse(final String line, final ParseInfo pi) { - - // Increment the line number - pi.lineNumber++; - - // Read the corresponding line - if (pi.lineNumber == 1) { - // BROADCAST ORBIT – 1 - pi.systemLineParser.parseFirstBroadcastOrbit(line, pi); - } else if (pi.lineNumber == 2) { - // BROADCAST ORBIT – 2 - pi.systemLineParser.parseSecondBroadcastOrbit(line, pi); - } else if (pi.lineNumber == 3) { - // BROADCAST ORBIT – 3 - pi.systemLineParser.parseThirdBroadcastOrbit(line, pi); - } else if (pi.lineNumber == 4) { - // BROADCAST ORBIT – 4 - pi.systemLineParser.parseFourthBroadcastOrbit(line, pi); - } else if (pi.lineNumber == 5) { - // BROADCAST ORBIT – 5 - pi.systemLineParser.parseFifthBroadcastOrbit(line, pi); - } else if (pi.lineNumber == 6) { - // BROADCAST ORBIT – 6 - pi.systemLineParser.parseSixthBroadcastOrbit(line, pi); - } else { - // BROADCAST ORBIT – 7 - pi.systemLineParser.parseSeventhBroadcastOrbit(line, pi); - } - - } - - /** {@inheritDoc} */ - @Override - public Stream allowedNext() { - return Stream.of(NAVIGATION_MESSAGE_FIRST, NAVIGATION_BROADCAST_ORBIT); - } - - }; - - /** Pattern for identifying line. */ - private final Pattern pattern; - - /** Simple constructor. - * @param lineRegexp regular expression for identifying line - */ - LineParser(final String lineRegexp) { - pattern = Pattern.compile(lineRegexp); - } - - /** Parse a line. - * @param line line to parse - * @param pi holder for transient data - */ - public abstract void parse(String line, ParseInfo pi); - - /** Get the allowed parsers for next line. - * @return allowed parsers for next line - */ - public abstract Stream allowedNext(); - - /** Check if parser can handle line. - * @param line line to parse - * @return true if parser can handle the specified line - */ - public boolean canHandle(final String line) { - return pattern.matcher(line).matches(); - } - - } - - /** Parsers for satellite system specific lines. */ - private enum SatelliteSystemLineParser { - - /** GPS. */ - GPS("G") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.gpsNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int gpsTocYear = parseInt(line, 4, 4); - final int gpsTocMonth = parseInt(line, 9, 2); - final int gpsTocDay = parseInt(line, 12, 2); - final int gpsTocHours = parseInt(line, 15, 2); - final int gpsTocMin = parseInt(line, 18, 2); - final int gpsTocSec = parseInt(line, 21, 2); - pi.gpsNav.setEpochToc(new AbsoluteDate(gpsTocYear, gpsTocMonth, gpsTocDay, gpsTocHours, - gpsTocMin, gpsTocSec, pi.timeScales.getGPS())); - - // Af0, Af1, and Af2 - pi.gpsNav.setAf0(parseDouble(line, 23, 19)); - pi.gpsNav.setAf1(parseDouble(line, 42, 19)); - pi.gpsNav.setAf2(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // IODE - pi.gpsNav.setIODE(parseDouble(line, 4, 19)); - // Crs - pi.gpsNav.setCrs(parseDouble(line, 23, 19)); - // Delta n - pi.gpsNav.setDeltaN(parseDouble(line, 42, 19)); - // M0 - pi.gpsNav.setM0(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Cuc - pi.gpsNav.setCuc(parseDouble(line, 4, 19)); - // e - pi.gpsNav.setE(parseDouble(line, 23, 19)); - // Cus - pi.gpsNav.setCus(parseDouble(line, 42, 19)); - // sqrt(A) - pi.gpsNav.setSqrtA(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Toe - pi.gpsNav.setTime(parseDouble(line, 4, 19)); - // Cic - pi.gpsNav.setCic(parseDouble(line, 23, 19)); - // Omega0 - pi.gpsNav.setOmega0(parseDouble(line, 42, 19)); - // Cis - pi.gpsNav.setCis(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // i0 - pi.gpsNav.setI0(parseDouble(line, 4, 19)); - // Crc - pi.gpsNav.setCrc(parseDouble(line, 23, 19)); - // omega - pi.gpsNav.setPa(parseDouble(line, 42, 19)); - // OMEGA DOT - pi.gpsNav.setOmegaDot(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // iDot - pi.gpsNav.setIDot(parseDouble(line, 4, 19)); - // Codes on L2 channel (ignored) - // parseDouble(line, 23, 19) - // GPS week (to go with Toe) - pi.gpsNav.setWeek((int) parseDouble(line, 42, 19)); - pi.gpsNav.setDate(new GNSSDate(pi.gpsNav.getWeek(), - SEC_TO_MILLI * pi.gpsNav.getTime(), - SatelliteSystem.GPS, - pi.timeScales).getDate()); - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // SV accuracy - pi.gpsNav.setSvAccuracy(parseDouble(line, 4, 19)); - // Health - pi.gpsNav.setSvHealth(parseDouble(line, 23, 19)); - // TGD - pi.gpsNav.setTGD(parseDouble(line, 42, 19)); - // IODC - pi.gpsNav.setIODC(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Add the navigation message to the file - pi.file.addGPSNavigationMessage(pi.gpsNav); - // Reinitialized the container for navigation data - pi.gpsNav = new GPSNavigationMessage(); - } - - }, - - /** Galileo. */ - GALILEO("E") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.galileoNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int galileoTocYear = parseInt(line, 4, 4); - final int galileoTocMonth = parseInt(line, 9, 2); - final int galileoTocDay = parseInt(line, 12, 2); - final int galileoTocHours = parseInt(line, 15, 2); - final int galileoTocMin = parseInt(line, 18, 2); - final int galileoTocSec = parseInt(line, 21, 2); - pi.galileoNav.setEpochToc(new AbsoluteDate(galileoTocYear, galileoTocMonth, galileoTocDay, galileoTocHours, - galileoTocMin, galileoTocSec, pi.timeScales.getGST())); - - // Af0, Af1, and Af2 - pi.galileoNav.setAf0(parseDouble(line, 23, 19)); - pi.galileoNav.setAf1(parseDouble(line, 42, 19)); - pi.galileoNav.setAf2(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // IODNav - pi.galileoNav.setIODNav(parseDouble(line, 4, 19)); - // Crs - pi.galileoNav.setCrs(parseDouble(line, 23, 19)); - // Delta n - pi.galileoNav.setDeltaN(parseDouble(line, 42, 19)); - // M0 - pi.galileoNav.setM0(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Cuc - pi.galileoNav.setCuc(parseDouble(line, 4, 19)); - // e - pi.galileoNav.setE(parseDouble(line, 23, 19)); - // Cus - pi.galileoNav.setCus(parseDouble(line, 42, 19)); - // sqrt(A) - pi.galileoNav.setSqrtA(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Toe - pi.galileoNav.setTime(parseDouble(line, 4, 19)); - // Cic - pi.galileoNav.setCic(parseDouble(line, 23, 19)); - // Omega0 - pi.galileoNav.setOmega0(parseDouble(line, 42, 19)); - // Cis - pi.galileoNav.setCis(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // i0 - pi.galileoNav.setI0(parseDouble(line, 4, 19)); - // Crc - pi.galileoNav.setCrc(parseDouble(line, 23, 19)); - // omega - pi.galileoNav.setPa(parseDouble(line, 42, 19)); - // OMEGA DOT - pi.galileoNav.setOmegaDot(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // iDot - pi.galileoNav.setIDot(parseDouble(line, 4, 19)); - // Data sources (ignored) - // parseDouble(line, 23, 19) - // GAL week (to go with Toe) - pi.galileoNav.setWeek((int) parseDouble(line, 42, 19)); - pi.galileoNav.setDate(new GNSSDate(pi.galileoNav.getWeek(), - SEC_TO_MILLI * pi.galileoNav.getTime(), - SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week! - pi.timeScales).getDate()); - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // SISA - pi.galileoNav.setSisa(parseDouble(line, 4, 19)); - // Health - pi.galileoNav.setSvHealth(parseDouble(line, 23, 19)); - // E5a/E1 BGD - pi.galileoNav.setBGDE1E5a(parseDouble(line, 42, 19)); - // E5b/E1 BGD - pi.galileoNav.setBGDE5bE1(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Add the navigation message to the file - pi.file.addGalileoNavigationMessage(pi.galileoNav); - // Reinitialized the container for navigation data - pi.galileoNav = new GalileoNavigationMessage(); - } - - }, - - /** Beidou. */ - BEIDOU("C") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.beidouNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int beidouTocYear = parseInt(line, 4, 4); - final int beidouTocMonth = parseInt(line, 9, 2); - final int beidouTocDay = parseInt(line, 12, 2); - final int beidouTocHours = parseInt(line, 15, 2); - final int beidouTocMin = parseInt(line, 18, 2); - final int beidouTocSec = parseInt(line, 21, 2); - pi.beidouNav.setEpochToc(new AbsoluteDate(beidouTocYear, beidouTocMonth, beidouTocDay, beidouTocHours, - beidouTocMin, beidouTocSec, pi.timeScales.getBDT())); - - // Af0, Af1, and Af2 - pi.beidouNav.setAf0(parseDouble(line, 23, 19)); - pi.beidouNav.setAf1(parseDouble(line, 42, 19)); - pi.beidouNav.setAf2(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // AODE - pi.beidouNav.setAODE(parseDouble(line, 4, 19)); - // Crs - pi.beidouNav.setCrs(parseDouble(line, 23, 19)); - // Delta n - pi.beidouNav.setDeltaN(parseDouble(line, 42, 19)); - // M0 - pi.beidouNav.setM0(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Cuc - pi.beidouNav.setCuc(parseDouble(line, 4, 19)); - // e - pi.beidouNav.setE(parseDouble(line, 23, 19)); - // Cus - pi.beidouNav.setCus(parseDouble(line, 42, 19)); - // sqrt(A) - pi.beidouNav.setSqrtA(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Toe - pi.beidouNav.setTime(parseDouble(line, 4, 19)); - // Cic - pi.beidouNav.setCic(parseDouble(line, 23, 19)); - // Omega0 - pi.beidouNav.setOmega0(parseDouble(line, 42, 19)); - // Cis - pi.beidouNav.setCis(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // i0 - pi.beidouNav.setI0(parseDouble(line, 4, 19)); - // Crc - pi.beidouNav.setCrc(parseDouble(line, 23, 19)); - // omega - pi.beidouNav.setPa(parseDouble(line, 42, 19)); - // OMEGA DOT - pi.beidouNav.setOmegaDot(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // iDot - pi.beidouNav.setIDot(parseDouble(line, 4, 19)); - // BDT week (to go with Toe) - pi.beidouNav.setWeek((int) parseDouble(line, 42, 19)); - pi.beidouNav.setDate(new GNSSDate(pi.beidouNav.getWeek(), - SEC_TO_MILLI * pi.beidouNav.getTime(), - SatelliteSystem.BEIDOU, - pi.timeScales).getDate()); - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // SV accuracy - pi.beidouNav.setSvAccuracy(parseDouble(line, 4, 19)); - // SatH1 (ignored) - // parseDouble(line, 23, 19) - // TGD1 - pi.beidouNav.setTGD1(parseDouble(line, 42, 19)); - // TGD2 - pi.beidouNav.setTGD2(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Transmission time of message (ignored) - // parseDouble(line, 4, 19); - // AODC - pi.beidouNav.setAODC(parseDouble(line, 23, 19)); - // Add the navigation message to the file - pi.file.addBeidouNavigationMessage(pi.beidouNav); - // Reinitialized the container for navigation data - pi.beidouNav = new BeidouNavigationMessage(); - - } - - }, - - /** QZSS. */ - QZSS("J") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.qzssNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int qzssTocYear = parseInt(line, 4, 4); - final int qzssTocMonth = parseInt(line, 9, 2); - final int qzssTocDay = parseInt(line, 12, 2); - final int qzssTocHours = parseInt(line, 15, 2); - final int qzssTocMin = parseInt(line, 18, 2); - final int qzssTocSec = parseInt(line, 21, 2); - pi.qzssNav.setEpochToc(new AbsoluteDate(qzssTocYear, qzssTocMonth, qzssTocDay, qzssTocHours, - qzssTocMin, qzssTocSec, pi.timeScales.getQZSS())); - - // Af0, Af1, and Af2 - pi.qzssNav.setAf0(parseDouble(line, 23, 19)); - pi.qzssNav.setAf1(parseDouble(line, 42, 19)); - pi.qzssNav.setAf2(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // IODE - pi.qzssNav.setIODE(parseDouble(line, 4, 19)); - // Crs - pi.qzssNav.setCrs(parseDouble(line, 23, 19)); - // Delta n - pi.qzssNav.setDeltaN(parseDouble(line, 42, 19)); - // M0 - pi.qzssNav.setM0(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Cuc - pi.qzssNav.setCuc(parseDouble(line, 4, 19)); - // e - pi.qzssNav.setE(parseDouble(line, 23, 19)); - // Cus - pi.qzssNav.setCus(parseDouble(line, 42, 19)); - // sqrt(A) - pi.qzssNav.setSqrtA(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Toe - pi.qzssNav.setTime(parseDouble(line, 4, 19)); - // Cic - pi.qzssNav.setCic(parseDouble(line, 23, 19)); - // Omega0 - pi.qzssNav.setOmega0(parseDouble(line, 42, 19)); - // Cis - pi.qzssNav.setCis(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // i0 - pi.qzssNav.setI0(parseDouble(line, 4, 19)); - // Crc - pi.qzssNav.setCrc(parseDouble(line, 23, 19)); - // omega - pi.qzssNav.setPa(parseDouble(line, 42, 19)); - // OMEGA DOT - pi.qzssNav.setOmegaDot(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // iDot - pi.qzssNav.setIDot(parseDouble(line, 4, 19)); - // Codes on L2 channel (ignored) - // parseDouble(line, 23, 19) - // GPS week (to go with Toe) - pi.qzssNav.setWeek((int) parseDouble(line, 42, 19)); - pi.qzssNav.setDate(new GNSSDate(pi.qzssNav.getWeek(), - SEC_TO_MILLI * pi.qzssNav.getTime(), - SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week! - pi.timeScales).getDate()); - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // SV accuracy - pi.qzssNav.setSvAccuracy(parseDouble(line, 4, 19)); - // Health - pi.qzssNav.setSvHealth(parseDouble(line, 23, 19)); - // TGD - pi.qzssNav.setTGD(parseDouble(line, 42, 19)); - // IODC - pi.qzssNav.setIODC(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Add the navigation message to the file - pi.file.addQZSSNavigationMessage(pi.qzssNav); - // Reinitialized the container for navigation data - pi.qzssNav = new QZSSNavigationMessage(); - } - - }, - - /** IRNSS. */ - IRNSS("I") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.irnssNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int irnssTocYear = parseInt(line, 4, 4); - final int irnssTocMonth = parseInt(line, 9, 2); - final int irnssTocDay = parseInt(line, 12, 2); - final int irnssTocHours = parseInt(line, 15, 2); - final int irnssTocMin = parseInt(line, 18, 2); - final int irnssTocSec = parseInt(line, 21, 2); - pi.irnssNav.setEpochToc(new AbsoluteDate(irnssTocYear, irnssTocMonth, irnssTocDay, irnssTocHours, - irnssTocMin, irnssTocSec, pi.timeScales.getIRNSS())); - - // Af0, Af1, and Af2 - pi.irnssNav.setAf0(parseDouble(line, 23, 19)); - pi.irnssNav.setAf1(parseDouble(line, 42, 19)); - pi.irnssNav.setAf2(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // IODEC - pi.irnssNav.setIODEC(parseDouble(line, 4, 19)); - // Crs - pi.irnssNav.setCrs(parseDouble(line, 23, 19)); - // Delta n - pi.irnssNav.setDeltaN(parseDouble(line, 42, 19)); - // M0 - pi.irnssNav.setM0(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Cuc - pi.irnssNav.setCuc(parseDouble(line, 4, 19)); - // e - pi.irnssNav.setE(parseDouble(line, 23, 19)); - // Cus - pi.irnssNav.setCus(parseDouble(line, 42, 19)); - // sqrt(A) - pi.irnssNav.setSqrtA(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Toe - pi.irnssNav.setTime(parseDouble(line, 4, 19)); - // Cic - pi.irnssNav.setCic(parseDouble(line, 23, 19)); - // Omega0 - pi.irnssNav.setOmega0(parseDouble(line, 42, 19)); - // Cis - pi.irnssNav.setCis(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // i0 - pi.irnssNav.setI0(parseDouble(line, 4, 19)); - // Crc - pi.irnssNav.setCrc(parseDouble(line, 23, 19)); - // omega - pi.irnssNav.setPa(parseDouble(line, 42, 19)); - // OMEGA DOT - pi.irnssNav.setOmegaDot(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // iDot - pi.irnssNav.setIDot(parseDouble(line, 4, 19)); - // IRNSS week (to go with Toe) - pi.irnssNav.setWeek((int) parseDouble(line, 42, 19)); - pi.irnssNav.setDate(new GNSSDate(pi.irnssNav.getWeek(), - SEC_TO_MILLI * pi.irnssNav.getTime(), - SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week! - pi.timeScales).getDate()); - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // SV accuracy - pi.irnssNav.setURA(parseDouble(line, 4, 19)); - // Health - pi.irnssNav.setSvHealth(parseDouble(line, 23, 19)); - // TGD - pi.irnssNav.setTGD(parseDouble(line, 42, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Add the navigation message to the file - pi.file.addIRNSSNavigationMessage(pi.irnssNav); - // Reinitialized the container for navigation data - pi.irnssNav = new IRNSSNavigationMessage(); - - } - - }, - - /** Glonass. */ - GLONASS("R") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.glonassNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int glonassTocYear = parseInt(line, 4, 4); - final int glonassTocMonth = parseInt(line, 9, 2); - final int glonassTocDay = parseInt(line, 12, 2); - final int glonassTocHours = parseInt(line, 15, 2); - final int glonassTocMin = parseInt(line, 18, 2); - final int glonassTocSec = parseInt(line, 21, 2); - final AbsoluteDate date = new AbsoluteDate(glonassTocYear, glonassTocMonth, glonassTocDay, glonassTocHours, glonassTocMin, glonassTocSec, pi.timeScales.getUTC()); - - pi.glonassNav.setEpochToc(date); - - // TauN (we read -TauN) and GammaN - pi.glonassNav.setTauN(-parseDouble(line, 23, 19)); - pi.glonassNav.setGammaN(parseDouble(line, 42, 19)); - - // Date - pi.glonassNav.setDate(date.getDate()); - - // Time - pi.glonassNav.setTime(fmod(parseDouble(line, 61, 19), Constants.JULIAN_DAY)); - - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // X - pi.glonassNav.setX(parseDouble(line, 4, 19) * KM_TO_M); - // Vx - pi.glonassNav.setXDot(parseDouble(line, 23, 19) * KM_TO_M); - // Ax - pi.glonassNav.setXDotDot(parseDouble(line, 42, 19) * KM_TO_M); - // Health - pi.glonassNav.setHealth(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Y - pi.glonassNav.setY(parseDouble(line, 4, 19) * KM_TO_M); - // Vy - pi.glonassNav.setYDot(parseDouble(line, 23, 19) * KM_TO_M); - // Ay - pi.glonassNav.setYDotDot(parseDouble(line, 42, 19) * KM_TO_M); - // Frequency number - pi.glonassNav.setFrequencyNumber(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Z - pi.glonassNav.setZ(parseDouble(line, 4, 19) * KM_TO_M); - // Vz - pi.glonassNav.setZDot(parseDouble(line, 23, 19) * KM_TO_M); - // Az - pi.glonassNav.setZDotDot(parseDouble(line, 42, 19) * KM_TO_M); - - // Add the navigation message to the file - pi.file.addGlonassNavigationMessage(pi.glonassNav); - // Reinitialized the container for navigation data - pi.glonassNav = new GLONASSNavigationMessage(); - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for GLONASS - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for GLONASS - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for GLONASS - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for GLONASS - } - - }, - - /** SBAS. */ - SBAS("S") { - - /** {@inheritDoc} */ - @Override - public void parseFirstLine(final String line, final ParseInfo pi) { - // PRN - pi.sbasNav.setPRN(parseInt(line, 1, 2)); - - // Toc - final int sbasTocYear = parseInt(line, 4, 4); - final int sbasTocMonth = parseInt(line, 9, 2); - final int sbasTocDay = parseInt(line, 12, 2); - final int sbasTocHours = parseInt(line, 15, 2); - final int sbasTocMin = parseInt(line, 18, 2); - final int sbasTocSec = parseInt(line, 21, 2); - // Time scale (UTC for Rinex 3.01 and GPS for other RINEX versions) - final TimeScale timeScale = ((int) pi.version * 100 == 301) ? pi.timeScales.getUTC() : pi.timeScales.getGPS(); - final AbsoluteDate refEpoch = new AbsoluteDate(sbasTocYear, sbasTocMonth, sbasTocDay, sbasTocHours, - sbasTocMin, sbasTocSec, timeScale); - pi.sbasNav.setEpochToc(refEpoch); - - // AGf0 and AGf1 - pi.sbasNav.setAGf0(parseDouble(line, 23, 19)); - pi.sbasNav.setAGf1(parseDouble(line, 42, 19)); - pi.sbasNav.setTime(parseDouble(line, 61, 19)); - - // Set the ephemeris epoch (same as time of clock epoch) - pi.sbasNav.setDate(refEpoch); - } - - /** {@inheritDoc} */ - @Override - public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) { - // X - pi.sbasNav.setX(parseDouble(line, 4, 19) * KM_TO_M); - // Vx - pi.sbasNav.setXDot(parseDouble(line, 23, 19) * KM_TO_M); - // Ax - pi.sbasNav.setXDotDot(parseDouble(line, 42, 19) * KM_TO_M); - // Health - pi.sbasNav.setHealth(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) { - // Y - pi.sbasNav.setY(parseDouble(line, 4, 19) * KM_TO_M); - // Vy - pi.sbasNav.setYDot(parseDouble(line, 23, 19) * KM_TO_M); - // Ay - pi.sbasNav.setYDotDot(parseDouble(line, 42, 19) * KM_TO_M); - // URA - pi.sbasNav.setURA(parseDouble(line, 61, 19)); - } - - /** {@inheritDoc} */ - @Override - public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) { - // Z - pi.sbasNav.setZ(parseDouble(line, 4, 19) * KM_TO_M); - // Vz - pi.sbasNav.setZDot(parseDouble(line, 23, 19) * KM_TO_M); - // Az - pi.sbasNav.setZDotDot(parseDouble(line, 42, 19) * KM_TO_M); - // IODN - pi.sbasNav.setIODN(parseDouble(line, 61, 19)); - - // Add the navigation message to the file - pi.file.addSBASNavigationMessage(pi.sbasNav); - - // Reinitialized the container for navigation data - pi.sbasNav = new SBASNavigationMessage(); - - } - - /** {@inheritDoc} */ - @Override - public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for SBAS - } - - /** {@inheritDoc} */ - @Override - public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for SBAS - } - - /** {@inheritDoc} */ - @Override - public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for SBAS - } - - /** {@inheritDoc} */ - @Override - public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) { - // Nothing to do for SBAS - } - - }; - - /** Parsing map. */ - private static final Map KEYS_MAP = new HashMap<>(); - static { - for (final SatelliteSystemLineParser satelliteSystem : values()) { - KEYS_MAP.put(satelliteSystem.getKey(), satelliteSystem); - } - } - - /** Satellite system key. */ - private String key; - - /** - * Constructor. - * @param key satellite system key - */ - SatelliteSystemLineParser(final String key) { - this.key = key; - } - - /** - * Getter for the satellite system key. - * @return the satellite system key - */ - public String getKey() { - return key; - } - - /** Parse a string to get the satellite system. - *

        - * The string first character must be the satellite system. - *

        - * @param s string to parse - * @return the satellite system - */ - public static SatelliteSystemLineParser getSatelliteSystemLineParser(final String s) { - return KEYS_MAP.get(s); - } - - /** - * Parse the first line of the navigation message. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseFirstLine(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 1" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseFirstBroadcastOrbit(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 2" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseSecondBroadcastOrbit(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 3" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseThirdBroadcastOrbit(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 4" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseFourthBroadcastOrbit(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 5" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseFifthBroadcastOrbit(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 6" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseSixthBroadcastOrbit(String line, ParseInfo pi); - - /** - * Parse the "BROADCASTORBIT - 7" line. - * @param line line to read - * @param pi holder for transient data - */ - public abstract void parseSeventhBroadcastOrbit(String line, ParseInfo pi); - - /** - * Calculates the floating-point remainder of a / b. - *

        - * fmod = a - x * b - * where x = (int) a / b - *

        - * @param a numerator - * @param b denominator - * @return the floating-point remainder of a / b - */ - private static double fmod(final double a, final double b) { - final double x = (int) (a / b); - return a - x * b; - } - - } - -} diff --git a/src/main/java/org/orekit/gnss/package-info.java b/src/main/java/org/orekit/gnss/package-info.java index 4b40f337cc..0b923a280c 100644 --- a/src/main/java/org/orekit/gnss/package-info.java +++ b/src/main/java/org/orekit/gnss/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame.java new file mode 100644 index 0000000000..fc0e229d8b --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame.java @@ -0,0 +1,299 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.gnss.metric.parser.EncodedMessage; + +/** + * Container for sub-frames in a GPS navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public abstract class SubFrame { + + /** TLM preamble. */ + public static final int PREAMBLE_VALUE = 0x8b; + + /** Words size. */ + protected static final int WORD_SIZE = 30; + + /** Size of parity field. */ + protected static final int PARITY_SIZE = 6; + + /** TLM preamble index. */ + private static final int PREAMBLE = 0; + + /** Telemetry message index. */ + private static final int MESSAGE = 1; + + /** Integrity status flag index. */ + private static final int INTEGRITY_STATUS = 2; + + /** Truncated Time Of Week count index. */ + private static final int TOW_COUNT = 3; + + /** Alert flag index. */ + private static final int ALERT = 4; + + /** Anti-spoofing flag index. */ + private static final int ANTI_SPOOFING = 5; + + /** Sub-frame ID index. */ + private static final int ID = 6; + + /** Raw data fields. */ + private final int[] fields; + + /** Simple constructor. + * @param words raw words + * @param nbFields number of fields in the sub-frame + * (including TLM and HOW data fields, excluding non-information and parity) + */ + protected SubFrame(final int[] words, final int nbFields) { + + this.fields = new int[nbFields]; + + // common fields present in telemetry and handover words for all sub-frames + setField(PREAMBLE, 1, 22, 8, words); + setField(MESSAGE, 1, 8, 14, words); + setField(INTEGRITY_STATUS, 1, 7, 1, words); + setField(TOW_COUNT, 2, 13, 17, words); + setField(ALERT, 2, 12, 1, words); + setField(ANTI_SPOOFING, 2, 11, 1, words); + setField(ID, 2, 8, 3, words); + + if (getField(PREAMBLE) != PREAMBLE_VALUE) { + throw new OrekitException(OrekitMessages.INVALID_GNSS_DATA, getField(PREAMBLE)); + } + + } + + /** Builder for sub-frames. + *

        + * This builder creates the proper sub-frame type corresponding to the ID in handover word + * and the SV Id for sub-frames 4 and 5. + *

        + * @param encodedMessage encoded message containing exactly one sub-frame + * @return sub-frame with TLM and HOW fields already set up + * @see SubFrame1 + * @see SubFrame2 + * @see SubFrame3 + * @see SubFrame4A0 + * @see SubFrame4A1 + * @see SubFrame4B + * @see SubFrame4C + * @see SubFrame4D + * @see SubFrame4E + * @see SubFrameAlmanac + * @see SubFrameDummyAlmanac + */ + public static SubFrame parse(final EncodedMessage encodedMessage) { + + encodedMessage.start(); + + // get the raw words + final int[] words = new int[10]; + for (int i = 0; i < words.length; ++i) { + words[i] = (int) encodedMessage.extractBits(30); + } + + // check parity on all words + for (int i = 0; i < words.length; ++i) { + // we assume last word of previous sub frame had parity bits set to 0, + // using the non_information bits at the end of each sub-frame + if (!checkParity(i == 0 ? 0x0 : words[i - 1], words[i])) { + throw new OrekitException(OrekitMessages.GNSS_PARITY_ERROR, i + 1); + } + } + + final int id = (words[1] >>> 8) & 0x7; + switch (id) { + case 1 : // IS-GPS-200 figure 40-1 sheet 1 + return new SubFrame1(words); + case 2 : // IS-GPS-200 figure 40-1 sheet 2 + return new SubFrame2(words); + case 3 : // IS-GPS-200 figure 40-1 sheet 3 + return new SubFrame3(words); + case 4 : { + final int svId = (words[2] >>> 22) & 0x3F; + // see table 20-V for mapping between SV-ID and page format + switch (svId) { + case 0 : // almanac for dummy Sv + return new SubFrameDummyAlmanac(words); + case 57 : // pages 1, 6, 11, 16, 21 + // IS-GPS-200 figure 40-1 sheet 6 + return new SubFrame4A0(words); + case 25 : case 26 : case 27 : case 28 : case 29 : case 30 : case 31 : case 32 : // pages 2, 3, 4, 5, 7, 8, 9, 10 + // IS-GPS-200 figure 40-1 sheets 4 is also applicable to sub-frame 4 + return new SubFrameAlmanac(words); + case 53 : case 54 : case 55 : // pages 14, 15, 17 + // IS-GPS-200 figure 40-1 sheet 11 + return new SubFrame4B(words); + case 58 : case 59 : case 60 : case 61 : case 62 : // pages 12, 19, 20, 22, 23, 24 + // IS-GPS-200 figure 40-1 sheet 7 + return new SubFrame4A1(words); + case 52 : // page 13 + // IS-GPS-200 figure 40-1 sheet 10 + return new SubFrame4C(words); + case 56 : // page 18 + // IS-GPS-200 figure 40-1 sheet 8 + return new SubFrame4D(words); + case 63 : // page 25 + // IS-GPS-200 figure 40-1 sheet 9 + return new SubFrame4E(words); + default : + throw new OrekitException(OrekitMessages.INVALID_GNSS_DATA, svId); + } + } + case 5 : { + // IS-GPS-200 figure 40-1 sheets 4 and 5 + final int page = (words[2] >>> 22) & 0x3F; + return page == 25 ? new SubFrame5B(words) : new SubFrameAlmanac(words); + } + default : throw new OrekitException(OrekitMessages.INVALID_GNSS_DATA, id); + } + + } + + /** Check parity. + *

        + * This implements algorithm in table 20-XIV from IS-GPS-200N + *

        + * @param previous previous 30 bits word (only two least significant bits are used) + * @param current current 30 bits word + * @return true if parity check succeeded + */ + public static boolean checkParity(final int previous, final int current) { + + final int d29Star = previous & 0x2; + final int d30Star = previous & 0x1; + + final int d25 = 0x1 & Integer.bitCount(d29Star | (current & 0x3B1F3480)); // 111011000111110011010010000000 + final int d26 = 0x1 & Integer.bitCount(d30Star | (current & 0x1D8F9A40)); // 011101100011111001101001000000 + final int d27 = 0x1 & Integer.bitCount(d29Star | (current & 0x2EC7CD00)); // 101110110001111100110100000000 + final int d28 = 0x1 & Integer.bitCount(d30Star | (current & 0x1763E680)); // 010111011000111110011010000000 + final int d29 = 0x1 & Integer.bitCount(d30Star | (current & 0x2BB1F340)); // 101011101100011111001101000000 + final int d30 = 0x1 & Integer.bitCount(d29Star | (current & 0x0B7A89C0)); // 001011011110101000100111000000 + + final int parity = ((((d25 << 1 | d26) << 1 | d27) << 1 | d28) << 1 | d29) << 1 | d30; + + return (parity & 0x3F) == (current & 0x3F); + + } + + /** Check if the sub-frame has parity errors. + * @return true if frame has parity errors + */ + public boolean hasParityErrors() { + return false; + } + + /** Get a field. + *

        + * The field indices are defined as constants in the various sub-frames classes. + *

        + * @param fieldIndex field index (counting from 0) + * @return field value + */ + protected int getField(final int fieldIndex) { + return fields[fieldIndex]; + } + + /** Set a field. + * @param fieldIndex field index (counting from 0) + * @param wordIndex word index (counting from 1, to match IS-GPS-200 tables) + * @param shift right shift to apply (i.e. number of LSB bits for next fields that should be removed) + * @param nbBits number of bits in the field + * @param words raw 30 bits words + */ + protected void setField(final int fieldIndex, final int wordIndex, + final int shift, final int nbBits, + final int[] words) { + fields[fieldIndex] = (words[wordIndex - 1] >>> shift) & ((0x1 << nbBits) - 1); + } + + /** Set a field. + * @param fieldIndex field index (counting from 0) + * @param wordIndexMSB word index containing MSB (counting from 1, to match IS-GPS-200 tables) + * @param shiftMSB right shift to apply to MSB (i.e. number of LSB bits for next fields that should be removed) + * @param nbBitsMSB number of bits in the MSB + * @param wordIndexLSB word index containing LSB (counting from 1, to match IS-GPS-200 tables) + * @param shiftLSB right shift to apply to LSB (i.e. number of LSB bits for next fields that should be removed) + * @param nbBitsLSB number of bits in the LSB + * @param words raw 30 bits words + */ + protected void setField(final int fieldIndex, + final int wordIndexMSB, final int shiftMSB, final int nbBitsMSB, + final int wordIndexLSB, final int shiftLSB, final int nbBitsLSB, + final int[] words) { + final int msb = (words[wordIndexMSB - 1] >>> shiftMSB) & ((0x1 << nbBitsMSB) - 1); + final int lsb = (words[wordIndexLSB - 1] >>> shiftLSB) & ((0x1 << nbBitsLSB) - 1); + fields[fieldIndex] = msb << nbBitsLSB | lsb; + } + + /** Get telemetry preamble. + * @return telemetry preamble + */ + public int getPreamble() { + return getField(PREAMBLE); + } + + /** Get telemetry message. + * @return telemetry message + */ + public int getMessage() { + return getField(MESSAGE); + } + + /** Get integrity status flag. + * @return integrity status flag + */ + public int getIntegrityStatus() { + return getField(INTEGRITY_STATUS); + } + + /** Get Time Of Week of next 12 second message. + * @return Time Of Week of next 12 second message (s) + */ + public int getTow() { + return getField(TOW_COUNT) * 6; + } + + /** Get alert flag. + * @return alert flag + */ + public int getAlert() { + return getField(ALERT); + } + + /** Get anti-spoofing flag. + * @return anti-spoofing flag + */ + public int getAntiSpoofing() { + return getField(ANTI_SPOOFING); + } + + /** Get sub-frame id. + * @return sub-frame id + */ + public int getId() { + return getField(ID); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame1.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame1.java new file mode 100644 index 0000000000..d1f042ad61 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame1.java @@ -0,0 +1,211 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.FastMath; + +/** + * Container for sub-frames 1. + *

        + * Table 20-1, sheet 1 and table 40-1, sheet 1 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame1 extends SubFrame { + + /** Index of Week Number field. */ + private static final int WEEK_NUMBER = 7; + + /** Index of C/A or P code field. */ + private static final int CA_OR_P = 8; + + /** Index of URA_INDEX index field. */ + private static final int URA_INDEX = 9; + + /** Index of SV Health field. */ + private static final int SV_HEALTH = 10; + + /** Index of IODC field. */ + private static final int IODC = 11; + + /** Index of L2 P data flag field. */ + private static final int L2_P_DATA = 12; + + /** Index of reserved field in word 4. */ + private static final int RESERVED_4 = 13; + + /** Index of reserved field in word 5. */ + private static final int RESERVED_5 = 14; + + /** Index of reserved field in word 6. */ + private static final int RESERVED_6 = 15; + + /** Index of reserved field in word 7. */ + private static final int RESERVED_7 = 16; + + /** Index of TGD field. */ + private static final int TGD = 17; + + /** Index of TOC field. */ + private static final int TOC = 18; + + /** Index of AF2 field. */ + private static final int AF2 = 19; + + /** Index of AF1 field. */ + private static final int AF1 = 20; + + /** Index of AF0 field. */ + private static final int AF0 = 21; + + /** */ + /** Simple constructor. + * @param words raw words + */ + SubFrame1(final int[] words) { + + // create raw container + super(words, AF0 + 1); + + // populate container + setField(WEEK_NUMBER, 3, 20, 10, words); + setField(CA_OR_P, 3, 18, 2, words); + setField(URA_INDEX, 3, 14, 4, words); + setField(SV_HEALTH, 3, 8, 6, words); + setField(IODC, 3, 6, 2, 8, 22, 8, words); + setField(L2_P_DATA, 4, 29, 1, words); + setField(RESERVED_4, 4, 6, 23, words); + setField(RESERVED_5, 5, 6, 24, words); + setField(RESERVED_6, 6, 6, 24, words); + setField(RESERVED_7, 7, 14, 16, words); + setField(TGD, 7, 6, 8, words); + setField(TOC, 8, 6, 16, words); + setField(AF2, 9, 22, 8, words); + setField(AF1, 9, 6, 16, words); + setField(AF0, 10, 8, 22, words); + + } + + /** Get Week Number. + * @return week number + */ + public int getWeekNumber() { + return getField(WEEK_NUMBER); + } + + /** Get C/A or P flag. + * @return C/A or P flag + */ + public int getCaOrPFlag() { + return getField(CA_OR_P); + } + + /** Get URA index. + * @return URA index + */ + public int getUraIndex() { + return getField(URA_INDEX); + } + + /** Get SV health. + * @return SV health + */ + public int getSvHealth() { + return getField(SV_HEALTH); + } + + /** Get IODC. + * @return IODC + */ + public int getIODC() { + return getField(IODC); + } + + /** Get L2 P data flag. + * @return L2 P data flag + */ + public int getL2PDataFlag() { + return getField(L2_P_DATA); + } + + /** Get the reserved field in word 4. + * @return reserved field in word 4 + */ + public int getReserved04() { + return getField(RESERVED_4); + } + + /** Get the reserved field in word 5. + * @return reserved field in word 5 + */ + public int getReserved05() { + return getField(RESERVED_5); + } + + /** Get the reserved field in word 6. + * @return reserved field in word 6 + */ + public int getReserved06() { + return getField(RESERVED_6); + } + + /** Get the reserved field in word 7. + * @return reserved field in word 7 + */ + public int getReserved07() { + return getField(RESERVED_7); + } + + /** Get the TGD. + * @return TGD + */ + public int getTGD() { + return getField(TGD); + } + + /** Get the TOC. + * @return TOC + */ + public int getTOC() { + return getField(TOC); + } + + /** Get af₂. + * @return af₂ (second/second²) + */ + public double getAF2() { + return FastMath.scalb((double) getField(AF2), -55); + } + + /** Get af₁. + * @return af₁ (second/second) + */ + public double getAF1() { + return FastMath.scalb((double) getField(AF1), -43); + } + + /** Get af₀. + * @return af₀ + */ + public double getAF0() { + return FastMath.scalb((double) getField(AF0), -31); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame2.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame2.java new file mode 100644 index 0000000000..2ce8d6974c --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame2.java @@ -0,0 +1,167 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.FastMath; +import org.orekit.gnss.metric.parser.Units; + +/** + * Container for sub-frames 2. + *

        + * Table 20-1, sheet 2 and table 40-1, sheet 2 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame2 extends SubFrame { + + /** Index of IODE field. */ + private static final int IODE = 7; + + /** Index of Crs field. */ + private static final int CRS = 8; + + /** Index of Δn field. */ + private static final int DELTA_N = 9; + + /** Index of MO field. */ + private static final int M0 = 10; + + /** Index of Cuc field. */ + private static final int CUC = 11; + + /** Index of e field. */ + private static final int E = 12; + + /** Index of Cus field. */ + private static final int CUS = 13; + + /** Index of √a field. */ + private static final int SQRT_A = 14; + + /** Index of TOE field. */ + private static final int TOE = 15; + + /** Index of it interval field. */ + private static final int FIT_INTERVAL = 16; + + /** Index of AODO field. */ + private static final int AODO = 17; + + /** Simple constructor. + * @param words raw words + */ + SubFrame2(final int[] words) { + + // create raw container + super(words, AODO + 1); + + // populate container + setField(IODE, 3, 22, 8, words); + setField(CRS, 3, 6, 16, words); + setField(DELTA_N, 4, 14, 16, words); + setField(M0, 4, 6, 8, 5, 6, 24, words); + setField(CUC, 6, 14, 16, words); + setField(E, 6, 6, 8, 7, 6, 24, words); + setField(CUS, 8, 14, 16, words); + setField(SQRT_A, 8, 6, 8, 9, 6, 24, words); + setField(TOE, 10, 14, 16, words); + setField(FIT_INTERVAL, 10, 13, 1, words); + setField(AODO, 10, 8, 5, words); + + } + + /** Get Issue Of Data (ephemeris). + * @return Issue Of Data (ephemeris) + */ + public int getIODE() { + return getField(IODE); + } + + /** Get Crs. + * @return crs (m) + */ + public double getCrs() { + return FastMath.scalb((double) getField(CRS), -5); + } + + /** Get Δn. + * @return Δn (rad/s) + */ + public double getDeltaN() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(DELTA_N), -43)); + } + + /** Get M₀. + * @return M₀ (rad) + */ + public double getM0() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(M0), -31)); + } + + /** Get Cuc. + * @return Cuc (rad) + */ + public double getCuc() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(CUC), -29)); + } + + /** Get e. + * @return e + */ + public double getE() { + return FastMath.scalb((double) getField(E), -33); + } + + /** Get Cus. + * @return Cus (rad) + */ + public double getCus() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(CUS), -29)); + } + + /** Get √a. + * @return d√a (√m) + */ + public double getSqrtA() { + return FastMath.scalb((double) getField(SQRT_A), -19); + } + + /** Get Time Of Ephemeris. + * @return Time Of Ephemeris + */ + public int getToe() { + return getField(TOE) << 4; + } + + /** Get fit interval. + * @return fit interval + */ + public int getFitInterval() { + return getField(FIT_INTERVAL); + } + + /** Get Age Of data Offset. + * @return Age Of Data Offset (s) + */ + public int getAODO() { + return getField(AODO) * 900; + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame3.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame3.java new file mode 100644 index 0000000000..73875a589e --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame3.java @@ -0,0 +1,145 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.FastMath; +import org.orekit.gnss.metric.parser.Units; + +/** + * Container for sub-frames 3. + *

        + * Table 20-1, sheet 3 and table 40-1, sheet 3 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame3 extends SubFrame { + + /** Index of Cic field. */ + private static final int CIC = 7; + + /** Index of Ω₀ field. */ + private static final int UPPERCASE_OMEGA_0 = 8; + + /** Index of Cis field. */ + private static final int CIS = 9; + + /** Index of i₀ field. */ + private static final int I0 = 10; + + /** Index of Crc field. */ + private static final int CRC = 11; + + /** Index of ω field. */ + private static final int LOWERCASE_OMEGA = 12; + + /** Index of dot(Ω) field. */ + private static final int OMEGA_DOT = 13; + + /** Index of IODE field. */ + private static final int IODE = 14; + + /** Index of dot(i) field. */ + private static final int I_DOT = 15; + + /** Simple constructor. + * @param words raw words + */ + SubFrame3(final int[] words) { + + // create raw container + super(words, I_DOT + 1); + + // populate container + setField(CIC, 3, 14, 16, words); + setField(UPPERCASE_OMEGA_0, 3, 6, 8, 4, 6, 24, words); + setField(CIS, 5, 14, 16, words); + setField(I0, 5, 6, 8, 6, 6, 24, words); + setField(CRC, 7, 14, 16, words); + setField(LOWERCASE_OMEGA, 7, 6, 8, 8, 6, 24, words); + setField(OMEGA_DOT, 9, 6, 24, words); + setField(IODE, 10, 22, 8, words); + setField(I_DOT, 10, 8, 14, words); + + } + + /** Get Cic. + * @return Cic (rad) + */ + public double getCic() { + return FastMath.scalb((double) getField(CIC), -29); + } + + /** Get Ω₀. + * @return Ω₀ (rad) + */ + public double getUppercaseOmega0() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(UPPERCASE_OMEGA_0), -31)); + } + + /** Get Cis. + * @return Cis (rad) + */ + public double getCis() { + return FastMath.scalb((double) getField(CIS), -29); + } + + /** Get i₀. + * @return i₀ (rad) + */ + public double getI0() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(I0), -31)); + } + + /** Get Crc. + * @return Crc (rad) + */ + public double getCrc() { + return FastMath.scalb((double) getField(CRC), -5); + } + + /** Get ω. + * @return ω(rad) + */ + public double getLowercaseOmega() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(LOWERCASE_OMEGA), -31)); + } + + /** Get dot(Ω). + * @return dot(Ω) (rad/s) + */ + public double getOmegaDot() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(OMEGA_DOT), -43)); + } + + /** Get Issue Of Data (ephemeris). + * @return Issue Of Data (ephemeris) + */ + public int getIODE() { + return getField(IODE); + } + + /** Get dot(i). + * @return dot(i) (rad/s) + */ + public double getIDot() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(I_DOT), -43)); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame45.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame45.java new file mode 100644 index 0000000000..cfad0b8482 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame45.java @@ -0,0 +1,62 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +/** + * Base container for sub-frames 4 and 5. + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame45 extends SubFrame { + + /** Index of data ID field. */ + private static final int DATA_ID = 7; + + /** Index of SV ID field. */ + private static final int SV_ID = 8; + + /** Simple constructor. + * @param words raw words + * @param nbFields number of fields in the sub-frame + * (including TLM and HOW data fields, excluding non-information and parity) + */ + SubFrame45(final int[] words, final int nbFields) { + + // create raw container + super(words, nbFields); + + // populate container + setField(DATA_ID, 3, 28, 2, words); + setField(SV_ID, 3, 22, 6, words); + + } + + /** Get data ID. + * @return data ID + */ + public int getDataId() { + return getField(DATA_ID); + } + + /** Get SV (page) ID. + * @return SV (page) ID + */ + public int getSvId() { + return getField(SV_ID); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A.java new file mode 100644 index 0000000000..8b626d0c8e --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A.java @@ -0,0 +1,144 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +/** + * Container for sub-frames 4, pages 1, 6, 11, 16 and 21, + * but also for sub-frames 4, pages 2, 3, 4, 5, 7, 8, and 9 + * which have a similar structure. + *

        + * Table 40-1, sheets 6 and 7 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4A extends SubFrame45 { + + /** Index of reserved field in word 3. */ + private static final int RESERVED_3 = 9; + + /** Index of reserved field in word 4. */ + private static final int RESERVED_4 = 10; + + /** Index of reserved field in word 5. */ + private static final int RESERVED_5 = 11; + + /** Index of reserved field in word 6. */ + private static final int RESERVED_6 = 12; + + /** Index of reserved field in word 7. */ + private static final int RESERVED_7 = 13; + + /** Index of reserved field in word 8. */ + private static final int RESERVED_8 = 14; + + /** Index of reserved field A in word 9. */ + private static final int RESERVED_A_9 = 15; + + /** Index of reserved field B in word 9. */ + private static final int RESERVED_B_9 = 16; + + /** Index of reserved field in word 10. */ + private static final int RESERVED_10 = 17; + + /** Simple constructor. + * @param words raw words + */ + SubFrame4A(final int[] words) { + + // create raw container + super(words, RESERVED_10 + 1); + + // populate container + setField(RESERVED_3, 3, 6, 16, words); + setField(RESERVED_4, 4, 6, 24, words); + setField(RESERVED_5, 5, 6, 24, words); + setField(RESERVED_6, 6, 6, 24, words); + setField(RESERVED_7, 7, 6, 24, words); + setField(RESERVED_8, 8, 6, 24, words); + setField(RESERVED_A_9, 9, 22, 8, words); + setField(RESERVED_B_9, 9, 6, 16, words); + setField(RESERVED_10, 10, 8, 22, words); + + } + + /** Get the reserved field in word 3. + * @return reserved field in word 3 + */ + public int getReserved03() { + return getField(RESERVED_3); + } + + /** Get the reserved field in word 4. + * @return reserved field in word 4 + */ + public int getReserved04() { + return getField(RESERVED_4); + } + + /** Get the reserved field in word 5. + * @return reserved field in word 5 + */ + public int getReserved05() { + return getField(RESERVED_5); + } + + /** Get the reserved field in word 6. + * @return reserved field in word 6 + */ + public int getReserved06() { + return getField(RESERVED_6); + } + + /** Get the reserved field in word 7. + * @return reserved field in word 7 + */ + public int getReserved07() { + return getField(RESERVED_7); + } + + /** Get the reserved field in word 8. + * @return reserved field in word 8 + */ + public int getReserved08() { + return getField(RESERVED_8); + } + + /** Get the reserved field A in word 9. + * @return reserved field A in word 9 + */ + public int getReservedA09() { + return getField(RESERVED_A_9); + } + + /** Get the reserved field B in word 9. + * @return reserved field B in word 9 + */ + public int getReservedB09() { + return getField(RESERVED_B_9); + } + + /** Get the reserved field in word 10. + * @return reserved field in word 10 + */ + public int getReserved10() { + return getField(RESERVED_10); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A0.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A0.java new file mode 100644 index 0000000000..0ec751904d --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A0.java @@ -0,0 +1,38 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +/** + * Container for sub-frames 4, pages 1, 6, 11, 16 and 21. + *

        + * Table 40-1, sheet 6 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4A0 extends SubFrame4A { + + /** Simple constructor. + * @param words raw words + */ + SubFrame4A0(final int[] words) { + super(words); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A1.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A1.java new file mode 100644 index 0000000000..b6d90dcfd0 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4A1.java @@ -0,0 +1,38 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +/** + * Container for sub-frames 4, pages 2, 3, 4, 5, 7, 8, and 9. + *

        + * Table 40-1, sheet 7 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4A1 extends SubFrame4A { + + /** Simple constructor. + * @param words raw words + */ + SubFrame4A1(final int[] words) { + super(words); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4B.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4B.java new file mode 100644 index 0000000000..c2d8a64497 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4B.java @@ -0,0 +1,131 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +/** + * Container for sub-frames 4, pages 10, 14, 15, 17. + *

        + * Table 40-1, sheet 11 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4B extends SubFrame45 { + + /** Index of reserved field in word 3. */ + private static final int RESERVED_3 = 9; + + /** Index of reserved field in word 4. */ + private static final int RESERVED_4 = 10; + + /** Index of reserved field in word 5. */ + private static final int RESERVED_5 = 11; + + /** Index of reserved field in word 6. */ + private static final int RESERVED_6 = 12; + + /** Index of reserved field in word 7. */ + private static final int RESERVED_7 = 13; + + /** Index of reserved field in word 8. */ + private static final int RESERVED_8 = 14; + + /** Index of reserved field in word 9. */ + private static final int RESERVED_9 = 15; + + /** Index of reserved field in word 10. */ + private static final int RESERVED_10 = 16; + + /** Simple constructor. + * @param words raw words + */ + SubFrame4B(final int[] words) { + + // create raw container + super(words, RESERVED_10 + 1); + + // populate container + setField(RESERVED_3, 3, 6, 16, words); + setField(RESERVED_4, 4, 6, 24, words); + setField(RESERVED_5, 5, 6, 24, words); + setField(RESERVED_6, 6, 6, 24, words); + setField(RESERVED_7, 7, 6, 24, words); + setField(RESERVED_8, 8, 6, 24, words); + setField(RESERVED_9, 9, 6, 24, words); + setField(RESERVED_10, 10, 8, 22, words); + + } + + /** Get the reserved field in word 3. + * @return reserved field in word 3 + */ + public int getReserved03() { + return getField(RESERVED_3); + } + + /** Get the reserved field in word 4. + * @return reserved field in word 4 + */ + public int getReserved04() { + return getField(RESERVED_4); + } + + /** Get the reserved field in word 5. + * @return reserved field in word 5 + */ + public int getReserved05() { + return getField(RESERVED_5); + } + + /** Get the reserved field in word 6. + * @return reserved field in word 6 + */ + public int getReserved06() { + return getField(RESERVED_6); + } + + /** Get the reserved field in word 7. + * @return reserved field in word 7 + */ + public int getReserved07() { + return getField(RESERVED_7); + } + + /** Get the reserved field in word 8. + * @return reserved field in word 8 + */ + public int getReserved08() { + return getField(RESERVED_8); + } + + /** Get the reserved field in word 9. + * @return reserved field in word 9 + */ + public int getReserved09() { + return getField(RESERVED_9); + } + + /** Get the reserved field in word 10. + * @return reserved field in word 10 + */ + public int getReserved10() { + return getField(RESERVED_10); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4C.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4C.java new file mode 100644 index 0000000000..df2b24d147 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4C.java @@ -0,0 +1,82 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.MathUtils; + +/** + * Container for sub-frames 4, page 13. + *

        + * Table 40-1, sheet 10 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4C extends SubFrame45 { + + /** Number of Estimated Range Deviations. */ + public static final int NB_ERD = 30; + + /** Index of availability indicator field. */ + private static final int AVAILABILITY_INDICATOR = 9; + + /** Size of Estimated Range Deviations. */ + private static final int ERD_SIZE = 6; + + /** Simple constructor. + * @param words raw words + */ + SubFrame4C(final int[] words) { + + // create raw container + super(words, AVAILABILITY_INDICATOR + NB_ERD + 1); + + // populate container + int field = AVAILABILITY_INDICATOR; + int word = 3; + int shift = 20; + setField(field, word, shift, 2, words); + for (int i = 0; i < NB_ERD; ++i) { + if (shift >= ERD_SIZE + PARITY_SIZE) { + // current word contains a complete ERD + shift -= ERD_SIZE; + setField(++field, word, shift, ERD_SIZE, words); + } else { + // current word contains only the MSF of next ERD + final int msbBits = shift - PARITY_SIZE; + shift += WORD_SIZE - ERD_SIZE; + setField(++field, + word, PARITY_SIZE, msbBits, + word + 1, WORD_SIZE - (ERD_SIZE - msbBits), ERD_SIZE - msbBits, words); + ++word; + } + } + + } + + /** Get an Estimated Range Deviation. + * @param index index of the ERD (between 1 and {@link #NB_ERD}) + * @return Estimated Range Deviation + */ + public int getERD(final int index) { + MathUtils.checkRangeInclusive(index, 1, NB_ERD); + return getField(AVAILABILITY_INDICATOR + index); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4D.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4D.java new file mode 100644 index 0000000000..c08900861d --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4D.java @@ -0,0 +1,236 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.FastMath; +import org.orekit.gnss.metric.parser.Units; +import org.orekit.utils.units.Unit; + +/** + * Container for sub-frames 4, page 18. + *

        + * Table 40-1, sheet 8 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4D extends SubFrame45 { + + /** Seconds per semi-circle. */ + private static final Unit S_PER_SC = Unit.SECOND.divide("s/sc", Units.SEMI_CIRCLE); + + /** Seconds per semi-circle². */ + private static final Unit S_PER_SC2 = S_PER_SC.divide("s/sc²", Units.SEMI_CIRCLE); + + /** Seconds per semi-circle³. */ + private static final Unit S_PER_SC3 = S_PER_SC2.divide("s/sc³", Units.SEMI_CIRCLE); + + /** Index of α₀ field. */ + private static final int ALPHA0 = 9; + + /** Index of α₁ field. */ + private static final int ALPHA1 = 10; + + /** Index of α₂ field. */ + private static final int ALPHA2 = 11; + + /** Index of α₃ field. */ + private static final int ALPHA3 = 12; + + /** Index of β₀ field. */ + private static final int BETA0 = 13; + + /** Index of β₁ field. */ + private static final int BETA1 = 14; + + /** Index of β₂ field. */ + private static final int BETA2 = 15; + + /** Index of β₃ field. */ + private static final int BETA3 = 16; + + /** Index of A₁ field. */ + private static final int A1 = 17; + + /** Index of A0 field. */ + private static final int A0 = 18; + + /** Index of TOT field. */ + private static final int TOT = 19; + + /** Index of Week Number T field. */ + private static final int WEEK_NUMBER_T = 20; + + /** Index of ΔtLS field. */ + private static final int DELTA_T_LS = 21; + + /** Index of Week Number LSF field. */ + private static final int WEEK_NUMBER_LSF = 22; + + /** Index of DN field. */ + private static final int DN = 23; + + /** Index of ΔtLSF field. */ + private static final int DELTA_T_LSF = 24; + + /** Index of reserved field in word 10. */ + private static final int RESERVED_10 = 25; + + /** Simple constructor. + * @param words raw words + */ + SubFrame4D(final int[] words) { + + // create raw container + super(words, RESERVED_10 + 1); + + // populate container + setField(ALPHA0, 3, 14, 8, words); + setField(ALPHA1, 3, 6, 8, words); + setField(ALPHA2, 4, 22, 8, words); + setField(ALPHA3, 4, 14, 8, words); + setField(BETA0, 4, 6, 8, words); + setField(BETA1, 5, 22, 8, words); + setField(BETA2, 5, 14, 8, words); + setField(BETA3, 5, 6, 8, words); + setField(A1, 6, 6, 24, words); + setField(A0, 7, 6, 24, 8, 22, 8, words); + setField(TOT, 8, 14, 8, words); + setField(WEEK_NUMBER_T, 8, 6, 8, words); + setField(DELTA_T_LS, 9, 22, 8, words); + setField(WEEK_NUMBER_LSF, 9, 14, 8, words); + setField(DN, 9, 6, 8, words); + setField(DELTA_T_LSF, 10, 22, 8, words); + setField(RESERVED_10, 10, 8, 14, words); + + } + + /** Get α₀ field. + * @return α₀ field (second). + */ + public double getAlpha0() { + return FastMath.scalb((double) getField(ALPHA0), -30); + } + + /** Get α₁ field. + * @return α₁ field (second/rad). + */ + public double getAlpha1() { + return S_PER_SC.toSI(FastMath.scalb((double) getField(ALPHA1), -27)); + } + + /** Get α₂ field. + * @return α₂ field (second/rad²). + */ + public double getAlpha2() { + return S_PER_SC2.toSI(FastMath.scalb((double) getField(ALPHA2), -24)); + } + + /** Get α₃ field. + * @return α₃ field (second/rad³) + */ + public double getAlpha3() { + return S_PER_SC3.toSI(FastMath.scalb((double) getField(ALPHA3), -24)); + } + + /** Get β₀ field. + * @return β₀ field (second) + */ + public double getBeta0() { + return FastMath.scalb((double) getField(BETA0), 11); + } + + /** Get β₁ field. + * @return β₁ field (second/rad) + */ + public double getBeta1() { + return S_PER_SC.toSI(FastMath.scalb((double) getField(BETA1), 14)); + } + + /** Get β₂ field. + * @return β₂ field (second/rad²) + */ + public double getBeta2() { + return S_PER_SC2.toSI(FastMath.scalb((double) getField(BETA2), 16)); + } + + /** Get β₃ field. + * @return β₃ field (second/rad³) + */ + public double getBeta3() { + return S_PER_SC3.toSI(FastMath.scalb((double) getField(BETA3), 16)); + } + + /** Get A₁ field. + * @return A₁ field (second/second). + */ + public double getA1() { + return FastMath.scalb((double) getField(A1), -50); + } + + /** Get A0 field. + * @return A0 field (seconds). + */ + public double getA0() { + return FastMath.scalb((double) getField(A0), -30); + } + + /** Get TOT field. + * @return TOT field. */ + public int getTot() { + return getField(TOT) << 12; + } + + /** Get Week Number T field. + * @return Week Number T field. */ + public int getWeekNumberT() { + return getField(WEEK_NUMBER_T); + } + + /** Get ΔtLS field. + * @return ΔtLS field. */ + public int getDeltaTLs() { + return getField(DELTA_T_LS); + } + + /** Get Week Number LSF field. + * @return Week Number LSF field. */ + public int getWeekNumberLsf() { + return getField(WEEK_NUMBER_LSF); + } + + /** Get DN field. + * @return DN field. */ + public int getDn() { + return getField(DN); + } + + /** Get ΔtLSF field. + * @return ΔtLSF field. */ + public int getDeltaTLsf() { + return getField(DELTA_T_LSF); + } + + /** Get reserved field in word 10. + * @return reserved field in word 10. */ + public int getReserved10() { + return getField(RESERVED_10); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4E.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4E.java new file mode 100644 index 0000000000..30b6b09281 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame4E.java @@ -0,0 +1,136 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.MathUtils; + +/** + * Container for sub-frames 4, page 25. + *

        + * Table 20-1, sheet 9 and table 40-1, sheet 9 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame4E extends SubFrame45 { + + /** Number of Anti-spoofing entries. */ + public static final int NB_AS = 32; + + /** Number of SV health entries. */ + public static final int NB_SVH = 8; + + /** Size of Anti-spoofing. */ + private static final int AS_SIZE = 4; + + /** Size of SV Health. */ + private static final int SVH_SIZE = 6; + + /** Index of first AS field. */ + private static final int FIRST_AS = 9; + + /** Index of reserved field in word 8. */ + private static final int RESERVED_8 = FIRST_AS + NB_AS; + + /** Index of reserved field in word 10. */ + private static final int RESERVED_10 = RESERVED_8 + 1 + NB_AS + NB_SVH; + + /** Simple constructor. + * @param words raw words + */ + SubFrame4E(final int[] words) { + + // create raw container + super(words, RESERVED_10 + 1); + + // populate container + int field = FIRST_AS - 1; + int word = 3; + int shift = 22; + + // anti-spoofing + for (int i = 0; i < NB_AS; ++i) { + if (shift >= AS_SIZE + PARITY_SIZE) { + // current word contains a complete AS + shift -= AS_SIZE; + setField(++field, word, shift, AS_SIZE, words); + } else { + // current word is exhausted + shift = WORD_SIZE - AS_SIZE; + setField(++field, ++word, shift, AS_SIZE, words); + } + } + + // 2 bits for system use + shift -= 2; + setField(RESERVED_8, word, shift, 2, words); + + // SV health + for (int i = 0; i < NB_SVH; ++i) { + if (shift >= SVH_SIZE + PARITY_SIZE) { + // current word contains a complete SVH + shift -= SVH_SIZE; + setField(++field, word, shift, SVH_SIZE, words); + } else { + // current word is exhausted + shift = WORD_SIZE - SVH_SIZE; + setField(++field, ++word, shift, SVH_SIZE, words); + } + } + + setField(RESERVED_10, 10, 8, 4, words); + + } + + /** Get the anti-spoofing for a satellite. + * @param index in the sub-frame (from 1 to 32, beware it is not the satellite number, + * it is also related to {@link #getDataId()}) + * @return anti-spoofing + */ + public int getAntiSpoofing(final int index) { + MathUtils.checkRangeInclusive(index, 1, NB_AS); + return getField(FIRST_AS + index - 1); + } + + /** Get the reserved field in word 8. + * @return reserved field in word 8 + */ + public int getReserved8() { + return getField(RESERVED_8); + } + + /** Get the Sv health for a satellite. + * @param index in the sub-frame (from 1 to 7 or 1 to 8 depending on + * {@link #getDataId()}, beware it is not the satellite number, + * it is also related to {@link #getDataId()}), an + * @return anti-spoofing + */ + public int getSvHealth(final int index) { + MathUtils.checkRangeInclusive(index, 1, NB_SVH); + return getField(RESERVED_8 + index); + } + + /** Get the reserved field in word 10. + * @return reserved field in word 10 + */ + public int getReserved10() { + return getField(RESERVED_10); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrame5B.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame5B.java new file mode 100644 index 0000000000..f1eeecb817 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrame5B.java @@ -0,0 +1,111 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.MathUtils; + +/** + * Container for sub-frames 5, page 25. + *

        + * Table 20-1, sheet 5 and table 40-1, sheet 5 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrame5B extends SubFrame45 { + + /** Number of SV health words. */ + private static final int SVH_WORDS = 6; + + /** Size of SV health fields per word. */ + private static final int SVH_FIELDS = 4; + + /** Index of TOA field. */ + private static final int TOA = 9; + + /** Index of Week Number field. */ + private static final int WEEK_NUMBER = 10; + + /** Index of reserved field A in word 10. */ + private static final int RESERVED_A_10 = WEEK_NUMBER + SVH_WORDS * SVH_FIELDS + 1; + + /** Index of reserved field B in word 10. */ + private static final int RESERVED_B_10 = RESERVED_A_10 + 1; + + /** Simple constructor. + * @param words raw words + */ + SubFrame5B(final int[] words) { + + // create raw container + super(words, RESERVED_B_10 + 1); + + // populate container + setField(TOA, 3, 14, 8, words); + setField(WEEK_NUMBER, 3, 6, 8, words); + int fieldIndex = WEEK_NUMBER + 1; + for (int i = 0; i < SVH_WORDS; ++i) { + for (int j = 0; j < SVH_FIELDS; ++j) { + setField(fieldIndex++, 4 + i, 18 - 6 * j, 6, words); + } + } + setField(RESERVED_A_10, 10, 24, 6, words); + setField(RESERVED_B_10, 10, 8, 16, words); + + } + + /** Get Time of Almanac. + * @return time of almanac (seconds) + */ + public int getTOA() { + return getField(TOA) << 12; + } + + /** Get the almanac week number. + * @return almanac week number + */ + public int getWeekNumber() { + return getField(WEEK_NUMBER); + } + + /** Get the SV Health for a satellite. + * @param index in the sub-frame (from 1 to 24, beware it is not the satellite number, + * it is also related to {@link #getDataId()}) + * @return SV health + */ + public int getSvHealth(final int index) { + MathUtils.checkRangeInclusive(index, 1, 24); + return getField(WEEK_NUMBER + index); + } + + /** Get the reserved field A in word 10. + * @return reserved field A in word 10 + */ + public int getReservedA10() { + return getField(RESERVED_A_10); + } + + /** Get the reserved field B in word 10. + * @return reserved field B in word 10 + */ + public int getReservedB10() { + return getField(RESERVED_B_10); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrameAlmanac.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrameAlmanac.java new file mode 100644 index 0000000000..12cf8ed50d --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrameAlmanac.java @@ -0,0 +1,175 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +import org.hipparchus.util.FastMath; +import org.orekit.gnss.metric.parser.Units; + +/** + * Container for sub-frames 5, page 1-24. + *

        + * Table 20-1, sheet 4 and table 40-1, sheet 4 in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrameAlmanac extends SubFrame45 { + + /** Index of e field. */ + private static final int E = 9; + + /** Index of TOA field. */ + private static final int TOA = 10; + + /** Index of δi field. */ + private static final int DELTA_I = 11; + + /** Index of dot(Ω) field. */ + private static final int OMEGA_DOT = 12; + + /** Index of SV health field. */ + private static final int SV_HEALTH = 13; + + /** Index of √a field. */ + private static final int SQRT_A = 14; + + /** Index of Ω₀ field. */ + private static final int UPPERCASE_OMEGA_0 = 15; + + /** Index of ω field. */ + private static final int LOWERCASE_OMEGA = 16; + + /** Index of M₀ field. */ + private static final int M0 = 17; + + /** Index of AF0 field. */ + private static final int AF0 = 18; + + /** Index of AF1 field. */ + private static final int AF1 = 19; + + /** Simple constructor. + * @param words raw words + */ + SubFrameAlmanac(final int[] words) { + + // create raw container + super(words, AF1 + 1); + + // populate container + setField(E, 3, 6, 16, words); + setField(TOA, 4, 22, 8, words); + setField(DELTA_I, 4, 6, 16, words); + setField(OMEGA_DOT, 5, 14, 16, words); + setField(SV_HEALTH, 5, 6, 8, words); + setField(SQRT_A, 6, 6, 24, words); + setField(UPPERCASE_OMEGA_0, 7, 6, 24, words); + setField(LOWERCASE_OMEGA, 8, 6, 24, words); + setField(M0, 9, 6, 24, words); + setField(AF0, 10, 22, 8, 10, 8, 3, words); + setField(AF1, 10, 11, 11, words); + + } + + /** Get the PRN code phase of the SV. + * @return PRN code phase + */ + public int getPRN() { + // sub-frames that contain almanacs use the SV-ID for the PRN + return getSvId(); + } + + /** Get eccentricity. + * @return eccentricity + */ + public double getE() { + return FastMath.scalb((double) getField(E), -21); + } + + /** Get Time Of Almanac. + * @return Time Of Almanac (seconds) + */ + public int getToaA() { + return getField(TOA) << 12; + } + + /** Get Δi. + * @return Δi (rad) + */ + public double getDeltai() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(DELTA_I), -19)); + } + + /** Get dot(Ω). + * @return dot(Ω) (rad/s) + */ + public double getOmegaDot() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(OMEGA_DOT), -38)); + } + + /** Get SV health. + * @return SV health + */ + public int getSvHealth() { + return getField(SV_HEALTH); + } + + /** Get √a. + * @return d√a (√m) + */ + public double getSqrtA() { + return FastMath.scalb((double) getField(SQRT_A), -11); + } + + /** Get Ω₀. + * @return Ω₀ (rad) + */ + public double getUppercaseOmega0() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(UPPERCASE_OMEGA_0), -23)); + } + + /** Get ω. + * @return ω(rad) + */ + public double getLowercaseOmega() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(LOWERCASE_OMEGA), -23)); + } + + /** Get M₀. + * @return M₀ (rad) + */ + public double getM0() { + return Units.SEMI_CIRCLE.toSI(FastMath.scalb((double) getField(M0), -23)); + } + + /** Get af₀. + * @return af₀ (second) + */ + public double getAF0() { + return FastMath.scalb((double) getField(AF0), -20); + } + + /** Get af₁. + * @return af₁ (second/second) + */ + public double getAF1() { + return FastMath.scalb((double) getField(AF1), -38); + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/SubFrameDummyAlmanac.java b/src/main/java/org/orekit/gnss/rflink/gps/SubFrameDummyAlmanac.java new file mode 100644 index 0000000000..5698ce86d3 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/SubFrameDummyAlmanac.java @@ -0,0 +1,41 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.gnss.rflink.gps; + +/** + * Almanac for dummy SV. + *

        + * Section 20.3.3.5.1.2 Almanac Data in + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class SubFrameDummyAlmanac extends SubFrame45 { + + /** Simple constructor. + * @param words raw words + */ + SubFrameDummyAlmanac(final int[] words) { + + // create raw container + super(words, 9); + + } + +} diff --git a/src/main/java/org/orekit/gnss/rflink/gps/package-info.java b/src/main/java/org/orekit/gnss/rflink/gps/package-info.java new file mode 100644 index 0000000000..7af45dcbbe --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/gps/package-info.java @@ -0,0 +1,29 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package provides classes related to GPS RF-links binary formats. + *

        + * The various formats and data containers here implement the public document + * NAVSTAR + * GPS Space Segment/Navigation User Segment Interface, IS-GPS-200N, 22 Aug 2022 + *

        + * + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.gnss.rflink.gps; diff --git a/src/main/java/org/orekit/gnss/rflink/package-info.java b/src/main/java/org/orekit/gnss/rflink/package-info.java new file mode 100644 index 0000000000..0166a00a61 --- /dev/null +++ b/src/main/java/org/orekit/gnss/rflink/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package provides classes related to RF-links binary formats. + * + * @author Luc Maisonobe + * @since 12.0 + */ +package org.orekit.gnss.rflink; diff --git a/src/main/java/org/orekit/models/earth/GeoMagneticElements.java b/src/main/java/org/orekit/models/earth/GeoMagneticElements.java index 8120b3918d..127bc326d9 100644 --- a/src/main/java/org/orekit/models/earth/GeoMagneticElements.java +++ b/src/main/java/org/orekit/models/earth/GeoMagneticElements.java @@ -17,10 +17,12 @@ package org.orekit.models.earth; import java.io.Serializable; +import java.text.DecimalFormat; import java.text.NumberFormat; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.orekit.utils.units.UnitsConverter; /** Contains the elements to represent a magnetic field at a single point. * @author Thomas Neidhart @@ -39,10 +41,10 @@ public class GeoMagneticElements implements Serializable { /** The magnetic declination in radians. */ private double declination; - /** The magnetic total intensity, in nano Teslas. */ + /** The magnetic total intensity, in Teslas. */ private double totalIntensity; - /** The magnetic horizontal intensity, in nano Teslas. */ + /** The magnetic horizontal intensity, in Teslas. */ private double horizontalIntensity; /** Construct a new element with the given field vector. The other elements @@ -50,16 +52,17 @@ public class GeoMagneticElements implements Serializable { * @param b the magnetic field vector */ public GeoMagneticElements(final Vector3D b) { - this.b = b; + this.b = new Vector3D(UnitsConverter.NANO_TESLAS_TO_TESLAS.getFrom().getScale(), b); - horizontalIntensity = FastMath.hypot(b.getX(), b.getY()); - totalIntensity = b.getNorm(); + final double intensityNanoTesla = FastMath.hypot(b.getX(), b.getY()); + horizontalIntensity = UnitsConverter.NANO_TESLAS_TO_TESLAS.convert(intensityNanoTesla); + totalIntensity = UnitsConverter.NANO_TESLAS_TO_TESLAS.convert(b.getNorm()); declination = FastMath.atan2(b.getY(), b.getX()); - inclination = FastMath.atan2(b.getZ(), horizontalIntensity); + inclination = FastMath.atan2(b.getZ(), intensityNanoTesla); } - /** Returns the magnetic field vector in nTesla. - * @return the magnetic field vector in nTesla + /** Returns the magnetic field vector in Tesla. + * @return the magnetic field vector in Tesla */ public Vector3D getFieldVector() { return b; @@ -80,7 +83,7 @@ public double getDeclination() { } /** Returns the total intensity of the magnetic field (= norm of the field vector). - * @return the total intensity in nTesla + * @return the total intensity in Tesla */ public double getTotalIntensity() { return totalIntensity; @@ -88,7 +91,7 @@ public double getTotalIntensity() { /** Returns the horizontal intensity of the magnetic field (= norm of the * vector in the plane spanned by the x/y components of the field vector). - * @return the horizontal intensity in nTesla + * @return the horizontal intensity in Tesla */ public double getHorizontalIntensity() { return horizontalIntensity; @@ -97,14 +100,15 @@ public double getHorizontalIntensity() { @Override public String toString() { final NumberFormat f = NumberFormat.getInstance(); + final DecimalFormat d = new DecimalFormat("0.######E0"); final StringBuilder sb = new StringBuilder(); sb.append("MagneticField["); sb.append("B="); - sb.append(b.toString(f)); + sb.append(b.toString(d)); sb.append(",H="); - sb.append(f.format(getHorizontalIntensity())); + sb.append(d.format(getHorizontalIntensity())); sb.append(",F="); - sb.append(f.format(getTotalIntensity())); + sb.append(d.format(getTotalIntensity())); sb.append(",I="); sb.append(f.format(getInclination())); sb.append(",D="); diff --git a/src/main/java/org/orekit/models/earth/GeoMagneticModelLoader.java b/src/main/java/org/orekit/models/earth/GeoMagneticModelLoader.java index ca4cbb96f2..db3e8cb747 100644 --- a/src/main/java/org/orekit/models/earth/GeoMagneticModelLoader.java +++ b/src/main/java/org/orekit/models/earth/GeoMagneticModelLoader.java @@ -75,6 +75,17 @@ public class GeoMagneticModelLoader implements DataLoader { /** The loaded models. */ private List models = new LinkedList(); + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public GeoMagneticModelLoader() { + // nothing to do + } + /** Returns a {@link Collection} of the {@link GeoMagneticField} models that * have been successfully loaded. The {@link Collection} is in * insertion-order, thus it may not be sorted in order of the model epoch. diff --git a/src/main/java/org/orekit/models/earth/Geoid.java b/src/main/java/org/orekit/models/earth/Geoid.java index bfdb4adb9b..bde307050a 100644 --- a/src/main/java/org/orekit/models/earth/Geoid.java +++ b/src/main/java/org/orekit/models/earth/Geoid.java @@ -16,8 +16,8 @@ */ package org.orekit.models.earth; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.CalculusFieldUnivariateFunction; import org.hipparchus.analysis.UnivariateFunction; import org.hipparchus.analysis.solvers.AllowedSolution; @@ -37,7 +37,7 @@ import org.orekit.forces.gravity.potential.GravityFields; import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.TideSystem; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.time.AbsoluteDate; @@ -162,7 +162,7 @@ public class Geoid implements EarthShape { * {@code geopotential} and the {@code * referenceEllipsoid} are defined in the same * frame. Usually a {@link GravityFields#getConstantNormalizedProvider(int, - * int) constant geopotential} is used to define a + * int, AbsoluteDate) constant geopotential} is used to define a * time-invariant Geoid. * @param referenceEllipsoid the normal gravity potential. * @throws NullPointerException if {@code geopotential == null || @@ -234,7 +234,7 @@ public double getUndulation(final double geodeticLatitude, .getNormalGravity(geodeticLatitude); // calculate disturbing potential, T, eq 30. - final double mu = this.harmonics.getMu(); + final double mu = this.harmonics.getMu(date); final double T = this.harmonics.nonCentralPart(date, position, mu); // calculate undulation, eq 30 return T / normalGravity; @@ -302,12 +302,6 @@ public AbsoluteDate getReferenceDate() { return this.provider.getReferenceDate(); } - @Deprecated - @Override - public double getOffset(final AbsoluteDate date) { - return this.provider.getOffset(date); - } - @Override public NormalizedSphericalHarmonics onDate(final AbsoluteDate date) { return new NormalizedSphericalHarmonics() { @@ -479,7 +473,7 @@ public > FieldGeodeticPoint getIntersection */ // transform to body frame final Frame bodyFrame = this.getBodyFrame(); - final FieldTransform frameToBody = frame.getTransformTo(bodyFrame, date); + final FieldStaticTransform frameToBody = frame.getStaticTransformTo(bodyFrame, date); final FieldVector3D close = frameToBody.transformPosition(closeInFrame); final FieldLine lineInBodyFrame = frameToBody.transformLine(lineInFrame); diff --git a/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java b/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java index a07084c695..21aed5609e 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; @@ -68,7 +69,7 @@ public interface Atmosphere extends Serializable { */ default Vector3D getVelocity(AbsoluteDate date, Vector3D position, Frame frame) { final Transform bodyToFrame = getFrame().getTransformTo(frame, date); - final Vector3D posInBody = bodyToFrame.getInverse().transformPosition(position); + final Vector3D posInBody = bodyToFrame.toStaticTransform().getInverse().transformPosition(position); final PVCoordinates pvBody = new PVCoordinates(posInBody, Vector3D.ZERO); final PVCoordinates pvFrame = bodyToFrame.transformPVCoordinates(pvBody); return pvFrame.getVelocity(); @@ -82,9 +83,10 @@ default Vector3D getVelocity(AbsoluteDate date, Vector3D position, Frame frame) * @return velocity (m/s) (defined in the same frame as the position) */ default > FieldVector3D getVelocity(FieldAbsoluteDate date, FieldVector3D position, Frame frame) { - final Transform bodyToFrame = getFrame().getTransformTo(frame, date.toAbsoluteDate()); - final FieldVector3D posInBody = bodyToFrame.getInverse().transformPosition(position); - final FieldPVCoordinates pvBody = new FieldPVCoordinates<>(posInBody, FieldVector3D.getZero(position.getX().getField())); + final FieldTransform bodyToFrame = getFrame().getTransformTo(frame, date); + final FieldVector3D posInBody = bodyToFrame.toStaticTransform().getInverse().transformPosition(position); + final FieldPVCoordinates pvBody = new FieldPVCoordinates<>(posInBody, + FieldVector3D.getZero(date.getField())); final FieldPVCoordinates pvFrame = bodyToFrame.transformPVCoordinates(pvBody); return pvFrame.getVelocity(); } diff --git a/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java b/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java index d0a8973391..8102af1d65 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -82,24 +82,6 @@ */ public class DTM2000 implements Atmosphere { - /** Identifier for hydrogen. */ - public static final int HYDROGEN = 1; - - /** Identifier for helium. */ - public static final int HELIUM = 2; - - /** Identifier for atomic oxygen. */ - public static final int ATOMIC_OXYGEN = 3; - - /** Identifier for molecular nitrogen. */ - public static final int MOLECULAR_NITROGEN = 4; - - /** Identifier for molecular oxygen. */ - public static final int MOLECULAR_OXYGEN = 5; - - /** Identifier for atomic nitrogen. */ - public static final int ATOMIC_NITROGEN = 6; - /** Serializable UID. */ private static final long serialVersionUID = 20170705L; @@ -365,7 +347,7 @@ public double getDensity(final AbsoluteDate date, final Vector3D position, final double lat = inBody.getLatitude(); // compute local solar time - final Vector3D sunPos = sun.getPVCoordinates(date, ecef).getPosition(); + final Vector3D sunPos = sun.getPosition(date, ecef); final double hl = FastMath.PI + FastMath.atan2( sunPos.getX() * pEcef.getY() - sunPos.getY() * pEcef.getX(), sunPos.getX() * pEcef.getX() + sunPos.getY() * pEcef.getY()); @@ -394,7 +376,7 @@ public double getDensity(final AbsoluteDate date, final Vector3D position, final int day = date.getComponents(utc).getDate().getDayOfYear(); // position in ECEF so we only have to do the transform once final Frame ecef = earth.getBodyFrame(); - final FieldVector3D pEcef = frame.getTransformTo(ecef, date).transformPosition(position); + final FieldVector3D pEcef = frame.getStaticTransformTo(ecef, date).transformPosition(position); // compute geodetic position final FieldGeodeticPoint inBody = earth.transform(pEcef, ecef, date); final T alti = inBody.getAltitude(); @@ -402,7 +384,7 @@ public double getDensity(final AbsoluteDate date, final Vector3D position, final T lat = inBody.getLatitude(); // compute local solar time - final Vector3D sunPos = sun.getPVCoordinates(dateD, ecef).getPosition(); + final Vector3D sunPos = sun.getPosition(dateD, ecef); final T y = pEcef.getY().multiply(sunPos.getX()).subtract(pEcef.getX().multiply(sunPos.getY())); final T x = pEcef.getX().multiply(sunPos.getX()).add(pEcef.getY().multiply(sunPos.getY())); final T hl = y.atan2(x).add(y.getPi()); diff --git a/src/main/java/org/orekit/models/earth/atmosphere/DTM2000InputParameters.java b/src/main/java/org/orekit/models/earth/atmosphere/DTM2000InputParameters.java index efc16da682..1f19723933 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/DTM2000InputParameters.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/DTM2000InputParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java b/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java index cff1ff1f34..f7ae30c8c6 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -383,7 +383,7 @@ public > T getDensity(final Vector3D sunInEart public double getDensity(final AbsoluteDate date, final Vector3D position, final Frame frame) { // Sun position in earth frame - final Vector3D sunInEarth = sun.getPVCoordinates(date, earth.getBodyFrame()).getPosition(); + final Vector3D sunInEarth = sun.getPosition(date, earth.getBodyFrame()); // Target position in earth frame final Vector3D posInEarth = frame @@ -405,7 +405,7 @@ public > T getDensity(final FieldAbsoluteDate< final FieldVector3D position, final Frame frame) { // Sun position in earth frame - final Vector3D sunInEarth = sun.getPVCoordinates(date.toAbsoluteDate(), earth.getBodyFrame()).getPosition(); + final Vector3D sunInEarth = sun.getPosition(date.toAbsoluteDate(), earth.getBodyFrame()); // Target position in earth frame final FieldVector3D posInEarth = frame diff --git a/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java b/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java index 9b4a066a17..5cb45077b5 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -503,7 +503,7 @@ public double getDensity(final double dateMJD, final double sunRA, final double * @param y10B Solar X-Ray & Lya 81-day ave. centered index
        * (Tabular time 5.0 days earlier) * @param dstdtc Temperature change computed from Dst index - * @param type fo the field elements + * @param type of the field elements * @return total mass-Density at input position (kg/m³) */ public > T getDensity(final T dateMJD, final T sunRA, final T sunDecli, @@ -869,7 +869,7 @@ private static double poly1CDTC(final double fs, final double st, final double c * @param fs scaled flux f10 * @param st local solar time in [0, 1[ * @param cs cosine of satLat - * @param type fo the field elements + * @param type of the field elements * @return the value of the polynomial */ private static > T poly1CDTC(final double fs, final T st, final T cs) { @@ -906,7 +906,7 @@ private static double poly2CDTC(final double fs, final double st, final double c * @param fs scaled flux f10 * @param st local solar time in [0, 1[ * @param cs cosine of satLat - * @param type fo the field elements + * @param type of the field elements * @return the value of the polynomial */ private static > T poly2CDTC(final double fs, final T st, final T cs) { @@ -937,7 +937,7 @@ private static double poly1BDTC(final double fs, final double st, final double c * @param st local solar time in [0, 1[ * @param cs cosine of satLat * @param hp scaled height * poly2BDTC - * @param type fo the field elements + * @param type of the field elements * @return the value of the polynomial */ private static > T poly1BDTC(final double fs, final T st, final T cs, final T hp) { @@ -966,7 +966,7 @@ private static double poly2BDTC(final double st) { /** Calculates second polynomial with BDTC array. * @param st local solar time in [0, 1[ - * @param type fo the field elements + * @param type of the field elements * @return the value of the polynomial */ private static > T poly2BDTC(final T st) { @@ -994,7 +994,7 @@ private static double mBar(final double z) { /** Evaluates mean molecualr mass - Equation (1). * @param z altitude (km) * @return mean molecular mass - * @param type fo the field elements + * @param type of the field elements */ private static > T mBar(final T z) { final T dz = z.subtract(100.); @@ -1023,7 +1023,7 @@ private static double localTemp(final double z, final double[] tc) { * @param z altitude * @param tc tc array * @return temperature profile - * @param type fo the field elements + * @param type of the field elements */ private static > T localTemp(final T z, final T[] tc) { final T dz = z.subtract(125.); @@ -1046,7 +1046,7 @@ private static double gravity(final double z) { /** Evaluates the gravity at the altitude - Equation (8). * @param z altitude (km) * @return the gravity (m/s2) - * @param type fo the field elements + * @param type of the field elements */ private static > T gravity(final T z) { final T tmp = z.divide(EARTH_RADIUS).add(1); @@ -1093,7 +1093,7 @@ private static double semian08(final double doy, final double alt, * @param s10B average 81-day centered s10 * @param xm10B average 81-day centered xn10 * @return semi-annual variation - * @param type fo the field elements + * @param type of the field elements */ private static > T semian08(final T doy, final T alt, final double f10B, final double s10B, final double xm10B) { @@ -1178,7 +1178,7 @@ private static > T dayOfYear(final T dateMJD) /** Compute min of two values, one double and one field element. * @param d double value * @param f field element - * @param type fo the field elements + * @param type of the field elements * @return min value */ private > T min(final double d, final T f) { @@ -1188,7 +1188,7 @@ private > T min(final double d, final T f) { /** Compute max of two values, one double and one field element. * @param d double value * @param f field element - * @param type fo the field elements + * @param type of the field elements * @return max value */ private > T max(final double d, final T f) { @@ -1220,7 +1220,7 @@ public double getDensity(final AbsoluteDate date, final Vector3D position, // compute sun position final Frame ecef = earth.getBodyFrame(); - final Vector3D sunPos = sun.getPVCoordinates(date, ecef).getPosition(); + final Vector3D sunPos = sun.getPosition(date, ecef); final GeodeticPoint sunInBody = earth.transform(sunPos, ecef, date); return getDensity(dateMJD, @@ -1262,7 +1262,7 @@ public > T getDensity(final FieldAbsoluteDate< // compute sun position final Frame ecef = earth.getBodyFrame(); final FieldVector3D sunPos = new FieldVector3D<>(date.getField(), - sun.getPVCoordinates(dateD, ecef).getPosition()); + sun.getPosition(dateD, ecef)); final FieldGeodeticPoint sunInBody = earth.transform(sunPos, ecef, date); return getDensity(dateMJD, diff --git a/src/main/java/org/orekit/models/earth/atmosphere/JB2008InputParameters.java b/src/main/java/org/orekit/models/earth/atmosphere/JB2008InputParameters.java index 194f7f2638..035e59c623 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/JB2008InputParameters.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/JB2008InputParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java b/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java index a74da8a940..bd429b6479 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -1228,7 +1228,7 @@ public > T getDensity(final FieldAbsoluteDate< private double localSolarTime(final AbsoluteDate date, final Vector3D position, final Frame frame) { - final Vector3D sunPos = sun.getPVCoordinates(date, frame).getPosition(); + final Vector3D sunPos = sun.getPosition(date, frame); final double lst = FastMath.PI + FastMath.atan2( sunPos.getX() * position.getY() - sunPos.getY() * position.getX(), sunPos.getX() * position.getX() + sunPos.getY() * position.getY()); @@ -1245,7 +1245,7 @@ private double localSolarTime(final AbsoluteDate date, private > T localSolarTime(final AbsoluteDate date, final FieldVector3D position, final Frame frame) { - final Vector3D sunPos = sun.getPVCoordinates(date, frame).getPosition(); + final Vector3D sunPos = sun.getPosition(date, frame); final T y = position.getY().multiply(sunPos.getX()).subtract(position.getX().multiply(sunPos.getY())); final T x = position.getX().multiply(sunPos.getX()).add(position.getY().multiply(sunPos.getY())); final T hl = y.atan2(x).add(y.getPi()); diff --git a/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00InputParameters.java b/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00InputParameters.java index b87ff536c5..ff3e0fba73 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00InputParameters.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00InputParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/SimpleExponentialAtmosphere.java b/src/main/java/org/orekit/models/earth/atmosphere/SimpleExponentialAtmosphere.java index 5e94873f8b..64222fe1f9 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/SimpleExponentialAtmosphere.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/SimpleExponentialAtmosphere.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/AbstractSolarActivityData.java b/src/main/java/org/orekit/models/earth/atmosphere/data/AbstractSolarActivityData.java new file mode 100644 index 0000000000..25632ded84 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/AbstractSolarActivityData.java @@ -0,0 +1,347 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.models.earth.atmosphere.data; + +import org.hipparchus.exception.DummyLocalizable; +import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.atmosphere.DTM2000InputParameters; +import org.orekit.models.earth.atmosphere.NRLMSISE00InputParameters; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeStamped; +import org.orekit.utils.GenericTimeStampedCache; +import org.orekit.utils.ImmutableTimeStampedCache; +import org.orekit.utils.TimeStampedGenerator; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Abstract class for solar activity data. + * + * @param type of the line parameters + * @param type of the solar activity data loader + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public abstract class AbstractSolarActivityData> + implements DTM2000InputParameters, NRLMSISE00InputParameters { + + /** Size of the list. */ + protected static final int N_NEIGHBORS = 2; + + /** Serializable UID. */ + private static final long serialVersionUID = 8804818166227680449L; + + /** Weather data thread safe cache. */ + private final transient GenericTimeStampedCache cache; + + /** Supported names. */ + private final String supportedNames; + + /** UTC time scale. */ + private final TimeScale utc; + + /** First available date. */ + private final AbsoluteDate firstDate; + + /** Last available date. */ + private final AbsoluteDate lastDate; + + /** + * @param supportedNames regular expression for supported AGI/CSSI space weather files names + * @param loader data loader + * @param dataProvidersManager provides access to auxiliary data files. + * @param utc UTC time scale + * @param maxSlots maximum number of independent cached time slots in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache} + * @param maxInterval time interval above which a new slot is created in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param minimumStep overriding minimum step designed for non-homogeneous tabulated values. To be used for example when + * caching monthly tabulated values. May be null. + */ + protected AbstractSolarActivityData(final String supportedNames, final D loader, + final DataProvidersManager dataProvidersManager, final TimeScale utc, + final int maxSlots, final double maxSpan, final double maxInterval, + final double minimumStep) { + // Load data + dataProvidersManager.feed(supportedNames, loader); + + // Create thread safe cache + this.cache = new GenericTimeStampedCache<>(N_NEIGHBORS, maxSlots, maxSpan, maxInterval, + new SolarActivityGenerator(loader.getDataSet()), minimumStep); + + // Initialise fields + this.supportedNames = supportedNames; + this.utc = utc; + this.firstDate = loader.getMinDate(); + this.lastDate = loader.getMaxDate(); + } + + /** + * Simple constructor. + * + * @param source source for the data + * @param loader data loader + * @param utc UTC time scale + * @param maxSlots maximum number of independent cached time slots in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache} + * @param maxInterval time interval above which a new slot is created in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param minimumStep overriding minimum step designed for non-homogeneous tabulated values. To be used for example when + * caching monthly tabulated values. May be null. + * + * @since 12.0 + */ + public AbstractSolarActivityData(final DataSource source, final D loader, final TimeScale utc, final int maxSlots, + final double maxSpan, final double maxInterval, final double minimumStep) { + try { + // Load file + try (InputStream is = source.getOpener().openStreamOnce(); + BufferedInputStream bis = new BufferedInputStream(is)) { + loader.loadData(bis, source.getName()); + } + + // Create thread safe cache + this.cache = new GenericTimeStampedCache<>(N_NEIGHBORS, maxSlots, maxSpan, maxInterval, + new SolarActivityGenerator(loader.getDataSet()), minimumStep); + + // Initialise fields + this.supportedNames = source.getName(); + this.utc = utc; + this.firstDate = loader.getMinDate(); + this.lastDate = loader.getMaxDate(); + } + catch (IOException | ParseException ioe) { + throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage())); + } + } + + /** + * Performs a linear interpolation between two values The weights are computed from the time delta between previous date, + * current date, next date. + * + * @param date current date + * @param solarActivityToDoubleMapper mapping function taking solar activity as input and returning a double + * + * @return the value interpolated for the current date + */ + protected double getLinearInterpolation(final AbsoluteDate date, final Function solarActivityToDoubleMapper) { + // Create solar activity around current date + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + + // Extract values + return getLinearInterpolation(localSolarActivity, solarActivityToDoubleMapper); + } + + /** + * Performs a linear interpolation between two values The weights are computed from the time delta between previous date, + * current date, next date. + * + * @param localSolarActivity solar activity around current date + * @param solarActivityToDoubleMapper mapping function taking solar activity as input and returning a double + * + * @return the value interpolated for the current date + */ + protected double getLinearInterpolation(final LocalSolarActivity localSolarActivity, + final Function solarActivityToDoubleMapper) { + // Extract values + final L previousParameters = localSolarActivity.getPreviousParam(); + final double previousValue = solarActivityToDoubleMapper.apply(previousParameters); + + final L nextParameters = localSolarActivity.getNextParam(); + final double nextValue = solarActivityToDoubleMapper.apply(nextParameters); + + // Perform a linear interpolation + final AbsoluteDate previousDate = localSolarActivity.getPreviousParam().getDate(); + final AbsoluteDate currentDate = localSolarActivity.getNextParam().getDate(); + final double dt = currentDate.durationFrom(previousDate); + final AbsoluteDate date = localSolarActivity.getDate(); + final double previousWeight = currentDate.durationFrom(date) / dt; + final double nextWeight = date.durationFrom(previousDate) / dt; + + // Returns the data interpolated at the date + return previousValue * previousWeight + nextValue * nextWeight; + } + + /** + * Get underlying cache. + * + * @return cache + */ + public GenericTimeStampedCache getCache() { + return cache; + } + + /** + * Get the supported names regular expression. + * + * @return the supported names. + */ + public String getSupportedNames() { + return supportedNames; + } + + /** + * Get the UTC timescale. + * + * @return UTC timescale + */ + public TimeScale getUTC() { + return utc; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getMinDate() { + return firstDate; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getMaxDate() { + return lastDate; + } + + /** Container for weather parameters around current date. Allows for thread safe use. */ + protected class LocalSolarActivity implements TimeStamped { + + /** Date. */ + private final AbsoluteDate currentDate; + + /** Previous parameters. */ + private final L previousParam; + + /** Next parameters. */ + private final L nextParam; + + /** + * Constructor. + * + * @param date current date + */ + public LocalSolarActivity(final AbsoluteDate date) { + // Asked date is before earliest available data + if (date.durationFrom(firstDate) < 0) { + throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, date, firstDate, lastDate, + firstDate.durationFrom(date)); + } + // Asked date is after latest available data + if (date.durationFrom(lastDate) > 0) { + throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER, date, firstDate, lastDate, + date.durationFrom(lastDate)); + } + + final List neighbours = cache.getNeighbors(date).collect(Collectors.toList()); + + this.currentDate = date; + this.previousParam = neighbours.get(0); + this.nextParam = neighbours.get(1); + } + + /** @return current date */ + public AbsoluteDate getDate() { + return currentDate; + } + + /** @return previous parameters */ + public L getPreviousParam() { + return previousParam; + } + + /** @return next parameters */ + public L getNextParam() { + return nextParam; + } + } + + /** Generator used in the weather data cache. */ + protected class SolarActivityGenerator implements TimeStampedGenerator { + + /** + * Default time step to shift the date. + *

        + * It is used so that, in the case where the earliest date is exactly at noon, we do not get the following interval + * [previous day; current day] but rather the expected interval [current day; next day] + */ + private static final double STEP = 1; + + /** Data set. */ + private final ImmutableTimeStampedCache data; + + /** + * Constructor. + * + * @param dataSet weather data + */ + protected SolarActivityGenerator(final Collection dataSet) { + this.data = new ImmutableTimeStampedCache<>(N_NEIGHBORS, dataSet); + } + + /** {@inheritDoc} */ + @Override + public List generate(final AbsoluteDate existingDate, final AbsoluteDate date) { + // No prior data in the cache + if (existingDate == null) { + return data.getNeighbors(date).collect(Collectors.toList()); + } + // Prior data in the cache, fill with data between date and existing date + if (date.isBefore(existingDate)) { + return generateDataFromEarliestToLatestDates(date, existingDate); + } + return generateDataFromEarliestToLatestDates(existingDate, date); + } + + /** + * Generate a list of parameters between earliest and latest dates. + * + * @param earliest earliest date + * @param latest latest date + * + * @return list of parameters between earliest and latest dates + */ + public List generateDataFromEarliestToLatestDates(final AbsoluteDate earliest, final AbsoluteDate latest) { + /* Gives first two parameters bracketing the earliest date + * A date shifted by step is used so that, in the case where the earliest date is exactly noon, we do not get the + * following interval [previous day; current day] but rather the expected interval [current day; next day] */ + List neighbours = data.getNeighbors(earliest.shiftedBy(STEP)).collect(Collectors.toList()); + + // Get next parameter until it brackets the latest date + AbsoluteDate latestNeighbourDate = neighbours.get(1).getDate(); + final List params = new ArrayList<>(neighbours); + while (latestNeighbourDate.isBefore(latest)) { + neighbours = data.getNeighbors(latestNeighbourDate.shiftedBy(STEP)).collect(Collectors.toList()); + params.add(neighbours.get(1)); + latestNeighbourDate = neighbours.get(1).getDate(); + } + return params; + } + } +} diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/AbstractSolarActivityDataLoader.java b/src/main/java/org/orekit/models/earth/atmosphere/data/AbstractSolarActivityDataLoader.java new file mode 100644 index 0000000000..112b0aab95 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/AbstractSolarActivityDataLoader.java @@ -0,0 +1,153 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.models.earth.atmosphere.data; + +import org.orekit.data.DataLoader; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeStamped; + +import java.io.Serializable; +import java.util.SortedSet; + +/** + * Abstract class for solar activity data loader. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public abstract class AbstractSolarActivityDataLoader + implements DataLoader { + + /** UTC time scale. */ + private final TimeScale utc; + + /** First available date. */ + private AbsoluteDate firstDate; + + /** Last available date. */ + private AbsoluteDate lastDate; + + /** + * Constructor. + * + * @param utc UTC time scale + */ + protected AbstractSolarActivityDataLoader(final TimeScale utc) { + this.utc = utc; + } + + /** {@inheritDoc} */ + @Override + public boolean stillAcceptsData() { + return true; + } + + /** + * Get the data set. + * + * @return the data set + */ + public abstract SortedSet getDataSet(); + + /** + * Get the UTC timescale. + * + * @return the UTC timescale + */ + public TimeScale getUTC() { + return utc; + } + + /** + * Gets the available data range minimum date. + * + * @return the minimum date. + */ + public AbsoluteDate getMinDate() { + return firstDate; + } + + /** + * Gets the available data range maximum date. + * + * @return the maximum date. + */ + public AbsoluteDate getMaxDate() { + return lastDate; + } + + /** Container class for Solar activity indexes. */ + public abstract static class LineParameters implements TimeStamped, Comparable, Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = 6607862001953526475L; + + /** Entry date. */ + private final AbsoluteDate date; + + /** + * Constructor. + * + * @param date entry date + */ + protected LineParameters(final AbsoluteDate date) { + this.date = date; + } + + /** {@inheritDoc} */ + @Override + public abstract int compareTo(LineParameters lineParameters); + + /** Check if the instance represents the same parameters as given line parameters. + * @param lineParameters other line parameters + * @return true if the instance and the other line parameter contain the same parameters + */ + @Override + public abstract boolean equals(Object lineParameters); + + /** Get a hashcode for this date. + * @return hashcode + */ + @Override + public abstract int hashCode(); + + /** @return entry date */ + @Override + public AbsoluteDate getDate() { + return date; + } + } + + /** + * Set the available data range minimum date. + * + * @param date available data range minimum date + */ + public void setMinDate(final AbsoluteDate date) { + this.firstDate = date; + } + + /** + * Set the available data range maximum date. + * + * @param date available data range maximum date + */ + public void setMaxDate(final AbsoluteDate date) { + this.lastDate = date; + } +} diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/CommonLineReader.java b/src/main/java/org/orekit/models/earth/atmosphere/data/CommonLineReader.java index a9ef80a8c3..9b86b2d415 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/CommonLineReader.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/CommonLineReader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherData.java b/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherData.java index fb19b0ebb9..7df65825b3 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherData.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherData.java @@ -17,42 +17,35 @@ package org.orekit.models.earth.atmosphere.data; -import java.util.List; -import java.util.stream.Collectors; - import org.orekit.annotation.DefaultDataContext; -import org.orekit.data.AbstractSelfFeedingLoader; import org.orekit.data.DataContext; import org.orekit.data.DataProvidersManager; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.models.earth.atmosphere.DTM2000InputParameters; -import org.orekit.models.earth.atmosphere.NRLMSISE00InputParameters; +import org.orekit.data.DataSource; import org.orekit.models.earth.atmosphere.data.CssiSpaceWeatherDataLoader.LineParameters; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; -import org.orekit.time.TimeStamped; import org.orekit.utils.Constants; -import org.orekit.utils.ImmutableTimeStampedCache; +import org.orekit.utils.GenericTimeStampedCache; +import org.orekit.utils.OrekitConfiguration; /** - * This class provides three-hourly and daily solar activity data needed by atmospheric - * models: F107 solar flux, Ap and Kp indexes. - * The {@link org.orekit.data.DataLoader} implementation and the parsing is handled by the class {@link CssiSpaceWeatherDataLoader}. + * This class provides three-hourly and daily solar activity data needed by atmospheric models: F107 solar flux, Ap and Kp + * indexes. The {@link org.orekit.data.DataLoader} implementation and the parsing is handled by the class + * {@link CssiSpaceWeatherDataLoader}. *

        * The data are retrieved through space weather files offered by AGI/CSSI on the AGI - * FTP as - * well as on the CelesTrack website. - * These files are updated several times a day by using several sources mentioned in the + * FTP as + * well as on the CelesTrack website. These files are updated several times a + * day by using several sources mentioned in the * Celestrak space * weather data documentation. *

        * * @author Clément Jonglez + * @author Vincent Cucchietti * @since 10.2 */ -public class CssiSpaceWeatherData extends AbstractSelfFeedingLoader - implements DTM2000InputParameters, NRLMSISE00InputParameters { +public class CssiSpaceWeatherData extends AbstractSolarActivityData { /** Default regular expression for supported names that works with all officially published files. */ public static final String DEFAULT_SUPPORTED_NAMES = "^S(?:pace)?W(?:eather)?-(?:All)?.*\\.txt$"; @@ -60,41 +53,25 @@ public class CssiSpaceWeatherData extends AbstractSelfFeedingLoader /** Serializable UID. */ private static final long serialVersionUID = 4249411710645968978L; - /** Size of the list. */ - private static final int N_NEIGHBORS = 2; - - /** Data set. */ - private final transient ImmutableTimeStampedCache data; - - /** UTC time scale. */ - private final TimeScale utc; - - /** First available date. */ - private final AbsoluteDate firstDate; - /** Date of last data before the prediction starts. */ private final AbsoluteDate lastObservedDate; /** Date of last daily prediction before the monthly prediction starts. */ private final AbsoluteDate lastDailyPredictedDate; - /** Last available date. */ - private final AbsoluteDate lastDate; - - /** Previous set of solar activity parameters. */ - private LineParameters previousParam; - - /** Current set of solar activity parameters. */ - private LineParameters nextParam; - /** * Simple constructor. This constructor uses the default data context. *

        - * The original file names provided by AGI/CSSI are of the form: - * SpaceWeather-All-v1.2.txt (AGI's ftp) or SW-Last5Years.txt (CelesTrak's website). - * So a recommended regular expression for the supported names that works - * with all published files is: {@link #DEFAULT_SUPPORTED_NAMES}. - *

        + * The original file names provided by AGI/CSSI are of the form: SpaceWeather-All-v1.2.txt + * (AGI's ftp). So a recommended regular + * expression for the supported names that works with all published files is: {@link #DEFAULT_SUPPORTED_NAMES}. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_DAY}
        • + *
        • Max span interval : {@code 0}
        • + *
        * * @param supportedNames regular expression for supported AGI/CSSI space weather files names */ @@ -105,100 +82,151 @@ public CssiSpaceWeatherData(final String supportedNames) { } /** - * Constructor that allows specifying the source of the CSSI space weather - * file. + * Constructor that allows specifying the source of the CSSI space weather file. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_DAY}
        • + *
        • Max span interval : {@code 0}
        • + *
        * - * @param supportedNames regular expression for supported AGI/CSSI space weather files names + * @param supportedNames regular expression for supported AGI/CSSI space weather files names * @param dataProvidersManager provides access to auxiliary data files. - * @param utc UTC time scale. + * @param utc UTC time scale. */ - public CssiSpaceWeatherData(final String supportedNames, - final DataProvidersManager dataProvidersManager, + public CssiSpaceWeatherData(final String supportedNames, final DataProvidersManager dataProvidersManager, final TimeScale utc) { - super(supportedNames, dataProvidersManager); - - this.utc = utc; - final CssiSpaceWeatherDataLoader loader = - new CssiSpaceWeatherDataLoader(utc); - this.feed(loader); - data = - new ImmutableTimeStampedCache<>(N_NEIGHBORS, loader.getDataSet()); - firstDate = loader.getMinDate(); - lastDate = loader.getMaxDate(); - lastObservedDate = loader.getLastObservedDate(); - lastDailyPredictedDate = loader.getLastDailyPredictedDate(); + this(supportedNames, new CssiSpaceWeatherDataLoader(utc), dataProvidersManager, utc); } - /** {@inheritDoc} */ - public AbsoluteDate getMinDate() { - return firstDate; + /** + * Constructor that allows specifying the source of the CSSI space weather file. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_DAY}
        • + *
        • Max span interval : {@code 0}
        • + *
        + * + * @param supportedNames regular expression for supported AGI/CSSI space weather files names + * @param loader data loader + * @param dataProvidersManager provides access to auxiliary data files. + * @param utc UTC time scale + */ + public CssiSpaceWeatherData(final String supportedNames, final CssiSpaceWeatherDataLoader loader, + final DataProvidersManager dataProvidersManager, final TimeScale utc) { + this(supportedNames, loader, dataProvidersManager, utc, OrekitConfiguration.getCacheSlotsNumber(), + Constants.JULIAN_DAY, 0); } - /** {@inheritDoc} */ - public AbsoluteDate getMaxDate() { - return lastDate; + /** + * Constructor that allows specifying the source of the CSSI space weather file and customizable thread safe cache + * configuration. + * + * @param supportedNames regular expression for supported AGI/CSSI space weather files names + * @param loader data loader + * @param dataProvidersManager provides access to auxiliary data files. + * @param utc UTC time scale + * @param maxSlots maximum number of independent cached time slots in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache} + * @param maxInterval time interval above which a new slot is created in the + * {@link GenericTimeStampedCache time-stamped cache} + */ + public CssiSpaceWeatherData(final String supportedNames, final CssiSpaceWeatherDataLoader loader, + final DataProvidersManager dataProvidersManager, final TimeScale utc, final int maxSlots, + final double maxSpan, final double maxInterval) { + super(supportedNames, loader, dataProvidersManager, utc, maxSlots, maxSpan, maxInterval, Double.NaN); + + // Initialise fields + this.lastObservedDate = loader.getLastObservedDate(); + this.lastDailyPredictedDate = loader.getLastDailyPredictedDate(); } /** - * Find the data bracketing a specified date. + * Simple constructor which use the {@link DataContext#getDefault() default data context}. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_DAY}
        • + *
        • Max span interval : {@code 0}
        • + *
        + * + * @param source source for the data * - * @param date date to bracket + * @since 12.0 */ - private void bracketDate(final AbsoluteDate date) { - if (date.durationFrom(firstDate) < 0) { - throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, - date, firstDate, lastDate, firstDate.durationFrom(date)); - } - if (date.durationFrom(lastDate) > 0) { - throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER, - date, firstDate, lastDate, date.durationFrom(lastDate)); - } + @DefaultDataContext + public CssiSpaceWeatherData(final DataSource source) { + this(source, DataContext.getDefault().getTimeScales().getUTC()); + } - // don't search if the cached selection is fine - if (previousParam != null && date.durationFrom(previousParam.getDate()) > 0 && - date.durationFrom(nextParam.getDate()) <= 0) { - return; - } + /** + * Simple constructor. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_DAY}
        • + *
        • Max span interval : {@code 0}
        • + *
        + * + * @param source source for the data + * @param utc UTC time scale + * + * @since 12.0 + */ + public CssiSpaceWeatherData(final DataSource source, final TimeScale utc) { + this(source, new CssiSpaceWeatherDataLoader(utc), utc); + } - final List neigbors = data.getNeighbors(date).collect(Collectors.toList()); - previousParam = (LineParameters) neigbors.get(0); - nextParam = (LineParameters) neigbors.get(1); - if (previousParam.getDate().compareTo(date) > 0) { // TODO delete dead code - /** - * Throwing exception if neighbors are unbalanced because we are at the - * beginning of the data set - */ - throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE, date, firstDate, lastDate); - } + /** + * Simple constructor. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_DAY}
        • + *
        • Max span interval : {@code 0}
        • + *
        + * + * @param source source for the data + * @param loader data loader + * @param utc UTC time scale + * + * @since 12.0 + */ + public CssiSpaceWeatherData(final DataSource source, final CssiSpaceWeatherDataLoader loader, final TimeScale utc) { + this(source, loader, utc, OrekitConfiguration.getCacheSlotsNumber(), Constants.JULIAN_DAY, 0); } /** - * Performs a linear interpolation between two values The weights are computed - * from the time delta between previous date, current date, next date. + * Simple constructor with customizable thread safe cache configuration. + * + * @param source source for the data + * @param loader data loader + * @param utc UTC time scale + * @param maxSlots maximum number of independent cached time slots in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache} + * @param maxInterval time interval above which a new slot is created in the + * {@link GenericTimeStampedCache time-stamped cache} * - * @param date the current date - * @param previousValue the value at previous date - * @param nextValue the value at next date - * @return the value interpolated for the current date + * @since 12.0 */ - private double getLinearInterpolation(final AbsoluteDate date, final double previousValue, final double nextValue) { - // perform a linear interpolation - final AbsoluteDate previousDate = previousParam.getDate(); - final AbsoluteDate currentDate = nextParam.getDate(); - final double dt = currentDate.durationFrom(previousDate); - final double previousWeight = currentDate.durationFrom(date) / dt; - final double nextWeight = date.durationFrom(previousDate) / dt; - - // returns the data interpolated at the date - return previousValue * previousWeight + nextValue * nextWeight; + public CssiSpaceWeatherData(final DataSource source, final CssiSpaceWeatherDataLoader loader, final TimeScale utc, + final int maxSlots, final double maxSpan, final double maxInterval) { + super(source, loader, utc, maxSlots, maxSpan, maxInterval, Double.NaN); + this.lastObservedDate = loader.getLastObservedDate(); + this.lastDailyPredictedDate = loader.getLastDailyPredictedDate(); } /** {@inheritDoc} */ public double getInstantFlux(final AbsoluteDate date) { - // Interpolating two neighboring daily fluxes - // get the neighboring dates - bracketDate(date); - return getLinearInterpolation(date, previousParam.getF107Obs(), nextParam.getF107Obs()); + return getLinearInterpolation(date, LineParameters::getF107Obs); } /** {@inheritDoc} */ @@ -209,40 +237,35 @@ public double getMeanFlux(final AbsoluteDate date) { /** {@inheritDoc} */ public double getThreeHourlyKP(final AbsoluteDate date) { if (date.compareTo(lastObservedDate) <= 0) { - /** - * If observation data is available, it contains three-hourly data - */ - bracketDate(date); - final double hourOfDay = date.offsetFrom(previousParam.getDate(), utc) / 3600; - int i_kp = (int) (hourOfDay / 3); + /* If observation data is available, it contains three-hourly data */ + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + final LineParameters previousParam = localSolarActivity.getPreviousParam(); + final double hourOfDay = date.offsetFrom(previousParam.getDate(), getUTC()) / 3600; + int i_kp = (int) (hourOfDay / 3); if (i_kp >= 8) { - /** - * hourOfDay can take the value 24.0 at midnight due to floating point precision + /* hourOfDay can take the value 24.0 at midnight due to floating point precision * when bracketing the dates or during a leap second because the hour of day is - * computed in UTC view - */ + * computed in UTC view */ i_kp = 7; } return previousParam.getThreeHourlyKp(i_kp); } else { - /** - * Only predictions are available, there are no three-hourly data - */ + /* Only predictions are available, there are no three-hourly data */ return get24HoursKp(date); } } /** {@inheritDoc} */ public double get24HoursKp(final AbsoluteDate date) { + // Get the neighboring solar activity + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + if (date.compareTo(lastDailyPredictedDate) <= 0) { // Daily data is available, just taking the daily average - bracketDate(date); - return previousParam.getKpSum() / 8; + return localSolarActivity.getPreviousParam().getKpSum() / 8; } else { // Only monthly data is available, better interpolate between two months - // get the neighboring dates - bracketDate(date); - return getLinearInterpolation(date, previousParam.getKpSum() / 8, nextParam.getKpSum() / 8); + return getLinearInterpolation(localSolarActivity, lineParam -> lineParam.getKpSum() / 8); } } @@ -252,35 +275,16 @@ public double getDailyFlux(final AbsoluteDate date) { return getDailyFluxOnDay(date.shiftedBy(-Constants.JULIAN_DAY)); } - /** - * Gets the daily flux on the current day. - * - * @param date the current date - * @return the daily F10.7 flux (observed) - */ - private double getDailyFluxOnDay(final AbsoluteDate date) { - if (date.compareTo(lastDailyPredictedDate) <= 0) { - // Getting the value for the previous day - bracketDate(date); - return previousParam.getF107Obs(); - } else { - // Only monthly data is available, better interpolate between two months - // get the neighboring dates - bracketDate(date); - return getLinearInterpolation(date, previousParam.getF107Obs(), nextParam.getF107Obs()); - } - } - /** {@inheritDoc} */ public double getAverageFlux(final AbsoluteDate date) { + // Get the neighboring solar activity + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + if (date.compareTo(lastDailyPredictedDate) <= 0) { - bracketDate(date); - return previousParam.getCtr81Obs(); + return localSolarActivity.getPreviousParam().getCtr81Obs(); } else { // Only monthly data is available, better interpolate between two months - // get the neighboring dates - bracketDate(date); - return getLinearInterpolation(date, previousParam.getCtr81Obs(), nextParam.getCtr81Obs()); + return getLinearInterpolation(localSolarActivity, LineParameters::getCtr81Obs); } } @@ -297,43 +301,61 @@ public double[] getAp(final AbsoluteDate date) { return apArray; } + /** + * Gets the daily flux on the current day. + * + * @param date the current date + * + * @return the daily F10.7 flux (observed) + */ + private double getDailyFluxOnDay(final AbsoluteDate date) { + // Get the neighboring solar activity + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + + if (date.compareTo(lastDailyPredictedDate) <= 0) { + // Getting the value for the previous day + return localSolarActivity.getPreviousParam().getF107Obs(); + } else { + // Only monthly data is available, better interpolate between two months + return getLinearInterpolation(localSolarActivity, LineParameters::getF107Obs); + } + } + /** * Gets the value of the three-hourly Ap index for the given date. * * @param date the current date + * * @return the current three-hourly Ap index */ private double getThreeHourlyAp(final AbsoluteDate date) { if (date.compareTo(lastObservedDate.shiftedBy(Constants.JULIAN_DAY)) < 0) { - /** - * If observation data is available, it contains three-hourly data. - */ - bracketDate(date); - final double hourOfDay = date.offsetFrom(previousParam.getDate(), utc) / 3600; - int i_ap = (int) (hourOfDay / 3); + // If observation data is available, it contains three-hourly data. + // Get the neighboring solar activity + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + + final LineParameters previousParam = localSolarActivity.getPreviousParam(); + final double hourOfDay = date.offsetFrom(previousParam.getDate(), getUTC()) / 3600; + int i_ap = (int) (hourOfDay / 3); if (i_ap >= 8) { - /** - * hourOfDay can take the value 24.0 at midnight due to floating point precision + /* hourOfDay can take the value 24.0 at midnight due to floating point precision * when bracketing the dates or during a leap second because the hour of day is - * computed in UTC view - */ + * computed in UTC view */ i_ap = 7; } return previousParam.getThreeHourlyAp(i_ap); } else { - /** - * Only predictions are available, there are no three-hourly data - */ + /* Only predictions are available, there are no three-hourly data */ return getDailyAp(date); } } /** - * Gets the running average of the 8 three-hourly Ap indices prior to current - * time If three-hourly data is available, the result is different than - * getDailyAp. + * Gets the running average of the 8 three-hourly Ap indices prior to current time If three-hourly data is available, the + * result is different than getDailyAp. * * @param date the current date + * * @return the 24 hours running average of the Ap index */ private double get24HoursAverageAp(final AbsoluteDate date) { @@ -345,10 +367,8 @@ private double get24HoursAverageAp(final AbsoluteDate date) { } return apSum / 8; } else { - /** - * Only monthly predictions are available, no need to compute the average from - * three hourly data - */ + /* Only monthly predictions are available, no need to compute the average from + * three hourly data */ return getDailyAp(date); } } @@ -357,22 +377,20 @@ private double get24HoursAverageAp(final AbsoluteDate date) { * Get the daily Ap index for the given date. * * @param date the current date + * * @return the daily Ap index */ private double getDailyAp(final AbsoluteDate date) { + // Get the neighboring solar activity + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + if (date.compareTo(lastDailyPredictedDate) <= 0) { // Daily data is available, just taking the daily average - bracketDate(date); - return previousParam.getApAvg(); + return localSolarActivity.getPreviousParam().getApAvg(); } else { // Only monthly data is available, better interpolate between two months - // get the neighboring dates - bracketDate(date); - return getLinearInterpolation(date, previousParam.getApAvg(), nextParam.getApAvg()); + return getLinearInterpolation(localSolarActivity, LineParameters::getApAvg); } } - public String getSupportedNames() { - return super.getSupportedNames(); - } } diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherDataLoader.java b/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherDataLoader.java index 7138a9f62f..898f1ee771 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherDataLoader.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/CssiSpaceWeatherDataLoader.java @@ -17,151 +17,196 @@ package org.orekit.models.earth.atmosphere.data; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.ChronologicalComparator; +import org.orekit.time.TimeScale; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.text.ParseException; +import java.util.Arrays; +import java.util.HashSet; import java.util.NoSuchElementException; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import org.hipparchus.exception.Localizable; -import org.orekit.data.DataLoader; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.ChronologicalComparator; -import org.orekit.time.TimeScale; -import org.orekit.time.TimeStamped; - /** - * This class reads solar activity data from CSSI Space Weather files for the - * class {@link CssiSpaceWeatherData}. + * This class reads solar activity data from CSSI Space Weather files for the class {@link CssiSpaceWeatherData}. *

        - * The data are retrieved through space weather files offered by CSSI/AGI. The - * data can be retrieved on the AGI + * The data are retrieved through space weather files offered by CSSI/AGI. The data can be retrieved on the AGI * - * FTP. This file is updated several times a day by using several sources - * mentioned in the - * Celestrak space weather data documentation. + * FTP. This file is updated several times a day by using several sources mentioned in the Celestrak space weather data documentation. *

        * * @author Clément Jonglez * @since 10.2 */ -public class CssiSpaceWeatherDataLoader implements DataLoader { +public class CssiSpaceWeatherDataLoader extends AbstractSolarActivityDataLoader { + + /** Date of last data before the prediction starts. */ + private AbsoluteDate lastObservedDate; - /** Helper class to parse line data and to raise exceptions if needed. - * @deprecated as of 11.2, replaced by {@link CommonLineReader} to remove duplicated code. + /** Date of last daily prediction before the monthly prediction starts. */ + private AbsoluteDate lastDailyPredictedDate; + + /** Data set. */ + private final SortedSet set; + + /** + * Constructor. + * + * @param utc UTC time scale */ - @Deprecated - public static class LineReader { + public CssiSpaceWeatherDataLoader(final TimeScale utc) { + super(utc); + this.lastDailyPredictedDate = null; + this.lastObservedDate = null; + this.set = new TreeSet<>(new ChronologicalComparator()); + } + + /** + * Checks if the string contains a floating point number. + * + * @param strNum string to check + * + * @return true if string contains a valid floating point number, else false + */ + private static boolean isNumeric(final String strNum) { + if (strNum == null) { + return false; + } + try { + Double.parseDouble(strNum); + } + catch (NumberFormatException nfe) { + return false; + } + return true; + } - /** Name of the file. Used in error messages. */ - private final String name; + /** {@inheritDoc} */ + public void loadData(final InputStream input, final String name) throws IOException, ParseException, OrekitException { - /** The input stream. */ - private final BufferedReader in; + int lineNumber = 0; + String line = null; + final Set parsedEpochs = new HashSet<>(); - /** The last line read from the file. */ - private String line; + try (BufferedReader br = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { - /** The number of the last line read from the file. */ - private long lineNo; + final CommonLineReader reader = new CommonLineReader(br); - /** - * Create a line reader. - * - * @param name of the data source for error messages. - * @param in the input data stream. - */ - public LineReader(final String name, final BufferedReader in) { - this.name = name; - this.in = in; - this.line = null; - this.lineNo = 0; - } + for (line = reader.readLine(); line != null; line = reader.readLine()) { + lineNumber++; - /** - * Read a line from the input data stream. - * - * @return the next line without the line termination character, or {@code null} - * if the end of the stream has been reached. - * @throws IOException if an I/O error occurs. - * @see BufferedReader#readLine() - */ - public String readLine() throws IOException { - line = in.readLine(); - lineNo++; - return line; - } + line = line.trim(); + if (!line.isEmpty()) { - /** - * Read a line from the input data stream, or if the end of the stream has been - * reached throw an exception. - * - * @param message for the exception if the end of the stream is reached. - * @param args for the exception if the end of stream is reached. - * @return the next line without the line termination character, or {@code null} - * if the end of the stream has been reached. - * @throws IOException if an I/O error occurs. - * @throws OrekitException if a line could not be read because the end of the - * stream has been reached. - * @see #readLine() - */ - public String readLineOrThrow(final Localizable message, final Object... args) - throws IOException, OrekitException { + if (line.equals("BEGIN DAILY_PREDICTED")) { + lastObservedDate = set.last().getDate(); + } + + if (line.equals("BEGIN MONTHLY_FIT")) { + lastDailyPredictedDate = set.last().getDate(); + } + + if (line.length() == 130 && isNumeric(line.substring(0, 4))) { + // extract the data from the line + final int year = Integer.parseInt(line.substring(0, 4)); + final int month = Integer.parseInt(line.substring(5, 7)); + final int day = Integer.parseInt(line.substring(8, 10)); + final AbsoluteDate date = new AbsoluteDate(year, month, day, getUTC()); + + if (parsedEpochs.add(date)) { // Checking if entry doesn't exist yet + final double[] threeHourlyKp = new double[8]; + /* Kp is written as an integer where a unit equals 0.1, the conversion is + * Kp_double = 0.1 * double(Kp_integer) */ + for (int i = 0; i < 8; i++) { + threeHourlyKp[i] = 0.1 * Double.parseDouble(line.substring(19 + 3 * i, 21 + 3 * i)); + } + final double kpSum = 0.1 * Double.parseDouble(line.substring(43, 46)); + + final double[] threeHourlyAp = new double[8]; + for (int i = 0; i < 8; i++) { + threeHourlyAp[i] = Double.parseDouble(line.substring(47 + 4 * i, 50 + 4 * i)); + } + final double apAvg = Double.parseDouble(line.substring(79, 82)); + + final double f107Adj = Double.parseDouble(line.substring(93, 98)); - final String text = readLine(); - if (text == null) { - throw new OrekitException(message, args); + final int fluxQualifier = Integer.parseInt(line.substring(99, 100)); + + final double ctr81Adj = Double.parseDouble(line.substring(101, 106)); + + final double lst81Adj = Double.parseDouble(line.substring(107, 112)); + + final double f107Obs = Double.parseDouble(line.substring(113, 118)); + + final double ctr81Obs = Double.parseDouble(line.substring(119, 124)); + + final double lst81Obs = Double.parseDouble(line.substring(125, 130)); + + set.add(new LineParameters(date, threeHourlyKp, kpSum, threeHourlyAp, apAvg, f107Adj, + fluxQualifier, ctr81Adj, lst81Adj, f107Obs, ctr81Obs, lst81Obs)); + } + } + } } - return text; } - - /** - * Annotate an exception with the file context. - * - * @param cause the reason why the line could not be parsed. - * @return an exception with the cause, file name, line number, and line text. - */ - public OrekitException unableToParseLine(final Throwable cause) { - return new OrekitException(cause, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNo, name, line); + catch (NumberFormatException nfe) { + throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, name, line); } - /** - * Get the last line read from the stream. - * - * @return May be {@code null} if no lines have been read or the end of stream - * has been reached. - */ - public String getLine() { - return line; + try { + setMinDate(set.first().getDate()); + setMaxDate(set.last().getDate()); } - - /** - * Get the line number of the last line read from the file. - * - * @return the line number. - */ - public long getLineNumber() { - return lineNo; + catch (NoSuchElementException nse) { + throw new OrekitException(nse, OrekitMessages.NO_DATA_IN_FILE, name); } } + /** + * Getter for the data set. + * + * @return the data set + */ + @Override + public SortedSet getDataSet() { + return set; + } + + /** + * Gets the day (at data start) of the last daily data entry. + * + * @return the last daily predicted date + */ + public AbsoluteDate getLastDailyPredictedDate() { + return lastDailyPredictedDate; + } + + /** + * Gets the day (at data start) of the last observed data entry. + * + * @return the last observed date + */ + public AbsoluteDate getLastObservedDate() { + return lastObservedDate; + } + /** Container class for Solar activity indexes. */ - public static class LineParameters implements TimeStamped, Serializable { + public static class LineParameters extends AbstractSolarActivityDataLoader.LineParameters { /** Serializable UID. */ private static final long serialVersionUID = 8151260459653484163L; - /** Entry date. */ - private final AbsoluteDate date; - /** Array of 8 three-hourly Kp indices for this entry. */ private final double[] threeHourlyKp; @@ -199,6 +244,7 @@ public static class LineParameters implements TimeStamped, Serializable { /** * Constructor. + * * @param date entry date * @param threeHourlyKp array of 8 three-hourly Kp indices for this entry * @param kpSum sum of the 8 Kp indices for the day expressed to the nearest third of a unit @@ -213,47 +259,135 @@ public static class LineParameters implements TimeStamped, Serializable { * @param lst81Obs last 81-day arithmetic average of F10.7 (observed) */ public LineParameters(final AbsoluteDate date, final double[] threeHourlyKp, final double kpSum, - final double[] threeHourlyAp, final double apAvg, final double f107Adj, final int fluxQualifier, - final double ctr81Adj, final double lst81Adj, final double f107Obs, final double ctr81Obs, - final double lst81Obs) { - this.date = date; + final double[] threeHourlyAp, final double apAvg, final double f107Adj, + final int fluxQualifier, final double ctr81Adj, final double lst81Adj, + final double f107Obs, final double ctr81Obs, final double lst81Obs) { + super(date); this.threeHourlyKp = threeHourlyKp.clone(); - this.kpSum = kpSum; + this.kpSum = kpSum; this.threeHourlyAp = threeHourlyAp.clone(); - this.apAvg = apAvg; - this.f107Adj = f107Adj; + this.apAvg = apAvg; + this.f107Adj = f107Adj; this.fluxQualifier = fluxQualifier; - this.ctr81Adj = ctr81Adj; - this.lst81Adj = lst81Adj; - this.f107Obs = f107Obs; - this.ctr81Obs = ctr81Obs; - this.lst81Obs = lst81Obs; + this.ctr81Adj = ctr81Adj; + this.lst81Adj = lst81Adj; + this.f107Obs = f107Obs; + this.ctr81Obs = ctr81Obs; + this.lst81Obs = lst81Obs; } + /** {@inheritDoc} */ @Override - public AbsoluteDate getDate() { - return date; + public int compareTo(final AbstractSolarActivityDataLoader.LineParameters lineParameters) { + return getDate().compareTo(lineParameters.getDate()); } - /** - * Gets the array of the eight three-hourly Kp indices for the current entry. - * @return the array of eight three-hourly Kp indices - */ - public double[] getThreeHourlyKp() { - return threeHourlyKp.clone(); + /** {@inheritDoc} */ + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final LineParameters that = (LineParameters) o; + + if (Double.compare(getKpSum(), that.getKpSum()) != 0) { + return false; + } + if (Double.compare(getApAvg(), that.getApAvg()) != 0) { + return false; + } + if (Double.compare(getF107Adj(), that.getF107Adj()) != 0) { + return false; + } + if (getFluxQualifier() != that.getFluxQualifier()) { + return false; + } + if (Double.compare(getCtr81Adj(), that.getCtr81Adj()) != 0) { + return false; + } + if (Double.compare(getLst81Adj(), that.getLst81Adj()) != 0) { + return false; + } + if (Double.compare(getF107Obs(), that.getF107Obs()) != 0) { + return false; + } + if (Double.compare(getCtr81Obs(), that.getCtr81Obs()) != 0) { + return false; + } + if (Double.compare(getLst81Obs(), that.getLst81Obs()) != 0) { + return false; + } + if (!Arrays.equals(getThreeHourlyKp(), that.getThreeHourlyKp())) { + return false; + } + return Arrays.equals(getThreeHourlyAp(), that.getThreeHourlyAp()); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + int result; + long temp; + result = Arrays.hashCode(getThreeHourlyKp()); + temp = Double.doubleToLongBits(getKpSum()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + Arrays.hashCode(getThreeHourlyAp()); + temp = Double.doubleToLongBits(getApAvg()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getF107Adj()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + getFluxQualifier(); + temp = Double.doubleToLongBits(getCtr81Adj()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getLst81Adj()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getF107Obs()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getCtr81Obs()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getLst81Obs()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; } /** * Gets the three-hourly Kp index at index i from the threeHourlyKp array. + * * @param i index of the Kp index to retrieve [0-7] + * * @return the three hourly Kp index at index i */ public double getThreeHourlyKp(final int i) { return threeHourlyKp[i]; } + /** + * Gets the three-hourly Ap index at index i from the threeHourlyAp array. + * + * @param i index of the Ap to retrieve [0-7] + * + * @return the three hourly Ap index at index i + */ + public double getThreeHourlyAp(final int i) { + return threeHourlyAp[i]; + } + + /** + * Gets the array of the eight three-hourly Kp indices for the current entry. + * + * @return the array of eight three-hourly Kp indices + */ + public double[] getThreeHourlyKp() { + return threeHourlyKp.clone(); + } + /** * Gets the sum of all eight Kp indices for the current entry. + * * @return the sum of all eight Kp indices */ public double getKpSum() { @@ -262,23 +396,16 @@ public double getKpSum() { /** * Gets the array of the eight three-hourly Ap indices for the current entry. + * * @return the array of eight three-hourly Ap indices */ public double[] getThreeHourlyAp() { return threeHourlyAp.clone(); } - /** - * Gets the three-hourly Ap index at index i from the threeHourlyAp array. - * @param i index of the Ap to retrieve [0-7] - * @return the three hourly Ap index at index i - */ - public double getThreeHourlyAp(final int i) { - return threeHourlyAp[i]; - } - /** * Gets the arithmetic average of all eight Ap indices for the current entry. + * * @return the average of all eight Ap indices */ public double getApAvg() { @@ -287,6 +414,7 @@ public double getApAvg() { /** * Gets the last 81-day arithmetic average of F10.7 (observed). + * * @return the last 81-day arithmetic average of F10.7 (observed) */ public double getLst81Obs() { @@ -295,6 +423,7 @@ public double getLst81Obs() { /** * Gets the centered 81-day arithmetic average of F10.7 (observed). + * * @return the centered 81-day arithmetic average of F10.7 (observed) */ public double getCtr81Obs() { @@ -303,6 +432,7 @@ public double getCtr81Obs() { /** * Gets the observed (unadjusted) value of F10.7. + * * @return the observed (unadjusted) value of F10.7 */ public double getF107Obs() { @@ -311,6 +441,7 @@ public double getF107Obs() { /** * Gets the last 81-day arithmetic average of F10.7 (adjusted). + * * @return the last 81-day arithmetic average of F10.7 (adjusted) */ public double getLst81Adj() { @@ -319,6 +450,7 @@ public double getLst81Adj() { /** * Gets the centered 81-day arithmetic average of F10.7 (adjusted). + * * @return the centered 81-day arithmetic average of F10.7 (adjusted) */ public double getCtr81Adj() { @@ -327,6 +459,7 @@ public double getCtr81Adj() { /** * Gets the Flux Qualifier. + * * @return the Flux Qualifier */ public int getFluxQualifier() { @@ -335,6 +468,7 @@ public int getFluxQualifier() { /** * Gets the 10.7-cm Solar Radio Flux (F10.7) Adjusted to 1 AU. + * * @return the 10.7-cm Solar Radio Flux (F10.7) Adjusted to 1 AU */ public double getF107Adj() { @@ -342,180 +476,4 @@ public double getF107Adj() { } } - /** UTC time scale. */ - private final TimeScale utc; - - /** First available date. */ - private AbsoluteDate firstDate; - - /** Date of last data before the prediction starts. */ - private AbsoluteDate lastObservedDate; - - /** Date of last daily prediction before the monthly prediction starts. */ - private AbsoluteDate lastDailyPredictedDate; - - /** Last available date. */ - private AbsoluteDate lastDate; - - /** Data set. */ - private SortedSet set; - - /** - * Constructor. - * @param utc UTC time scale - */ - public CssiSpaceWeatherDataLoader(final TimeScale utc) { - this.utc = utc; - firstDate = null; - lastDailyPredictedDate = null; - lastDate = null; - lastObservedDate = null; - set = new TreeSet<>(new ChronologicalComparator()); - } - - /** - * Getter for the data set. - * @return the data set - */ - public SortedSet getDataSet() { - return set; - } - - /** - * Gets the available data range minimum date. - * @return the minimum date. - */ - public AbsoluteDate getMinDate() { - return firstDate; - } - - /** - * Gets the available data range maximum date. - * @return the maximum date. - */ - public AbsoluteDate getMaxDate() { - return lastDate; - } - - /** - * Gets the day (at data start) of the last daily data entry. - * @return the last daily predicted date - */ - public AbsoluteDate getLastDailyPredictedDate() { - return lastDailyPredictedDate; - } - - /** - * Gets the day (at data start) of the last observed data entry. - * @return the last observed date - */ - public AbsoluteDate getLastObservedDate() { - return lastObservedDate; - } - - /** - * Checks if the string contains a floating point number. - * - * @param strNum string to check - * @return true if string contains a valid floating point number, else false - */ - private static boolean isNumeric(final String strNum) { - if (strNum == null) { - return false; - } - try { - Double.parseDouble(strNum); - } catch (NumberFormatException nfe) { - return false; - } - return true; - } - - /** {@inheritDoc} */ - public void loadData(final InputStream input, final String name) - throws IOException, ParseException, OrekitException { - - // read the data - int lineNumber = 0; - String line = null; - - try (BufferedReader br = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { - - final CommonLineReader reader = new CommonLineReader(br); - - for (line = reader.readLine(); line != null; line = reader.readLine()) { - lineNumber++; - - line = line.trim(); - if (line.length() > 0) { - - if (line.equals("BEGIN DAILY_PREDICTED")) { - lastObservedDate = set.last().getDate(); - } - - if (line.equals("BEGIN MONTHLY_FIT")) { - lastDailyPredictedDate = set.last().getDate(); - } - - if (line.length() == 130 && isNumeric(line.substring(0, 4))) { - // extract the data from the line - final int year = Integer.parseInt(line.substring(0, 4)); - final int month = Integer.parseInt(line.substring(5, 7)); - final int day = Integer.parseInt(line.substring(8, 10)); - final AbsoluteDate date = new AbsoluteDate(year, month, day, this.utc); - - if (!set.contains(date)) { // Checking if entry doesn't exist yet - final double[] threeHourlyKp = new double[8]; - /** - * Kp is written as an integer where a unit equals 0.1, the conversion is - * Kp_double = 0.1 * double(Kp_integer) - */ - for (int i = 0; i < 8; i++) { - threeHourlyKp[i] = 0.1 * Double.parseDouble(line.substring(19 + 3 * i, 21 + 3 * i)); - } - final double kpSum = 0.1 * Double.parseDouble(line.substring(43, 46)); - - final double[] threeHourlyAp = new double[8]; - for (int i = 0; i < 8; i++) { - threeHourlyAp[i] = Double.parseDouble(line.substring(47 + 4 * i, 50 + 4 * i)); - } - final double apAvg = Double.parseDouble(line.substring(79, 82)); - - final double f107Adj = Double.parseDouble(line.substring(93, 98)); - - final int fluxQualifier = Integer.parseInt(line.substring(99, 100)); - - final double ctr81Adj = Double.parseDouble(line.substring(101, 106)); - - final double lst81Adj = Double.parseDouble(line.substring(107, 112)); - - final double f107Obs = Double.parseDouble(line.substring(113, 118)); - - final double ctr81Obs = Double.parseDouble(line.substring(119, 124)); - - final double lst81Obs = Double.parseDouble(line.substring(125, 130)); - - set.add(new LineParameters(date, threeHourlyKp, kpSum, threeHourlyAp, apAvg, f107Adj, - fluxQualifier, ctr81Adj, lst81Adj, f107Obs, ctr81Obs, lst81Obs)); - } - } - } - } - } catch (NumberFormatException nfe) { - throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, name, line); - } - - try { - firstDate = set.first().getDate(); - lastDate = set.last().getDate(); - } catch (NoSuchElementException nse) { - throw new OrekitException(nse, OrekitMessages.NO_DATA_IN_FILE, name); - } - - } - - /** {@inheritDoc} */ - public boolean stillAcceptsData() { - return true; - } } diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/DtcDataLoader.java b/src/main/java/org/orekit/models/earth/atmosphere/data/DtcDataLoader.java index c1fff4c1c9..0feb658956 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/DtcDataLoader.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/DtcDataLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/JB2008SpaceEnvironmentData.java b/src/main/java/org/orekit/models/earth/atmosphere/data/JB2008SpaceEnvironmentData.java index 6b172f42f3..4d3283e977 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/JB2008SpaceEnvironmentData.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/JB2008SpaceEnvironmentData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,12 +17,18 @@ package org.orekit.models.earth.atmosphere.data; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; import java.util.List; import java.util.stream.Collectors; +import org.hipparchus.exception.DummyLocalizable; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.models.earth.atmosphere.JB2008InputParameters; @@ -137,6 +143,63 @@ public JB2008SpaceEnvironmentData(final String supportedNamesSOL, } + /** + * Simple constructor. This constructor uses the {@link DataContext#getDefault() + * default data context}. + * @param sourceSolfsmy source for the SOLFSMY data + * @param sourceDtc source for the DTC data + * @since 12.0 + */ + @DefaultDataContext + public JB2008SpaceEnvironmentData(final DataSource sourceSolfsmy, + final DataSource sourceDtc) { + this(sourceSolfsmy, sourceDtc, DataContext.getDefault().getTimeScales().getUTC()); + } + + /** + * Constructor that allows specifying the source of the SOLFSMY space weather + * file. + * This constructor takes a supplementary argument, the source for DTCFILE, + * in order to setup the second loader. + * + * @param sourceSolfsmy source for the SOLFSMY data + * @param sourceDtc source for the DTC data + * @param utc UTC time scale + * @since 12.0 + */ + public JB2008SpaceEnvironmentData(final DataSource sourceSolfsmy, + final DataSource sourceDtc, + final TimeScale utc) { + try { + + // Load SOLFSMY data + final SOLFSMYDataLoader loaderSOL = new SOLFSMYDataLoader(utc); + try (InputStream is = sourceSolfsmy.getOpener().openStreamOnce(); + BufferedInputStream bis = new BufferedInputStream(is)) { + loaderSOL.loadData(bis, sourceSolfsmy.getName()); + } + + // Load DTC data + final DtcDataLoader loaderDTC = new DtcDataLoader(utc); + try (InputStream is = sourceDtc.getOpener().openStreamOnce(); + BufferedInputStream bis = new BufferedInputStream(is)) { + loaderDTC.loadData(bis, sourceDtc.getName()); + } + + // Initialise fields + dataSOL = new ImmutableTimeStampedCache<>(N_NEIGHBORS, loaderSOL.getDataSet()); + dataDTC = new ImmutableTimeStampedCache<>(N_NEIGHBORS, loaderDTC.getDataSet()); + // Because the two files are generated by the same organism, + // the first and last epochs are identical between the two files + firstDate = loaderSOL.getMinDate(); + lastDate = loaderSOL.getMaxDate(); + + } catch (IOException | ParseException ioe) { + throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage())); + } + + } + /** {@inheritDoc} */ public AbsoluteDate getMinDate() { return firstDate; diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimation.java b/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimation.java index 6c0f4974b3..f1e9b48da7 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimation.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,78 +16,59 @@ */ package org.orekit.models.earth.atmosphere.data; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import java.text.ParseException; -import java.util.Iterator; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.hipparchus.analysis.UnivariateFunction; +import org.hipparchus.analysis.interpolation.LinearInterpolator; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; -import org.orekit.data.AbstractSelfFeedingLoader; import org.orekit.data.DataContext; -import org.orekit.data.DataLoader; import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; -import org.orekit.errors.OrekitMessages; -import org.orekit.models.earth.atmosphere.DTM2000InputParameters; -import org.orekit.models.earth.atmosphere.NRLMSISE00InputParameters; +import org.orekit.models.earth.atmosphere.data.MarshallSolarActivityFutureEstimationLoader.LineParameters; import org.orekit.time.AbsoluteDate; -import org.orekit.time.ChronologicalComparator; import org.orekit.time.DateComponents; -import org.orekit.time.Month; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; -import org.orekit.time.TimeStamped; +import org.orekit.time.TimeStampedDouble; import org.orekit.utils.Constants; +import org.orekit.utils.GenericTimeStampedCache; +import org.orekit.utils.OrekitConfiguration; +import org.orekit.utils.TimeStampedGenerator; /** - * This class reads and provides solar activity data needed by - * atmospheric models: F107 solar flux, Ap and Kp indexes. + * This class provides solar activity data needed by atmospheric models: F107 solar flux, Ap and Kp indexes. *

        - * The data are retrieved through the NASA Marshall - * Solar Activity Future Estimation (MSAFE) as estimates of monthly - * F10.7 Mean solar flux and Ap geomagnetic parameter. - * The data can be retrieved at the NASA - * Marshall Solar Activity website. - * Here Kp indices are deduced from Ap indexes, which in turn are tabulated - * equivalent of retrieved Ap values. - *

        - *

        - * If several MSAFE files are available, some dates may appear in several - * files (for example August 2007 is in all files from the first one - * published in March 1999 to the February 2008 file). In this case, the - * data from the most recent file is used and the older ones are discarded. - * The date of the file is assumed to be 6 months after its first entry - * (which explains why the file having August 2007 as its first entry is the - * February 2008 file). This implies that MSAFE files must not be - * edited to change their time span, otherwise this would break the old - * entries overriding mechanism. - *

        - *

        - * With these data, the {@link #getInstantFlux(AbsoluteDate)} and {@link - * #getMeanFlux(AbsoluteDate)} methods return the same values and the {@link - * #get24HoursKp(AbsoluteDate)} and {@link #getThreeHourlyKP(AbsoluteDate)} - * methods return the same values. - *

        - *

        - * Conversion from Ap index values in the MSAFE file to Kp values used by atmosphere - * models is done using Jacchia's equation in [1]. - *

        - *

        - * With these data, the {@link #getAp(AbsoluteDate date)} method returns an array - * of seven times the same daily Ap value, i.e. it is suited to be used only with - * the {@link org.orekit.models.earth.atmosphere.NRLMSISE00 NRLMSISE00} atmospheric + * Data comes from the NASA Marshall Solar Activity Future Estimation (MSAFE) as estimates of monthly F10.7 + * Mean solar flux and Ap geomagnetic parameter + * (see Marshall Solar Activity website). + * + *

        Data can be retrieved at the NASA + * Marshall Solar Activity archived forecast. + * Here Kp indices are deduced from Ap indexes, which in turn are tabulated equivalent of retrieved Ap values. + * + *

        If several MSAFE files are available, some dates may appear in several files (for example August 2007 is in all files from + * the first one published in March 1999 to the February 2008 file). In this case, the data from the most recent file is used + * and the older ones are discarded. The date of the file is assumed to be 6 months after its first entry (which explains why + * the file having August 2007 as its first entry is the February 2008 file). This implies that MSAFE files must not + * be edited to change their time span, otherwise this would break the old entries overriding mechanism. + * + *

        With these data, the {@link #getInstantFlux(AbsoluteDate)} and {@link #getMeanFlux(AbsoluteDate)} methods return the same + * values and the {@link #get24HoursKp(AbsoluteDate)} and {@link #getThreeHourlyKP(AbsoluteDate)} methods return the same + * values. + * + *

        Conversion from Ap index values in the MSAFE file to Kp values used by atmosphere models is done using Jacchia's equation + * in [1]. + * + *

        With these data, the {@link #getAp(AbsoluteDate date)} method returns an array of seven times the same daily Ap value, + * i.e. it is suited to be used only with the {@link org.orekit.models.earth.atmosphere.NRLMSISE00 NRLMSISE00} atmospheric * model where the switch #9 is set to 1. - *

        * *

        References

        * @@ -98,194 +79,190 @@ * @author Luc Maisonobe * @author Evan Ward * @author Pascal Parraud + * @author Vincent Cucchietti */ -public class MarshallSolarActivityFutureEstimation extends AbstractSelfFeedingLoader - implements DataLoader, DTM2000InputParameters, NRLMSISE00InputParameters { +public class MarshallSolarActivityFutureEstimation + extends AbstractSolarActivityData { - /** Default regular expression for the supported name that work with all officially published files. + /** + * Default regular expression for the supported name that work with all officially published files. + * * @since 10.0 */ public static final String DEFAULT_SUPPORTED_NAMES = - "\\p{Alpha}\\p{Lower}\\p{Lower}\\p{Digit}\\p{Digit}\\p{Digit}\\p{Digit}(?:f|F)10(?:_prd)?\\.(?:txt|TXT)"; - - /** Strength level of activity. */ - public enum StrengthLevel { - - /** Strong level of activity. */ - STRONG, - - /** Average level of activity. */ - AVERAGE, - - /** Weak level of activity. */ - WEAK - - } + "\\p{Alpha}\\p{Lower}\\p{Lower}\\p{Digit}\\p{Digit}\\p{Digit}\\p{Digit}(?:f|F)10(?:[-_]prd)?\\.(?:txt|TXT)"; /** Serializable UID. */ private static final long serialVersionUID = -5212198874900835369L; - /** Pattern for the data fields of MSAFE data. */ - private final Pattern dataPattern; - - /** Data set. */ - private final SortedSet data; - /** Selected strength level of activity. */ private final StrengthLevel strengthLevel; - /** UTC time scale. */ - private final TimeScale utc; - - /** First available date. */ - private AbsoluteDate firstDate; - - /** Last available date. */ - private AbsoluteDate lastDate; - - /** Previous set of solar activity parameters. */ - private LineParameters previousParam; + /** Cache dedicated to average flux. */ + private final transient GenericTimeStampedCache averageFluxCache; - /** Current set of solar activity parameters. */ - private LineParameters currentParam; - - /** Simple constructor. This constructor uses the {@link DataContext#getDefault() - * default data context}. + /** + * Simple constructor. This constructor uses the {@link DataContext#getDefault() default data context}. *

        - * The original file names used by NASA Marshall space center are of the - * form: may2019f10_prd.txt or Oct1999F10.TXT. So a recommended regular - * expression for the supported name that work with all published files is: + * The original file names used by NASA Marshall space center are of the form: may2019f10_prd.txt or Oct1999F10.TXT. So a + * recommended regular expression for the supported name that work with all published files is: * {@link #DEFAULT_SUPPORTED_NAMES}. - *

        + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code Constants.JULIAN_YEAR}
        • + *
        • Max span interval : {@code 0}
        • + *
        + * * @param supportedNames regular expression for supported files names * @param strengthLevel selected strength level of activity + * * @see #MarshallSolarActivityFutureEstimation(String, StrengthLevel, DataProvidersManager, TimeScale) */ @DefaultDataContext public MarshallSolarActivityFutureEstimation(final String supportedNames, final StrengthLevel strengthLevel) { this(supportedNames, strengthLevel, - DataContext.getDefault().getDataProvidersManager(), - DataContext.getDefault().getTimeScales().getUTC()); + DataContext.getDefault().getDataProvidersManager(), + DataContext.getDefault().getTimeScales().getUTC()); } /** * Constructor that allows specifying the source of the MSAFE auxiliary data files. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code 31 * Constants.JULIAN_DAY}
        • + *
        • Max interval : {@code 0}
        • + *
        • Minimum step: {@code 27 * Constants.JULIAN_DAY}
        • + *
        * * @param supportedNames regular expression for supported files names * @param strengthLevel selected strength level of activity * @param dataProvidersManager provides access to auxiliary data files. * @param utc UTC time scale. + * * @since 10.1 */ - public MarshallSolarActivityFutureEstimation( - final String supportedNames, - final StrengthLevel strengthLevel, - final DataProvidersManager dataProvidersManager, - final TimeScale utc) { - super(supportedNames, dataProvidersManager); - - firstDate = null; - lastDate = null; - data = new TreeSet<>(new ChronologicalComparator()); - this.strengthLevel = strengthLevel; - this.utc = utc; - - // the data lines have the following form: - // 2010.5003 JUL 83.4 81.3 78.7 6.4 5.9 5.2 - // 2010.5837 AUG 87.3 83.4 78.5 7.0 6.1 4.9 - // 2010.6670 SEP 90.8 85.5 79.4 7.8 6.2 4.7 - // 2010.7503 OCT 94.2 87.6 80.4 9.1 6.4 4.9 - final StringBuilder builder = new StringBuilder("^"); - - // first group: year - builder.append("\\p{Blank}*(\\p{Digit}\\p{Digit}\\p{Digit}\\p{Digit})"); - - // month as fraction of year, not stored in a group - builder.append("\\.\\p{Digit}+"); - - // second group: month as a three upper case letters abbreviation - builder.append("\\p{Blank}+("); - for (final Month month : Month.values()) { - builder.append(month.getUpperCaseAbbreviation()); - builder.append('|'); - } - builder.delete(builder.length() - 1, builder.length()); - builder.append(")"); - - // third to eighth group: data fields - for (int i = 0; i < 6; ++i) { - builder.append("\\p{Blank}+([-+]?[0-9]+\\.[0-9]+)"); - } - - // end of line - builder.append("\\p{Blank}*$"); - - // compile the pattern - dataPattern = Pattern.compile(builder.toString()); - + public MarshallSolarActivityFutureEstimation(final String supportedNames, + final StrengthLevel strengthLevel, + final DataProvidersManager dataProvidersManager, + final TimeScale utc) { + this(supportedNames, strengthLevel, dataProvidersManager, utc, OrekitConfiguration.getCacheSlotsNumber(), + Constants.JULIAN_DAY * 31, 0, Constants.JULIAN_DAY * 27); } - /** Get the strength level for activity. - * @return strength level to set + /** + * Constructor that allows specifying the source of the MSAFE auxiliary data files and customizable thread safe cache + * configuration. + * + * @param supportedNames regular expression for supported files names + * @param strengthLevel selected strength level of activity + * @param dataProvidersManager provides access to auxiliary data files. + * @param utc UTC time scale. + * @param maxSlots maximum number of independent cached time slots in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache} + * @param maxInterval time interval above which a new slot is created in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param minimumStep overriding minimum step designed for non-homogeneous tabulated values. To be used for example when + * caching monthly tabulated values. May be null. + * + * @since 10.1 */ - public StrengthLevel getStrengthLevel() { - return strengthLevel; + public MarshallSolarActivityFutureEstimation(final String supportedNames, + final StrengthLevel strengthLevel, + final DataProvidersManager dataProvidersManager, + final TimeScale utc, + final int maxSlots, + final double maxSpan, + final double maxInterval, + final double minimumStep) { + super(supportedNames, new MarshallSolarActivityFutureEstimationLoader(strengthLevel, utc), + dataProvidersManager, utc, maxSlots, maxSpan, maxInterval, minimumStep); + + // Initialise fields + this.strengthLevel = strengthLevel; + this.averageFluxCache = new GenericTimeStampedCache<>(N_NEIGHBORS, OrekitConfiguration.getCacheSlotsNumber(), + Constants.JULIAN_DAY, 0, new AverageFluxGenerator()); } - /** Find the data bracketing a specified date. - * @param date date to bracket + /** + * Simple constructor which use the {@link DataContext#getDefault() default data context}. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code 31 * Constants.JULIAN_DAY}
        • + *
        • Max interval : {@code 0}
        • + *
        • Minimum step: {@code 27 * Constants.JULIAN_DAY}
        • + *
        + * + * @param source source for the data + * @param strengthLevel selected strength level of activity + * + * @since 12.0 */ - private void bracketDate(final AbsoluteDate date) { - - if (date.durationFrom(firstDate) < 0) { - throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, - date, firstDate, lastDate, firstDate.durationFrom(date)); - } - if (date.durationFrom(lastDate) > 0) { - throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER, - date, firstDate, lastDate, date.durationFrom(lastDate)); - } - - // don't search if the cached selection is fine - if (previousParam != null && - date.durationFrom(previousParam.getDate()) > 0 && - date.durationFrom(currentParam.getDate()) <= 0 ) { - return; - } - - if (date.equals(firstDate)) { - currentParam = (LineParameters) data.tailSet(date.shiftedBy(1)).first(); - previousParam = (LineParameters) data.first(); - } else if (date.equals(lastDate)) { - currentParam = (LineParameters) data.last(); - previousParam = (LineParameters) data.headSet(date.shiftedBy(-1)).last(); - } else { - currentParam = (LineParameters) data.tailSet(date).first(); - previousParam = (LineParameters) data.headSet(date).last(); - } - - } - - @Override - public String getSupportedNames() { - return super.getSupportedNames(); + @DefaultDataContext + public MarshallSolarActivityFutureEstimation(final DataSource source, + final StrengthLevel strengthLevel) { + this(source, strengthLevel, DataContext.getDefault().getTimeScales().getUTC()); } - /** {@inheritDoc} */ - public AbsoluteDate getMinDate() { - if (firstDate == null) { - feed(this); - } - return firstDate; + /** + * Simple constructor. + *

        + * It provides a default configuration for the thread safe cache : + *

          + *
        • Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}
        • + *
        • Max span : {@code 31 * Constants.JULIAN_DAY}
        • + *
        • Max interval : {@code 0}
        • + *
        • Minimum step: {@code 27 * Constants.JULIAN_DAY}
        • + *
        + * + * @param source source for the data + * @param strengthLevel selected strength level of activity + * @param utc UTC time scale + * + * @since 12.0 + */ + public MarshallSolarActivityFutureEstimation(final DataSource source, + final StrengthLevel strengthLevel, + final TimeScale utc) { + this(source, strengthLevel, utc, OrekitConfiguration.getCacheSlotsNumber(), + Constants.JULIAN_DAY * 31, 0, Constants.JULIAN_DAY * 27); } - /** {@inheritDoc} */ - public AbsoluteDate getMaxDate() { - if (lastDate == null) { - feed(this); - } - return lastDate; + /** + * Constructor with customizable thread safe cache configuration. + * + * @param source source for the data + * @param strengthLevel selected strength level of activity + * @param utc UTC time scale + * @param maxSlots maximum number of independent cached time slots in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache} + * @param maxInterval time interval above which a new slot is created in the + * {@link GenericTimeStampedCache time-stamped cache} + * @param minimumStep overriding minimum step designed for non-homogeneous tabulated values. To be used for example when + * caching monthly tabulated values. Use {@code Double.NaN} otherwise. + * + * @since 12.0 + */ + public MarshallSolarActivityFutureEstimation(final DataSource source, + final StrengthLevel strengthLevel, + final TimeScale utc, + final int maxSlots, + final double maxSpan, + final double maxInterval, + final double minimumStep) { + super(source, new MarshallSolarActivityFutureEstimationLoader(strengthLevel, utc), utc, + maxSlots, maxSpan, maxInterval, minimumStep); + this.strengthLevel = strengthLevel; + this.averageFluxCache = new GenericTimeStampedCache<>(N_NEIGHBORS, OrekitConfiguration.getCacheSlotsNumber(), + Constants.JULIAN_DAY, 0, new AverageFluxGenerator()); } /** {@inheritDoc} */ @@ -295,21 +272,7 @@ public double getInstantFlux(final AbsoluteDate date) { /** {@inheritDoc} */ public double getMeanFlux(final AbsoluteDate date) { - - // get the neighboring dates - bracketDate(date); - - // perform a linear interpolation - final AbsoluteDate previousDate = previousParam.getDate(); - final AbsoluteDate currentDate = currentParam.getDate(); - final double dt = currentDate.durationFrom(previousDate); - final double previousF107 = previousParam.getF107(); - final double currentF107 = currentParam.getF107(); - final double previousWeight = currentDate.durationFrom(date) / dt; - final double currentWeight = date.durationFrom(previousDate) / dt; - - return previousF107 * previousWeight + currentF107 * currentWeight; - + return getLinearInterpolation(date, LineParameters::getF107); } /** {@inheritDoc} */ @@ -317,35 +280,39 @@ public double getThreeHourlyKP(final AbsoluteDate date) { return get24HoursKp(date); } - /** Get the date of the file from which data at the specified date comes from. + /** + * Get the date of the file from which data at the specified date comes from. *

        - * If several MSAFE files are available, some dates may appear in several - * files (for example August 2007 is in all files from the first one - * published in March 1999 to the February 2008 file). In this case, the - * data from the most recent file is used and the older ones are discarded. - * The date of the file is assumed to be 6 months after its first entry - * (which explains why the file having August 2007 as its first entry is the - * February 2008 file). This implies that MSAFE files must not be - * edited to change their time span, otherwise this would break the old - * entries overriding mechanism. + * If several MSAFE files are available, some dates may appear in several files (for example August 2007 is in all files + * from the first one published in March 1999 to the February 2008 file). In this case, the data from the most recent + * file is used and the older ones are discarded. The date of the file is assumed to be 6 months after its first entry + * (which explains why the file having August 2007 as its first entry is the February 2008 file). This implies that MSAFE + * files must not be edited to change their time span, otherwise this would break the old entries overriding + * mechanism. *

        + * * @param date date of the solar activity data + * * @return date of the file */ public DateComponents getFileDate(final AbsoluteDate date) { - bracketDate(date); + // Get the neighboring solar activity + final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date); + final LineParameters previousParam = localSolarActivity.getPreviousParam(); + final LineParameters currentParam = localSolarActivity.getNextParam(); + + // Choose which file date to return final double dtP = date.durationFrom(previousParam.getDate()); final double dtC = currentParam.getDate().durationFrom(date); return (dtP < dtC) ? previousParam.getFileDate() : currentParam.getFileDate(); } - /** The Kp index is derived from the Ap index. + /** + * The Kp index is derived from the Ap index. *

        The method used is explained on - * NOAA website. as follows:

        + * href="http://www.ngdc.noaa.gov/stp/GEOMAG/kp_ap.html"> NOAA website. as follows:

        *

        The scale is 0 to 9 expressed in thirds of a unit, e.g. 5- is 4 2/3, - * 5 is 5 and 5+ is 5 1/3. The ap (equivalent range) index is derived from - * the Kp index as follows:

        + * 5 is 5 and 5+ is 5 1/3. The ap (equivalent range) index is derived from the Kp index as follows:

        * * * @@ -363,11 +330,12 @@ public DateComponents getFileDate(final AbsoluteDate date) { * * *
        Kp / Ap Conversion Table
        + * * @param date date of the Kp data + * * @return the 24H geomagnetic index */ public double get24HoursKp(final AbsoluteDate date) { - // get the daily Ap final double ap = getDailyAp(date); @@ -381,227 +349,179 @@ public double getDailyFlux(final AbsoluteDate date) { return getMeanFlux(date.shiftedBy(-Constants.JULIAN_DAY)); } - /** {@inheritDoc} */ public double getAverageFlux(final AbsoluteDate date) { + // Extract closest neighbours average + final List neighbors = averageFluxCache.getNeighbors(date).collect(Collectors.toList()); - // Initializes the average flux - double average = 0.; + // Create linear interpolating function + final double[] x = new double[] { 0, 1 }; + final double[] y = neighbors.stream().map(TimeStampedDouble::getValue).mapToDouble(Double::doubleValue).toArray(); - // Loops over the 81 days centered on the given date - for (int i = -40; i < 41; i++) { - average += getMeanFlux(date.shiftedBy(i * Constants.JULIAN_DAY)); - } + final LinearInterpolator interpolator = new LinearInterpolator(); + final UnivariateFunction interpolatingFunction = interpolator.interpolate(x, y); - // Returns the 81 day average flux - return average / 81; + // Interpolate + final AbsoluteDate previousDate = neighbors.get(0).getDate(); + final AbsoluteDate nextDate = neighbors.get(1).getDate(); + return interpolatingFunction.value(date.durationFrom(previousDate) / nextDate.durationFrom(previousDate)); } /** {@inheritDoc} */ public double[] getAp(final AbsoluteDate date) { - // Gets the AP for the current date final double ap = getDailyAp(date); // Retuns an array of Ap filled in with the daily Ap only - return new double[] {ap, ap, ap, ap, ap, ap, ap}; + return new double[] { ap, ap, ap, ap, ap, ap, ap }; } - /** Gets the daily Ap index. + /** + * Gets the daily Ap index. * * @param date the current date + * * @return the daily Ap index */ private double getDailyAp(final AbsoluteDate date) { + return getLinearInterpolation(date, LineParameters::getAp); + } - // get the neighboring dates - bracketDate(date); - - // perform a linear interpolation - final AbsoluteDate previousDate = previousParam.getDate(); - final AbsoluteDate currentDate = currentParam.getDate(); - final double dt = currentDate.durationFrom(previousDate); - final double previousAp = previousParam.getAp(); - final double currentAp = currentParam.getAp(); - final double previousWeight = currentDate.durationFrom(date) / dt; - final double currentWeight = date.durationFrom(previousDate) / dt; + /** + * Replace the instance with a data transfer object for serialization. + * + * @return data transfer object that will be serialized + */ + @DefaultDataContext + private Object writeReplace() { + return new DataTransferObject(getSupportedNames(), strengthLevel); + } - // returns the daily Ap interpolated at the date - return previousAp * previousWeight + currentAp * currentWeight; + /** + * Get the strength level for activity. + * + * @return strength level to set + */ + public StrengthLevel getStrengthLevel() { + return strengthLevel; } - /** Container class for Solar activity indexes. */ - private static class LineParameters implements TimeStamped, Serializable { + /** Strength level of activity. */ + public enum StrengthLevel { - /** Serializable UID. */ - private static final long serialVersionUID = 6607862001953526475L; + /** Strong level of activity. */ + STRONG, - /** File date. */ - private final DateComponents fileDate; + /** Average level of activity. */ + AVERAGE, - /** Entry date. */ - private final AbsoluteDate date; + /** Weak level of activity. */ + WEAK - /** F10.7 flux at date. */ - private final double f107; + } - /** Ap index at date. */ - private final double ap; + /** Generator generating average flux data between given dates. */ + private class AverageFluxGenerator implements TimeStampedGenerator { - /** Simple constructor. - * @param fileDate file date - * @param date entry date - * @param f107 F10.7 flux at date - * @param ap Ap index at date - */ - private LineParameters(final DateComponents fileDate, final AbsoluteDate date, final double f107, final double ap) { - this.fileDate = fileDate; - this.date = date; - this.f107 = f107; - this.ap = ap; + /** {@inheritDoc} */ + @Override + public List generate(final AbsoluteDate existingDate, final AbsoluteDate date) { + // No prior data in the cache + if (existingDate == null) { + return generateDataFromEarliestToLatestDates(getCurrentDay(date), getNextDay(date)); + } + // Prior data in the cache, fill with data between date and existing date + if (date.isBefore(existingDate)) { + return generateDataFromEarliestToLatestDates(date, existingDate); + } + return generateDataFromEarliestToLatestDates(existingDate, date); } - /** Get the file date. - * @return file date + /** + * Generate data from earliest to latest date. + * + * @param earliest earliest date + * @param latest latest date + * + * @return data generated from earliest to latest date */ - public DateComponents getFileDate() { - return fileDate; + private List generateDataFromEarliestToLatestDates(final AbsoluteDate earliest, + final AbsoluteDate latest) { + final List generated = new ArrayList<>(); + + // Add next computed average until it brackets the latest date + AbsoluteDate latestNeighbourDate = getCurrentDay(earliest); + while (latestNeighbourDate.isBeforeOrEqualTo(latest)) { + generated.add(computeAverageFlux(latestNeighbourDate)); + latestNeighbourDate = getNextDay(latestNeighbourDate); + } + return generated; } - /** Get the current date. - * @return current date + /** + * Get the current day at midnight. + * + * @param date date + * + * @return current day at midnight. */ - public AbsoluteDate getDate() { - return date; - } + private AbsoluteDate getCurrentDay(final AbsoluteDate date) { + // Find previous day date time components + final TimeScale utc = getUTC(); + final DateComponents dateComponents = date.getComponents(utc).getDate(); - /** Get the F10.0 flux. - * @return f10.7 flux - */ - public double getF107() { - return f107; + // Create absolute date for previous day + return new AbsoluteDate(new DateTimeComponents(dateComponents, TimeComponents.H00), utc); } - /** Get the Ap index. - * @return Ap index + /** + * Get the next day at midnight. + * + * @param date date + * + * @return next day at midnight. */ - public double getAp() { - return ap; - } - - } - - /** {@inheritDoc} */ - public void loadData(final InputStream input, final String name) - throws IOException, ParseException, OrekitException { - - // select the groups we want to store - final int f107Group; - final int apGroup; - switch (strengthLevel) { - case STRONG : - f107Group = 3; - apGroup = 6; - break; - case AVERAGE : - f107Group = 4; - apGroup = 7; - break; - default : - f107Group = 5; - apGroup = 8; - break; + private AbsoluteDate getNextDay(final AbsoluteDate date) { + // Find previous day date time components + final TimeScale utc = getUTC(); + final DateComponents dateComponents = date.getComponents(utc).getDate(); + final DateComponents shiftedComponents = new DateComponents(dateComponents, 1); + + // Create absolute date for previous day + return new AbsoluteDate(new DateTimeComponents(shiftedComponents, TimeComponents.H00), utc); } - boolean inData = false; - DateComponents fileDate = null; - // read the data - try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { - - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - line = line.trim(); - if (line.length() > 0) { - final Matcher matcher = dataPattern.matcher(line); - if (matcher.matches()) { - - // we are in the data section - inData = true; - - // extract the data from the line - final int year = Integer.parseInt(matcher.group(1)); - final Month month = Month.parseMonth(matcher.group(2)); - final AbsoluteDate date = new AbsoluteDate(year, month, 1, this.utc); - if (fileDate == null) { - // the first entry of each file correspond exactly to 6 months before file publication - // so we compute the file date by adding 6 months to its first entry - if (month.getNumber() > 6) { - fileDate = new DateComponents(year + 1, month.getNumber() - 6, 1); - } else { - fileDate = new DateComponents(year, month.getNumber() + 6, 1); - } - } - - // check if there is already an entry for this date or not - boolean addEntry = false; - final Iterator iterator = data.tailSet(date).iterator(); - if (iterator.hasNext()) { - final LineParameters existingEntry = (LineParameters) iterator.next(); - if (existingEntry.getDate().equals(date)) { - // there is an entry for this date - if (existingEntry.getFileDate().compareTo(fileDate) < 0) { - // the entry was read from an earlier file - // we replace it with the new entry as it is fresher - iterator.remove(); - addEntry = true; - } - } else { - // it is the first entry we get for this date - addEntry = true; - } - } else { - // it is the first entry we get for this date - addEntry = true; - } - if (addEntry) { - // we must add the new entry - data.add(new LineParameters(fileDate, date, - Double.parseDouble(matcher.group(f107Group)), - Double.parseDouble(matcher.group(apGroup)))); - } - - } else { - if (inData) { - // we have already read some data, so we are not in the header anymore - // however, we don't recognize this non-empty line, - // we consider the file is corrupted - throw new OrekitException(OrekitMessages.NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE, - name); - } - } - } + /** + * Compute the average flux for given absolute date. + * + * @param date date at which the average flux is desired + * + * @return average flux + */ + private TimeStampedDouble computeAverageFlux(final AbsoluteDate date) { + // Extract list of neighbors to compute average + final TimeStampedGenerator generator = getCache().getGenerator(); + final AbsoluteDate initialDate = date.shiftedBy(-40 * Constants.JULIAN_DAY); + final AbsoluteDate finalDate = date.shiftedBy(40 * Constants.JULIAN_DAY); + final List monthlyData = generator.generate(initialDate, finalDate); + + // Create interpolator for given data + final LinearInterpolator interpolator = new LinearInterpolator(); + + final double[] x = monthlyData.stream().map(param -> param.getDate().durationFrom(initialDate)) + .mapToDouble(Double::doubleValue).toArray(); + final double[] y = monthlyData.stream().map(LineParameters::getF107).mapToDouble(Double::doubleValue).toArray(); + + final UnivariateFunction interpolatingFunction = interpolator.interpolate(x, y); + + // Loops over the 81 days centered on the given date + double average = 0; + for (int i = -40; i < 41; i++) { + average += interpolatingFunction.value(date.shiftedBy(i * Constants.JULIAN_DAY).durationFrom(initialDate)); } + // Returns the 81 day average flux + return new TimeStampedDouble(average / 81, date); } - - if (data.isEmpty()) { - throw new OrekitException(OrekitMessages.NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE, - name); - } - firstDate = data.first().getDate(); - lastDate = data.last().getDate(); - - } - - /** {@inheritDoc} */ - public boolean stillAcceptsData() { - return true; - } - - /** Replace the instance with a data transfer object for serialization. - * @return data transfer object that will be serialized - */ - @DefaultDataContext - private Object writeReplace() { - return new DataTransferObject(getSupportedNames(), strengthLevel); } /** Internal class used only for serialization. */ @@ -617,7 +537,9 @@ private static class DataTransferObject implements Serializable { /** Selected strength level of activity. */ private final StrengthLevel strengthLevel; - /** Simple constructor. + /** + * Simple constructor. + * * @param supportedNames regular expression for supported files names * @param strengthLevel selected strength level of activity */ @@ -627,17 +549,18 @@ private static class DataTransferObject implements Serializable { this.strengthLevel = strengthLevel; } - /** Replace the deserialized data transfer object with a {@link MarshallSolarActivityFutureEstimation}. + /** + * Replace the deserialized data transfer object with a {@link MarshallSolarActivityFutureEstimation}. + * * @return replacement {@link MarshallSolarActivityFutureEstimation} */ private Object readResolve() { try { return new MarshallSolarActivityFutureEstimation(supportedNames, strengthLevel); - } catch (OrekitException oe) { + } + catch (OrekitException oe) { throw new OrekitInternalError(oe); } } - } - } diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimationLoader.java b/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimationLoader.java new file mode 100644 index 0000000000..536ec62a7b --- /dev/null +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/MarshallSolarActivityFutureEstimationLoader.java @@ -0,0 +1,348 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.models.earth.atmosphere.data; + +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.atmosphere.data.MarshallSolarActivityFutureEstimation.StrengthLevel; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.ChronologicalComparator; +import org.orekit.time.DateComponents; +import org.orekit.time.Month; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeStamped; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * This class reads solar activity data needed by atmospheric models: F107 solar flux, Ap and Kp indexes. + *

        + * The data are retrieved through the NASA Marshall Solar Activity Future Estimation (MSAFE) as estimates of monthly F10.7 + * Mean solar flux and Ap geomagnetic parameter. The data can be retrieved at the NASA Marshall Solar Activity website. Here Kp indices are deduced + * from Ap indexes, which in turn are tabulated equivalent of retrieved Ap values. + *

        + *

        + * If several MSAFE files are available, some dates may appear in several files (for example August 2007 is in all files from + * the first one published in March 1999 to the February 2008 file). In this case, the data from the most recent file is used + * and the older ones are discarded. The date of the file is assumed to be 6 months after its first entry (which explains why + * the file having August 2007 as its first entry is the February 2008 file). This implies that MSAFE files must not + * be edited to change their time span, otherwise this would break the old entries overriding mechanism. + *

        + * + *

        References

        + * + *
        1. Jacchia, L. G. "CIRA 1972, recent atmospheric models, and improvements in + * progress." COSPAR, 21st Plenary Meeting. Vol. 1. 1978.
        + * + * @author Bruno Revelin + * @author Luc Maisonobe + * @author Evan Ward + * @author Pascal Parraud + * @author Vincent Cucchietti + */ +public class MarshallSolarActivityFutureEstimationLoader + extends AbstractSolarActivityDataLoader { + + /** Pattern for the data fields of MSAFE data. */ + private final Pattern dataPattern; + + /** Data set. */ + private final SortedSet data; + + /** Selected strength level of activity. */ + private final StrengthLevel strengthLevel; + + /** + * Simple constructor. This constructor uses the {@link DataContext#getDefault() default data context}. + * + * @param strengthLevel selected strength level of activity + */ + @DefaultDataContext + public MarshallSolarActivityFutureEstimationLoader(final StrengthLevel strengthLevel) { + this(strengthLevel, DataContext.getDefault().getTimeScales().getUTC()); + } + + /** + * Constructor that allows specifying the source of the MSAFE auxiliary data files. + * + * @param strengthLevel selected strength level of activity + * @param utc UTC time scale. + * + * @since 10.1 + */ + public MarshallSolarActivityFutureEstimationLoader(final StrengthLevel strengthLevel, final TimeScale utc) { + super(utc); + + this.data = new TreeSet<>(new ChronologicalComparator()); + this.strengthLevel = strengthLevel; + + // the data lines have the following form: + // 2010.5003 JUL 83.4 81.3 78.7 6.4 5.9 5.2 + // 2010.5837 AUG 87.3 83.4 78.5 7.0 6.1 4.9 + // 2010.6670 SEP 90.8 85.5 79.4 7.8 6.2 4.7 + // 2010.7503 OCT 94.2 87.6 80.4 9.1 6.4 4.9 + final StringBuilder builder = new StringBuilder("^"); + + // first group: year + builder.append("\\p{Blank}*(\\p{Digit}\\p{Digit}\\p{Digit}\\p{Digit})"); + + // month as fraction of year, not stored in a group + builder.append("\\.\\p{Digit}+"); + + // second group: month as a three upper case letters abbreviation + builder.append("\\p{Blank}+("); + for (final Month month : Month.values()) { + builder.append(month.getUpperCaseAbbreviation()); + builder.append('|'); + } + builder.delete(builder.length() - 1, builder.length()); + builder.append(")"); + + // third to eighth group: data fields + for (int i = 0; i < 6; ++i) { + builder.append("\\p{Blank}+([-+]?[0-9]+\\.[0-9]+)"); + } + + // end of line + builder.append("\\p{Blank}*$"); + + // compile the pattern + this.dataPattern = Pattern.compile(builder.toString()); + + } + + /** {@inheritDoc} */ + public void loadData(final InputStream input, final String name) + throws IOException, ParseException, OrekitException { + + // select the groups we want to store + final int f107Group; + final int apGroup; + switch (strengthLevel) { + case STRONG: + f107Group = 3; + apGroup = 6; + break; + case AVERAGE: + f107Group = 4; + apGroup = 7; + break; + default: + f107Group = 5; + apGroup = 8; + break; + } + + boolean inData = false; + DateComponents fileDate = null; + + // try to read the data + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { + + // Go through each line + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + line = line.trim(); + if (!line.isEmpty()) { + final Matcher matcher = dataPattern.matcher(line); + if (matcher.matches()) { + + // We are in the data section + inData = true; + + // Extract the data from the line + final int year = Integer.parseInt(matcher.group(1)); + final Month month = Month.parseMonth(matcher.group(2)); + final AbsoluteDate date = new AbsoluteDate(year, month, 1, getUTC()); + if (fileDate == null) { + /* The first entry of each file correspond exactly to 6 months before file publication, + so we compute the file date by adding 6 months to its first entry */ + if (month.getNumber() > 6) { + fileDate = new DateComponents(year + 1, month.getNumber() - 6, 1); + } else { + fileDate = new DateComponents(year, month.getNumber() + 6, 1); + } + } + + // check if there is already an entry for this date or not + boolean addEntry = false; + final Iterator iterator = data.tailSet(date).iterator(); + if (iterator.hasNext()) { + final LineParameters existingEntry = (LineParameters) iterator.next(); + if (existingEntry.getDate().equals(date)) { + // there is an entry for this date + if (existingEntry.getFileDate().compareTo(fileDate) < 0) { + // the entry was read from an earlier file + // we replace it with the new entry as it is fresher + iterator.remove(); + addEntry = true; + } + } else { + // it is the first entry we get for this date + addEntry = true; + } + } else { + // it is the first entry we get for this date + addEntry = true; + } + if (addEntry) { + // we must add the new entry + data.add(new LineParameters(fileDate, date, + Double.parseDouble(matcher.group(f107Group)), + Double.parseDouble(matcher.group(apGroup)))); + } + + } else { + if (inData) { + /* We have already read some data, so we are not in the header anymore + however, we don't recognize this non-empty line, we consider the file is corrupted */ + throw new OrekitException(OrekitMessages.NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE, + name); + } + } + } + } + + } + + if (data.isEmpty()) { + throw new OrekitException(OrekitMessages.NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE, name); + } + setMinDate(data.first().getDate()); + setMaxDate(data.last().getDate()); + + } + + /** @return the data set */ + @Override + public SortedSet getDataSet() { + return data.stream().map(value -> (LineParameters) value).collect(Collectors.toCollection(TreeSet::new)); + } + + /** Container class for Solar activity indexes. */ + public static class LineParameters extends AbstractSolarActivityDataLoader.LineParameters { + + /** Serializable UID. */ + private static final long serialVersionUID = 6607862001953526475L; + + /** File date. */ + private final DateComponents fileDate; + + /** F10.7 flux at date. */ + private final double f107; + + /** Ap index at date. */ + private final double ap; + + /** + * Simple constructor. + * + * @param fileDate file date + * @param date entry date + * @param f107 F10.7 flux at date + * @param ap Ap index at date + */ + private LineParameters(final DateComponents fileDate, final AbsoluteDate date, final double f107, final double ap) { + super(date); + this.fileDate = fileDate; + this.f107 = f107; + this.ap = ap; + } + + /** {@inheritDoc} */ + @Override + public int compareTo(final AbstractSolarActivityDataLoader.LineParameters lineParameters) { + return getDate().compareTo(lineParameters.getDate()); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object otherInstance) { + if (this == otherInstance) { + return true; + } + if (otherInstance == null || getClass() != otherInstance.getClass()) { + return false; + } + + final LineParameters msafeParams = (LineParameters) otherInstance; + + if (Double.compare(getF107(), msafeParams.getF107()) != 0) { + return false; + } + if (Double.compare(getAp(), msafeParams.getAp()) != 0) { + return false; + } + return getFileDate().equals(msafeParams.getFileDate()); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + int result; + long temp; + result = getFileDate().hashCode(); + temp = Double.doubleToLongBits(getF107()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getAp()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + /** + * Get the file date. + * + * @return file date + */ + public DateComponents getFileDate() { + return fileDate; + } + + /** + * Get the F10.0 flux. + * + * @return f10.7 flux + */ + public double getF107() { + return f107; + } + + /** + * Get the Ap index. + * + * @return Ap index + */ + public double getAp() { + return ap; + } + + } + +} diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/SOLFSMYDataLoader.java b/src/main/java/org/orekit/models/earth/atmosphere/data/SOLFSMYDataLoader.java index bb837239ab..6350b0d479 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/SOLFSMYDataLoader.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/SOLFSMYDataLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/data/package-info.java b/src/main/java/org/orekit/models/earth/atmosphere/data/package-info.java index f663cfd39a..009bd0983a 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/data/package-info.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/data/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/package-info.java b/src/main/java/org/orekit/models/earth/atmosphere/package-info.java index 14f136083b..c756272596 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/package-info.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/displacement/OceanLoading.java b/src/main/java/org/orekit/models/earth/displacement/OceanLoading.java index 9eb9ff8074..73cd0adbf0 100644 --- a/src/main/java/org/orekit/models/earth/displacement/OceanLoading.java +++ b/src/main/java/org/orekit/models/earth/displacement/OceanLoading.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficients.java b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficients.java index f1842f20f5..9a72e932d2 100644 --- a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficients.java +++ b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficients.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java index 6e07e2f5af..35657402dc 100644 --- a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java +++ b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/displacement/StationDisplacement.java b/src/main/java/org/orekit/models/earth/displacement/StationDisplacement.java index 2f355447cb..89ad23b819 100644 --- a/src/main/java/org/orekit/models/earth/displacement/StationDisplacement.java +++ b/src/main/java/org/orekit/models/earth/displacement/StationDisplacement.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/displacement/TectonicsDisplacement.java b/src/main/java/org/orekit/models/earth/displacement/TectonicsDisplacement.java new file mode 100644 index 0000000000..53d6d8861d --- /dev/null +++ b/src/main/java/org/orekit/models/earth/displacement/TectonicsDisplacement.java @@ -0,0 +1,57 @@ +/* Copyright 2023 Thales Alenia Space + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.models.earth.displacement; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.data.BodiesElements; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; + +/** Modeling of displacement of reference points due to plate tectonics. + *

        + * Instances of this class are guaranteed to be immutable + *

        + * @see org.orekit.estimation.measurements.GroundStation + * @see org.orekit.files.sinex + * @since 12.0 + * @author Luc Maisonobe + */ +public class TectonicsDisplacement implements StationDisplacement { + + /** Coordinates reference epoch. */ + private AbsoluteDate epoch; + + /** Station velocity. */ + private Vector3D velocity; + + /** Simple constructor. + * @param velocity station velocity in Earth frame (m/s) + * @param epoch coordinates reference epoch + */ + public TectonicsDisplacement(final AbsoluteDate epoch, final Vector3D velocity) { + this.epoch = epoch; + this.velocity = velocity; + } + + /** {@inheritDoc} */ + @Override + public Vector3D displacement(final BodiesElements elements, final Frame earthFrame, final Vector3D referencePoint) { + return new Vector3D(elements.getDate().durationFrom(epoch), velocity); + } + +} + diff --git a/src/main/java/org/orekit/models/earth/displacement/TidalDisplacement.java b/src/main/java/org/orekit/models/earth/displacement/TidalDisplacement.java index 700d8ee641..f544802772 100644 --- a/src/main/java/org/orekit/models/earth/displacement/TidalDisplacement.java +++ b/src/main/java/org/orekit/models/earth/displacement/TidalDisplacement.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -197,9 +197,9 @@ public Vector3D displacement(final BodiesElements elements, final Frame earthFra // preliminary computation (we hold everything in local variables so method is thread-safe) final PointData pointData = new PointData(referencePoint); - final Vector3D sunPosition = sun.getPVCoordinates(date, earthFrame).getPosition(); + final Vector3D sunPosition = sun.getPosition(date, earthFrame); final BodyData sunData = new BodyData(sunPosition, ratio2S, ratio3S, pointData); - final Vector3D moonPosition = moon.getPVCoordinates(date, earthFrame).getPosition(); + final Vector3D moonPosition = moon.getPosition(date, earthFrame); final BodyData moonData = new BodyData(moonPosition, ratio2M, ratio3M, pointData); // step 1 in IERS procedure: corrections in the time domain diff --git a/src/main/java/org/orekit/models/earth/displacement/Tide.java b/src/main/java/org/orekit/models/earth/displacement/Tide.java index 7525dad4c8..8e25ec52ff 100644 --- a/src/main/java/org/orekit/models/earth/displacement/Tide.java +++ b/src/main/java/org/orekit/models/earth/displacement/Tide.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/displacement/package-info.java b/src/main/java/org/orekit/models/earth/displacement/package-info.java index db2dcc1ee2..e8267b7644 100644 --- a/src/main/java/org/orekit/models/earth/displacement/package-info.java +++ b/src/main/java/org/orekit/models/earth/displacement/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModel.java b/src/main/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModel.java index b9173b86a0..84a20da1d4 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -52,9 +52,6 @@ public class EstimatedIonosphericModel implements IonosphericModel { /** Name of the parameter of this model: the Vertical Total Electron Content. */ public static final String VERTICAL_TOTAL_ELECTRON_CONTENT = "vertical total electron content"; - /** Serializable UID. */ - private static final long serialVersionUID = 20200304L; - /** Ionospheric delay factor. */ private static final double FACTOR = 40.3e16; @@ -81,7 +78,7 @@ public EstimatedIonosphericModel(final IonosphericMappingFunction model, final d public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame, final double frequency, final double[] parameters) { // Elevation in radians - final Vector3D position = state.getPVCoordinates(baseFrame).getPosition(); + final Vector3D position = state.getPosition(baseFrame); final double elevation = position.getDelta(); // Only consider measures above the horizon @@ -121,7 +118,7 @@ public double pathDelay(final double elevation, final double frequency, final do public > T pathDelay(final FieldSpacecraftState state, final TopocentricFrame baseFrame, final double frequency, final T[] parameters) { // Elevation and azimuth in radians - final FieldVector3D position = state.getPVCoordinates(baseFrame).getPosition(); + final FieldVector3D position = state.getPosition(baseFrame); final T elevation = position.getDelta(); if (elevation.getReal() > 0.0) { @@ -141,7 +138,7 @@ public > T pathDelay(final FieldSpacecraftStat * @param type of the elements * @param elevation elevation of the satellite in radians * @param frequency frequency of the signal in Hz - * @param parameters ionospheric model parameters + * @param parameters ionospheric model parameters at state date * @return the path delay due to the ionosphere in m */ public > T pathDelay(final T elevation, final double frequency, final T[] parameters) { diff --git a/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java b/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java index 780cf43c7b..ee94cbb057 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -293,7 +293,7 @@ private T computeMODIP(final T lat, final T lon, final double[][] stModip) { // Auxiliary parameter l (Eq. 6 to 8) final int lF = (int) ((longitude.getReal() + 180) * 0.1); int l = lF - 2; - if (l < 0) { + if (l < -2) { l += 36; } else if (l > 33) { l -= 36; diff --git a/src/main/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModel.java b/src/main/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModel.java index 451340df2e..9d229930b8 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,45 +16,41 @@ */ package org.orekit.models.earth.ionosphere; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.Serializable; import java.nio.charset.StandardCharsets; -import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.regex.Pattern; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction; +import org.hipparchus.exception.DummyLocalizable; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.BodyShape; import org.orekit.bodies.GeodeticPoint; -import org.orekit.data.AbstractSelfFeedingLoader; import org.orekit.data.DataContext; import org.orekit.data.DataLoader; import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; import org.orekit.frames.TopocentricFrame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.DateTimeComponents; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; /** * Global Ionosphere Map (GIM) model. @@ -123,51 +119,22 @@ * @author Bryan Cazabonne * */ -public class GlobalIonosphereMapModel extends AbstractSelfFeedingLoader - implements IonosphericModel { - - /** Serializable UID. */ - private static final long serialVersionUID = 201928052L; +public class GlobalIonosphereMapModel implements IonosphericModel { /** Pattern for delimiting regular expressions. */ private static final Pattern SEPARATOR = Pattern.compile("\\s+"); - /** Threshold for latitude and longitude difference. */ - private static final double THRESHOLD = 0.001; - - /** Geodetic site latitude, radians.*/ - private double latitude; - - /** Geodetic site longitude, radians.*/ - private double longitude; - - /** Mean earth radius [m]. */ - private double r0; - - /** Height of the ionospheric single layer [m]. */ - private double h; - - /** Time interval between two TEC maps [s]. */ - private double dt; - - /** Number of TEC maps as read on the header of the file. */ - private int nbMaps; - - /** Flag for mapping function computation. */ - private boolean mapping; - - /** Epoch of the first TEC map as read in the header of the IONEX file. */ - private AbsoluteDate startDate; - - /** Epoch of the last TEC map as read in the header of the IONEX file. */ - private AbsoluteDate endDate; - - /** Map of interpolated TEC at a specific date. */ - private Map tecMap; + /** Map of interpolable TEC. */ + private TimeSpanMap tecMap; /** UTC time scale. */ private final TimeScale utc; + /** Loaded IONEX files. + * @since 12.0 + */ + private String names; + /** * Constructor with supported names given by user. This constructor uses the {@link * DataContext#getDefault() default data context}. @@ -197,39 +164,67 @@ public GlobalIonosphereMapModel(final String supportedNames) { public GlobalIonosphereMapModel(final String supportedNames, final DataProvidersManager dataProvidersManager, final TimeScale utc) { - super(supportedNames, dataProvidersManager); - this.latitude = Double.NaN; - this.longitude = Double.NaN; - this.tecMap = new HashMap<>(); - this.utc = utc; + this.utc = utc; + this.tecMap = new TimeSpanMap<>(null); + this.names = ""; + + // Read files + dataProvidersManager.feed(supportedNames, new Parser()); + + } + + /** + * Constructor that uses user defined data sources. + * + * @param utc UTC time scale. + * @param ionex sources for the IONEX files + * @since 12.0 + */ + public GlobalIonosphereMapModel(final TimeScale utc, + final DataSource... ionex) { + try { + this.utc = utc; + this.tecMap = new TimeSpanMap<>(null); + this.names = ""; + final Parser parser = new Parser(); + for (final DataSource source : ionex) { + try (InputStream is = source.getOpener().openStreamOnce(); + BufferedInputStream bis = new BufferedInputStream(is)) { + parser.loadData(bis, source.getName()); + } + } + } catch (IOException ioe) { + throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage())); + } } /** * Calculates the ionospheric path delay for the signal path from a ground - * station to a satellite. + * station to a satellite traversing ionosphere single layer at some pierce point. *

        * The path delay can be computed for any elevation angle. *

        * @param date current date - * @param geo geodetic point of receiver/station - * @param elevation elevation of the satellite in radians + * @param piercePoint ionospheric pierce point + * @param elevation elevation of the satellite from receiver point in radians * @param frequency frequency of the signal in Hz * @return the path delay due to the ionosphere in m */ - public double pathDelay(final AbsoluteDate date, final GeodeticPoint geo, - final double elevation, final double frequency) { + private double pathDelayAtIPP(final AbsoluteDate date, final GeodeticPoint piercePoint, + final double elevation, final double frequency) { // TEC in TECUnits - final double tec = getTEC(date, geo); + final TECMapPair pair = getPairAtDate(date); + final double tec = pair.getTEC(date, piercePoint); // Square of the frequency final double freq2 = frequency * frequency; // "Slant" Total Electron Content final double stec; // Check if a mapping factor is needed - if (mapping) { + if (pair.mapping) { stec = tec; } else { // Mapping factor - final double fz = mappingFunction(elevation); + final double fz = mappingFunction(elevation, pair.r0, pair.h); stec = tec * fz; } // Delay computation @@ -241,18 +236,26 @@ public double pathDelay(final AbsoluteDate date, final GeodeticPoint geo, public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame, final double frequency, final double[] parameters) { + // Satellite position in body frame + final Frame bodyFrame = baseFrame.getParentShape().getBodyFrame(); + final Vector3D satPoint = state.getPosition(bodyFrame); + // Elevation in radians - final Vector3D position = state.getPVCoordinates(baseFrame).getPosition(); - final double elevation = position.getDelta(); + final double elevation = bodyFrame. + getStaticTransformTo(baseFrame, state.getDate()). + transformPosition(satPoint). + getDelta(); // Only consider measures above the horizon if (elevation > 0.0) { - // Date - final AbsoluteDate date = state.getDate(); - // Geodetic point - final GeodeticPoint geo = baseFrame.getPoint(); - // Delay - return pathDelay(date, geo, elevation, frequency); + // Normalized Line Of Sight in body frame + final Vector3D los = satPoint.subtract(baseFrame.getCartesianPoint()).normalize(); + // Ionosphere Pierce Point + final GeodeticPoint ipp = piercePoint(state.getDate(), baseFrame.getCartesianPoint(), los, baseFrame.getParentShape()); + if (ipp != null) { + // Delay + return pathDelayAtIPP(state.getDate(), ipp, elevation, frequency); + } } return 0.0; @@ -261,31 +264,33 @@ public double pathDelay(final SpacecraftState state, final TopocentricFrame base /** * Calculates the ionospheric path delay for the signal path from a ground - * station to a satellite. + * station to a satellite traversing ionosphere single layer at some pierce point. *

        * The path delay can be computed for any elevation angle. *

        * @param type of the elements * @param date current date - * @param geo geodetic point of receiver/station - * @param elevation elevation of the satellite in radians + * @param piercePoint ionospheric pierce point + * @param elevation elevation of the satellite from receiver point in radians * @param frequency frequency of the signal in Hz * @return the path delay due to the ionosphere in m */ - public > T pathDelay(final FieldAbsoluteDate date, final GeodeticPoint geo, - final T elevation, final double frequency) { + private > T pathDelayAtIPP(final FieldAbsoluteDate date, + final GeodeticPoint piercePoint, + final T elevation, final double frequency) { // TEC in TECUnits - final T tec = getTEC(date, geo); + final TECMapPair pair = getPairAtDate(date.toAbsoluteDate()); + final T tec = pair.getTEC(date, piercePoint); // Square of the frequency final double freq2 = frequency * frequency; // "Slant" Total Electron Content final T stec; // Check if a mapping factor is needed - if (mapping) { + if (pair.mapping) { stec = tec; } else { // Mapping factor - final T fz = mappingFunction(elevation); + final T fz = mappingFunction(elevation, pair.r0, pair.h); stec = tec.multiply(fz); } // Delay computation @@ -295,96 +300,46 @@ public > T pathDelay(final FieldAbsoluteDate> T pathDelay(final FieldSpacecraftState state, final TopocentricFrame baseFrame, - final double frequency, final T[] parameters) { + final double frequency, final T[] parameters) { + + // Satellite position in body frame + final Frame bodyFrame = baseFrame.getParentShape().getBodyFrame(); + final FieldVector3D satPoint = state.getPosition(bodyFrame); // Elevation in radians - final FieldVector3D position = state.getPVCoordinates(baseFrame).getPosition(); - final T elevation = position.getDelta(); + final T elevation = bodyFrame. + getStaticTransformTo(baseFrame, state.getDate()). + transformPosition(satPoint). + getDelta(); // Only consider measures above the horizon if (elevation.getReal() > 0.0) { - // Date - final FieldAbsoluteDate date = state.getDate(); - // Geodetic point - final GeodeticPoint geo = baseFrame.getPoint(); - // Delay - return pathDelay(date, geo, elevation, frequency); + // Normalized Line Of Sight in body frame + final Vector3D los = satPoint.toVector3D().subtract(baseFrame.getCartesianPoint()).normalize(); + // Ionosphere Pierce Point + final GeodeticPoint ipp = piercePoint(state.getDate().toAbsoluteDate(), baseFrame.getCartesianPoint(), los, baseFrame.getParentShape()); + if (ipp != null) { + // Delay + return pathDelayAtIPP(state.getDate(), ipp, elevation, frequency); + } } return elevation.getField().getZero(); } - /** - * Computes the Total Electron Content (TEC) at a given date by performing a - * temporal interpolation with the two closest date in the IONEX file. - * @param date current date - * @param recPoint geodetic point of receiver/station - * @return the TEC after a temporal interpolation, in TECUnits - */ - public double getTEC(final AbsoluteDate date, final GeodeticPoint recPoint) { - - // Load TEC data only if needed - loadsIfNeeded(recPoint); - - // Check if the date is out of range - checkDate(date); - - // Date and Time components - final DateTimeComponents dateTime = date.getComponents(utc); - // Find the two closest dates of the current date - final double secInDay = dateTime.getTime().getSecondsInLocalDay(); - final double ratio = FastMath.floor(secInDay / dt) * dt; - final AbsoluteDate tI = new AbsoluteDate(dateTime.getDate(), - new TimeComponents(ratio), - utc); - final AbsoluteDate tIp1 = tI.shiftedBy(dt); - - // Get the TEC values at the two closest dates - final double tecI = tecMap.get(tI); - final double tecIp1 = tecMap.get(tIp1); - - // Perform temporal interpolation (Ref, Eq. 2) - final double tec = (tIp1.durationFrom(date) / dt) * tecI + (date.durationFrom(tI) / dt) * tecIp1; - return tec; - } - - /** - * Computes the Total Electron Content (TEC) at a given date by performing a - * temporal interpolation with the two closest date in the IONEX file. - * @param type of the elements - * @param date current date - * @param recPoint geodetic point of receiver/station - * @return the TEC after a temporal interpolation, in TECUnits + /** Get the pair valid at date. + * @param date computation date + * @return pair valid at date + * @since 12.0 */ - public > T getTEC(final FieldAbsoluteDate date, final GeodeticPoint recPoint) { - - // Load TEC data only if needed - loadsIfNeeded(recPoint); - - // Check if the date is out of range - checkDate(date.toAbsoluteDate()); - - // Field - final Field field = date.getField(); - - // Date and Time components - final DateTimeComponents dateTime = date.getComponents(utc); - // Find the two closest dates of the current date - final double secInDay = dateTime.getTime().getSecondsInLocalDay(); - final double ratio = FastMath.floor(secInDay / dt) * dt; - final FieldAbsoluteDate tI = new FieldAbsoluteDate<>(field, dateTime.getDate(), - new TimeComponents(ratio), - utc); - final FieldAbsoluteDate tIp1 = tI.shiftedBy(dt); - - // Get the TEC values at the two closest dates - final double tecI = tecMap.get(tI.toAbsoluteDate()); - final double tecIp1 = tecMap.get(tIp1.toAbsoluteDate()); - - // Perform temporal interpolation (Ref, Eq. 2) - final T tec = tIp1.durationFrom(date).divide(dt).multiply(tecI).add(date.durationFrom(tI).divide(dt).multiply(tecIp1)); - return tec; + private TECMapPair getPairAtDate(final AbsoluteDate date) { + final TECMapPair pair = tecMap.get(date); + if (pair == null) { + throw new OrekitException(OrekitMessages.NO_TEC_DATA_IN_FILES_FOR_DATE, + names, date); + } + return pair; } @Override @@ -395,9 +350,12 @@ public List getParametersDrivers() { /** * Computes the ionospheric mapping function. * @param elevation the elevation of the satellite in radians + * @param r0 mean Earth radius + * @param h height of the ionospheric layer * @return the mapping function */ - private double mappingFunction(final double elevation) { + private double mappingFunction(final double elevation, + final double r0, final double h) { // Calculate the zenith angle from the elevation final double z = FastMath.abs(0.5 * FastMath.PI - elevation); // Distance ratio @@ -412,9 +370,12 @@ private double mappingFunction(final double elevation) { * Computes the ionospheric mapping function. * @param type of the elements * @param elevation the elevation of the satellite in radians + * @param r0 mean Earth radius + * @param h height of the ionospheric layer * @return the mapping function */ - private > T mappingFunction(final T elevation) { + private > T mappingFunction(final T elevation, + final double r0, final double h) { // Calculate the zenith angle from the elevation final T z = FastMath.abs(elevation.getPi().multiply(0.5).subtract(elevation)); // Distance ratio @@ -425,71 +386,39 @@ private > T mappingFunction(final T elevation) return fz; } - /** - * Lazy loading of TEC data. - * @param recPoint geodetic point of receiver/station + /** Compute Ionospheric Pierce Point. + *

        + * The pierce point is computed assuming a spherical ionospheric shell above mean Earth radius. + *

        + * @param date computation date + * @param recPoint point at receiver station in body frame + * @param los normalized line of sight in body frame + * @param bodyShape shape of the body + * @return pierce point, or null if recPoint is above ionosphere single layer + * @since 12.0 */ - private void loadsIfNeeded(final GeodeticPoint recPoint) { - - // Current latitude and longitude of the geodetic point - final double lat = recPoint.getLatitude(); - final double lon = MathUtils.normalizeAngle(recPoint.getLongitude(), 0.0); - - // Read the file only if the TEC map is empty or if the geodetic point displacement is - // greater than 0.001 radians (in latitude or longitude) - if (tecMap.isEmpty() || FastMath.abs(lat - latitude) > THRESHOLD || FastMath.abs(lon - longitude) > THRESHOLD) { - this.latitude = lat; - this.longitude = lon; - - // Read file - final Parser parser = new Parser(); - feed(parser); - - // File header - final IONEXHeader top = parser.getIONEXHeader(); - this.startDate = top.getFirstDate(); - this.endDate = top.getLastDate(); - this.dt = top.getInterval(); - this.nbMaps = top.getTECMapsNumer(); - this.r0 = top.getEarthRadius(); - this.h = top.getHIon(); - this.mapping = top.isMappingFunction(); - - // TEC map - for (TECMap map : parser.getTECMaps()) { - tecMap.put(map.getDate(), map.getTEC()); - } + private GeodeticPoint piercePoint(final AbsoluteDate date, + final Vector3D recPoint, final Vector3D los, + final BodyShape bodyShape) { + + final TECMapPair pair = getPairAtDate(date); + final double r = pair.r0 + pair.h; + final double r2 = r * r; + final double p2 = recPoint.getNormSq(); + if (p2 >= r2) { + // we are above ionosphere single layer + return null; } - checkSize(); - } - /** - * Check if the current date is between the startDate and - * the endDate of the IONEX file. - * @param date current date - */ - private void checkDate(final AbsoluteDate date) { - if (startDate.durationFrom(date) > 0 || date.durationFrom(endDate) > 0) { - throw new OrekitException(OrekitMessages.NO_TEC_DATA_IN_FILE_FOR_DATE, - getSupportedNames(), date); - } - } + // compute positive k such that recPoint + k los is on the spherical shell at radius r + final double dot = Vector3D.dotProduct(recPoint, los); + final double k = FastMath.sqrt(dot * dot + r2 - p2) - dot; - /** - * Check if the number of parsed TEC maps is consistent with the header specification. - */ - private void checkSize() { - if (tecMap.size() != nbMaps) { - throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE, tecMap.size(), nbMaps); - } - } + // Ionosphere Pierce Point in body frame + final Vector3D ipp = new Vector3D(1, recPoint, k, los); + + return bodyShape.transform(ipp, bodyShape.getBodyFrame(), null); - /** Replace the instance with a data transfer object for serialization. - * @return data transfer object that will be serialized - */ - @DefaultDataContext - private Object writeReplace() { - return new DataTransferObject(getSupportedNames()); } /** Parser for IONEX files. */ @@ -520,7 +449,7 @@ public boolean stillAcceptsData() { @Override public void loadData(final InputStream input, final String name) - throws IOException, ParseException { + throws IOException { maps = new ArrayList<>(); @@ -528,14 +457,13 @@ public void loadData(final InputStream input, final String name) int lineNumber = 0; String line = null; try (InputStreamReader isr = new InputStreamReader(input, StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(isr)) { + BufferedReader br = new BufferedReader(isr)) { // Placeholders for parsed data - int interval = 3600; int nbOfMaps = 1; int exponent = -1; - double baseRadius = 6371.0e3; - double hIon = 350e3; + double baseRadius = Double.NaN; + double hIon = Double.NaN; boolean mappingF = false; boolean inTEC = false; double[] latitudes = null; @@ -556,7 +484,7 @@ public void loadData(final InputStream input, final String name) lastEpoch = parseDate(line); break; case "INTERVAL" : - interval = parseInt(line, 2, 4); + // ignored; break; case "# OF MAPS IN FILE" : nbOfMaps = parseInt(line, 2, 4); @@ -586,21 +514,20 @@ public void loadData(final InputStream input, final String name) case "END OF HEADER" : // Check that latitude and longitude bondaries were found if (latitudes == null || longitudes == null) { - throw new OrekitException(OrekitMessages.NO_LATITUDE_LONGITUDE_BONDARIES_IN_IONEX_HEADER, getSupportedNames()); + throw new OrekitException(OrekitMessages.NO_LATITUDE_LONGITUDE_BONDARIES_IN_IONEX_HEADER, name); } // Check that first and last epochs were found if (firstEpoch == null || lastEpoch == null) { - throw new OrekitException(OrekitMessages.NO_EPOCH_IN_IONEX_HEADER, getSupportedNames()); + throw new OrekitException(OrekitMessages.NO_EPOCH_IN_IONEX_HEADER, name); } // At the end of the header, we build the IONEXHeader object - header = new IONEXHeader(firstEpoch, lastEpoch, interval, nbOfMaps, - baseRadius, hIon, mappingF); + header = new IONEXHeader(nbOfMaps, baseRadius, hIon, mappingF); break; case "START OF TEC MAP" : inTEC = true; break; case END : - final double tec = interpolateTEC(values, exponent, latitudes, longitudes); + final BilinearInterpolatingFunction tec = interpolateTEC(values, exponent, latitudes, longitudes); final TECMap map = new TECMap(epoch, tec); maps.add(map); // Reset parameters @@ -649,22 +576,23 @@ public void loadData(final InputStream input, final String name) lineNumber, name, line); } - } + // TEC map + if (maps.size() != header.getTECMapsNumer()) { + throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE, + maps.size(), header.getTECMapsNumer()); + } + TECMap previous = null; + for (TECMap current : maps) { + if (previous != null) { + tecMap.addValidBetween(new TECMapPair(previous, current, + header.getEarthRadius(), header.getHIon(), header.isMappingFunction()), + previous.date, current.date); + } + previous = current; + } - /** - * Get the header of the IONEX file. - * @return the header of the IONEX file - */ - public IONEXHeader getIONEXHeader() { - return header; - } + names = names.isEmpty() ? name : (names + ", " + name); - /** - * Get the list of the TEC maps. - * @return the list of TEC maps. - */ - public List getTECMaps() { - return maps; } /** Extract a string from a line. @@ -733,29 +661,30 @@ private double[] parseCoordinate(final String line) { * @param values TEC values * @param latitudes array containing the different latitudes in radians * @param longitudes array containing the different latitudes in radians - * @return the interpolated TEC in TECUnits + * @return the interpolating TEC functiopn in TECUnits */ - private double interpolateTEC(final ArrayList values, final double exponent, - final double[] latitudes, final double[] longitudes) { + private BilinearInterpolatingFunction interpolateTEC(final ArrayList values, final double exponent, + final double[] latitudes, final double[] longitudes) { // Array dimensions final int dimLat = latitudes.length; final int dimLon = longitudes.length; // Build the array of TEC data + final double factor = FastMath.pow(10.0, exponent); final double[][] fvalTEC = new double[dimLat][dimLon]; int index = dimLon * dimLat; for (int x = 0; x < dimLat; x++) { for (int y = dimLon - 1; y >= 0; y--) { index = index - 1; - fvalTEC[x][y] = values.get(index); + fvalTEC[x][y] = values.get(index) * factor; } } // Build Bilinear Interpolation function - final BilinearInterpolatingFunction functionTEC = new BilinearInterpolatingFunction(latitudes, longitudes, fvalTEC); - final double tec = functionTEC.value(latitude, longitude) * FastMath.pow(10.0, exponent); - return tec; + return new BilinearInterpolatingFunction(latitudes, longitudes, fvalTEC); + } + } /** @@ -771,47 +700,98 @@ private static class TECMap { private AbsoluteDate date; /** Interpolated TEC [TECUnits]. */ - private double tec; + private BilinearInterpolatingFunction tec; /** * Constructor. * @param date date of the TEC map * @param tec interpolated tec */ - TECMap(final AbsoluteDate date, final double tec) { + TECMap(final AbsoluteDate date, final BilinearInterpolatingFunction tec) { this.date = date; this.tec = tec; } - /** - * Get the date of the TEC map. - * @return the date + } + + /** Container for a consecutive pair of TEC maps. + * @since 12.0 + */ + private static class TECMapPair { + + /** First snapshot. */ + private final TECMap first; + + /** Second snapshot. */ + private final TECMap second; + + /** Mean earth radius [m]. */ + private double r0; + + /** Height of the ionospheric single layer [m]. */ + private double h; + + /** Flag for mapping function computation. */ + private boolean mapping; + + /** Simple constructor. + * @param first first snapshot + * @param second second snapshot + * @param r0 mean Earth radius + * @param h height of the ionospheric layer + * @param mapping flag for mapping computation */ - public AbsoluteDate getDate() { - return date; + TECMapPair(final TECMap first, final TECMap second, + final double r0, final double h, final boolean mapping) { + this.first = first; + this.second = second; + this.r0 = r0; + this.h = h; + this.mapping = mapping; } - /** - * Get the value of the interpolated TEC. - * @return the TEC in TECUnits + /** Get TEC at pierce point. + * @param date date + * @param ipp Ionospheric Pierce Point + * @return TEC */ - public double getTEC() { - return tec; + public double getTEC(final AbsoluteDate date, final GeodeticPoint ipp) { + // Get the TEC values at the two closest dates + final AbsoluteDate t1 = first.date; + final double tec1 = first.tec.value(ipp.getLatitude(), ipp.getLongitude()); + final AbsoluteDate t2 = second.date; + final double tec2 = second.tec.value(ipp.getLatitude(), ipp.getLongitude()); + final double dt = t2.durationFrom(t1); + + // Perform temporal interpolation (Ref, Eq. 2) + return (t2.durationFrom(date) / dt) * tec1 + (date.durationFrom(t1) / dt) * tec2; + } - } + /** Get TEC at pierce point. + * @param date date + * @param ipp Ionospheric Pierce Point + * @param type of the field elements + * @return TEC + */ + public > T getTEC(final FieldAbsoluteDate date, final GeodeticPoint ipp) { - /** Container for IONEX header. */ - private static class IONEXHeader { + // Get the TEC values at the two closest dates + final AbsoluteDate t1 = first.date; + final double tec1 = first.tec.value(ipp.getLatitude(), ipp.getLongitude()); + final AbsoluteDate t2 = second.date; + final double tec2 = second.tec.value(ipp.getLatitude(), ipp.getLongitude()); + final double dt = t2.durationFrom(t1); - /** Epoch of the first TEC map. */ - private AbsoluteDate firstDate; + // Perform temporal interpolation (Ref, Eq. 2) + return date.durationFrom(t2).negate().divide(dt).multiply(tec1).add(date.durationFrom(t1).divide(dt).multiply(tec2)); - /** Epoch of the last TEC map. */ - private AbsoluteDate lastDate; + } - /** Interval between two maps [s]. */ - private int interval; + } + + /** Container for IONEX header. */ + private static class IONEXHeader { /** Number of maps contained in the IONEX file. */ private int nbOfMaps; @@ -827,51 +807,20 @@ private static class IONEXHeader { /** * Constructor. - * @param firstDate epoch of the first TEC map. - * @param lastDate epoch of the last TEC map. * @param nbOfMaps number of TEC maps contained in the file - * @param interval number of seconds between two tec maps. * @param baseRadius mean earth radius in meters * @param hIon height of the ionospheric single layer in meters * @param mappingFunction flag for mapping function adopted for TEC determination */ - IONEXHeader(final AbsoluteDate firstDate, final AbsoluteDate lastDate, - final int interval, final int nbOfMaps, + IONEXHeader(final int nbOfMaps, final double baseRadius, final double hIon, final boolean mappingFunction) { - this.firstDate = firstDate; - this.lastDate = lastDate; - this.interval = interval; this.nbOfMaps = nbOfMaps; this.baseRadius = baseRadius; this.hIon = hIon; this.isMappingFunction = mappingFunction; } - /** - * Get the first date of the IONEX file. - * @return the first date of the IONEX file - */ - public AbsoluteDate getFirstDate() { - return firstDate; - } - - /** - * Get the last date of the IONEX file. - * @return the last date of the IONEX file - */ - public AbsoluteDate getLastDate() { - return lastDate; - } - - /** - * Get the time interval between two TEC maps. - * @return the interval between two TEC maps - */ - public int getInterval() { - return interval; - } - /** * Get the number of TEC maps contained in the file. * @return the number of TEC maps @@ -906,34 +855,4 @@ public boolean isMappingFunction() { } - /** Internal class used only for serialization. */ - @DefaultDataContext - private static class DataTransferObject implements Serializable { - - /** Serializable UID. */ - private static final long serialVersionUID = 201928052L; - - /** Regular expression that matches the names of the IONEX files. */ - private final String supportedNames; - - /** Simple constructor. - * @param supportedNames regular expression that matches the names of the IONEX files - */ - DataTransferObject(final String supportedNames) { - this.supportedNames = supportedNames; - } - - /** Replace the deserialized data transfer object with a {@link GlobalIonosphereMapModel}. - * @return replacement {@link GlobalIonosphereMapModel} - */ - private Object readResolve() { - try { - return new GlobalIonosphereMapModel(supportedNames); - } catch (OrekitException oe) { - throw new OrekitInternalError(oe); - } - } - - } - } diff --git a/src/main/java/org/orekit/models/earth/ionosphere/IonosphericMappingFunction.java b/src/main/java/org/orekit/models/earth/ionosphere/IonosphericMappingFunction.java index d7514b15c1..b491c788f4 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/IonosphericMappingFunction.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/IonosphericMappingFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/ionosphere/IonosphericModel.java b/src/main/java/org/orekit/models/earth/ionosphere/IonosphericModel.java index 9aac4a35bd..c9fd0175bf 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/IonosphericModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/IonosphericModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,17 +16,11 @@ */ package org.orekit.models.earth.ionosphere; -import java.io.Serializable; -import java.util.List; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.MathArrays; import org.orekit.frames.TopocentricFrame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; /** Defines a ionospheric model, used to calculate the path delay imposed to * electro-magnetic signals between an orbital satellite and a ground station. @@ -39,7 +33,7 @@ * @author Bryan Cazabonne * @since 7.1 */ -public interface IonosphericModel extends ParametersDriversProvider, Serializable { +public interface IonosphericModel extends ParameterDriversProvider { /** * Calculates the ionospheric path delay for the signal path from a ground @@ -54,7 +48,7 @@ public interface IonosphericModel extends ParametersDriversProvider, Serializabl * @param state spacecraft state * @param baseFrame base frame associated with the station * @param frequency frequency of the signal in Hz - * @param parameters ionospheric model parameters + * @param parameters ionospheric model parameters at state date * @return the path delay due to the ionosphere in m */ double pathDelay(SpacecraftState state, TopocentricFrame baseFrame, double frequency, double[] parameters); @@ -73,35 +67,9 @@ public interface IonosphericModel extends ParametersDriversProvider, Serializabl * @param state spacecraft state * @param baseFrame base frame associated with the station * @param frequency frequency of the signal in Hz - * @param parameters ionospheric model parameters + * @param parameters ionospheric model parameters at state date * @return the path delay due to the ionosphere in m */ - > T pathDelay(FieldSpacecraftState state, TopocentricFrame baseFrame, double frequency, T[] parameters); - - /** Get ionospheric model parameters. - * @return ionospheric model parameters - */ - default double[] getParameters() { - final List drivers = getParametersDrivers(); - final double[] parameters = new double[drivers.size()]; - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = drivers.get(i).getValue(); - } - return parameters; - } - - /** Get ionospheric model parameters. - * @param field field to which the elements belong - * @param type of the elements - * @return ionospheric model parameters - */ - default > T[] getParameters(final Field field) { - final List drivers = getParametersDrivers(); - final T[] parameters = MathArrays.buildArray(field, drivers.size()); - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue()); - } - return parameters; - } - + > T pathDelay(FieldSpacecraftState state, TopocentricFrame baseFrame, + double frequency, T[] parameters); } diff --git a/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoCoefficientsLoader.java b/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoCoefficientsLoader.java index af8324cca9..1d3d0946a5 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoCoefficientsLoader.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoCoefficientsLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java b/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java index 2fcba03498..1958f92e5f 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -67,9 +67,6 @@ */ public class KlobucharIonoModel implements IonosphericModel { - /** Serializable UID. */ - private static final long serialVersionUID = 7277525837842061107L; - /** The 4 coefficients of a cubic equation representing the amplitude of the vertical delay. Units are sec/semi-circle^(i-1) for the i-th coefficient, i=1, 2, 3, 4. */ private final double[] alpha; @@ -194,7 +191,7 @@ public double pathDelay(final SpacecraftState state, final TopocentricFrame base final double frequency, final double[] parameters) { // Elevation in radians - final Vector3D position = state.getPVCoordinates(baseFrame).getPosition(); + final Vector3D position = state.getPosition(baseFrame); final double elevation = position.getDelta(); // Only consider measures above the horizon @@ -302,7 +299,7 @@ public > T pathDelay(final FieldSpacecraftStat final double frequency, final T[] parameters) { // Elevation and azimuth in radians - final FieldVector3D position = state.getPVCoordinates(baseFrame).getPosition(); + final FieldVector3D position = state.getPosition(baseFrame); final T elevation = position.getDelta(); if (elevation.getReal() > 0.0) { diff --git a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java index 14bc4ec015..8872fd2521 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -47,8 +47,6 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedFieldPVCoordinates; -import org.orekit.utils.TimeStampedPVCoordinates; /** * NeQuick ionospheric delay model. @@ -65,9 +63,6 @@ public class NeQuickModel implements IonosphericModel { /** NeQuick resources base directory. */ private static final String NEQUICK_BASE = "/assets/org/orekit/nequick/"; - /** Serializable UID. */ - private static final long serialVersionUID = 201928051L; - /** Pattern for delimiting regular expressions. */ private static final Pattern SEPARATOR = Pattern.compile("\\s+"); @@ -146,8 +141,7 @@ public double pathDelay(final SpacecraftState state, final TopocentricFrame base // Reference body shape final BodyShape ellipsoid = baseFrame.getParentShape(); // Satellite geodetic coordinates - final TimeStampedPVCoordinates pv = state.getPVCoordinates(ellipsoid.getBodyFrame()); - final GeodeticPoint satPoint = ellipsoid.transform(pv.getPosition(), ellipsoid.getBodyFrame(), state.getDate()); + final GeodeticPoint satPoint = ellipsoid.transform(state.getPosition(ellipsoid.getBodyFrame()), ellipsoid.getBodyFrame(), state.getDate()); // Total Electron Content final double tec = stec(date, recPoint, satPoint); @@ -169,8 +163,7 @@ public > T pathDelay(final FieldSpacecraftStat // Reference body shape final BodyShape ellipsoid = baseFrame.getParentShape(); // Satellite geodetic coordinates - final TimeStampedFieldPVCoordinates pv = state.getPVCoordinates(ellipsoid.getBodyFrame()); - final FieldGeodeticPoint satPoint = ellipsoid.transform(pv.getPosition(), ellipsoid.getBodyFrame(), state.getDate()); + final FieldGeodeticPoint satPoint = ellipsoid.transform(state.getPosition(ellipsoid.getBodyFrame()), ellipsoid.getBodyFrame(), state.getDate()); // Total Electron Content final T tec = stec(date, recPoint, satPoint); diff --git a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java index 0891c01e97..c2d7476e66 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/NeQuickParameters.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -283,7 +283,7 @@ private double computeMODIP(final double lat, final double lon, final double[][] // Auxiliary parameter l (Eq. 6 to 8) final int lF = (int) ((longitude + 180) * 0.1); int l = lF - 2; - if (l < 0) { + if (l < -2) { l += 36; } else if (l > 33) { l -= 36; diff --git a/src/main/java/org/orekit/models/earth/ionosphere/SingleLayerModelMappingFunction.java b/src/main/java/org/orekit/models/earth/ionosphere/SingleLayerModelMappingFunction.java index c920f4c00e..239afb7988 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/SingleLayerModelMappingFunction.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/SingleLayerModelMappingFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModel.java b/src/main/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModel.java index 7a833603e2..919618609e 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -56,9 +56,6 @@ */ public class SsrVtecIonosphericModel implements IonosphericModel { - /** Serializable UID. */ - private static final long serialVersionUID = 20210322L; - /** Earth radius in meters (see reference). */ private static final double EARTH_RADIUS = 6370000.0; @@ -82,7 +79,7 @@ public double pathDelay(final SpacecraftState state, final TopocentricFrame base final double frequency, final double[] parameters) { // Elevation in radians - final Vector3D position = state.getPVCoordinates(baseFrame).getPosition(); + final Vector3D position = state.getPosition(baseFrame); final double elevation = position.getDelta(); // Only consider measures above the horizon @@ -124,7 +121,7 @@ public > T pathDelay(final FieldSpacecraftStat final Field field = state.getDate().getField(); // Elevation in radians - final FieldVector3D position = state.getPVCoordinates(baseFrame).getPosition(); + final FieldVector3D position = state.getPosition(baseFrame); final T elevation = position.getDelta(); // Only consider measures above the horizon diff --git a/src/main/java/org/orekit/models/earth/ionosphere/package-info.java b/src/main/java/org/orekit/models/earth/ionosphere/package-info.java index df802edff9..d38d6f848b 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/package-info.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/package-info.java b/src/main/java/org/orekit/models/earth/package-info.java index 5781ccf51d..6ac0b95bb6 100644 --- a/src/main/java/org/orekit/models/earth/package-info.java +++ b/src/main/java/org/orekit/models/earth/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/AlongTrackAiming.java b/src/main/java/org/orekit/models/earth/tessellation/AlongTrackAiming.java index b6b95135d9..46425c3e09 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/AlongTrackAiming.java +++ b/src/main/java/org/orekit/models/earth/tessellation/AlongTrackAiming.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,7 +25,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.Pair; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.orbits.Orbit; @@ -84,7 +84,7 @@ public Vector3D alongTileDirection(final Vector3D point, final GeodeticPoint gp) int iSup = halfTrack.size() - 1; while (iSup - iInf > 1) { final int iMiddle = (iSup + iInf) / 2; - if ((lStart < lEnd) ^ (halfTrack.get(iMiddle).getFirst().getLatitude() > gp.getLatitude())) { + if (lStart < lEnd ^ halfTrack.get(iMiddle).getFirst().getLatitude() > gp.getLatitude()) { // the specified latitude is in the second half iInf = iMiddle; } else { @@ -139,7 +139,7 @@ private static List> findHalfTrack // find the span of the next half track final Propagator propagator = - new KeplerianPropagator(orbit, new InertialProvider(orbit.getFrame())); + new KeplerianPropagator(orbit, new FrameAlignedProvider(orbit.getFrame())); final HalfTrackSpanHandler handler = new HalfTrackSpanHandler(isAscending); final LatitudeExtremumDetector detector = new LatitudeExtremumDetector(0.25 * orbit.getKeplerianPeriod(), 1.0e-3, ellipsoid). diff --git a/src/main/java/org/orekit/models/earth/tessellation/ConstantAzimuthAiming.java b/src/main/java/org/orekit/models/earth/tessellation/ConstantAzimuthAiming.java index e108dacb52..89d9f580f6 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/ConstantAzimuthAiming.java +++ b/src/main/java/org/orekit/models/earth/tessellation/ConstantAzimuthAiming.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/Direction.java b/src/main/java/org/orekit/models/earth/tessellation/Direction.java index 2064f68210..a517755543 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/Direction.java +++ b/src/main/java/org/orekit/models/earth/tessellation/Direction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/DivertedSingularityAiming.java b/src/main/java/org/orekit/models/earth/tessellation/DivertedSingularityAiming.java index daeae9778a..aaebaccf56 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/DivertedSingularityAiming.java +++ b/src/main/java/org/orekit/models/earth/tessellation/DivertedSingularityAiming.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java b/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java index 48f5c94483..c56e9838a3 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java +++ b/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSampler.java b/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSampler.java index 2bcf50a42e..24eef64a9e 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSampler.java +++ b/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSampler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSpanHandler.java b/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSpanHandler.java index 17c45c58f6..8cf7ef0e0a 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSpanHandler.java +++ b/src/main/java/org/orekit/models/earth/tessellation/HalfTrackSpanHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ import org.hipparchus.ode.events.Action; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.LatitudeExtremumDetector; +import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; @@ -27,7 +27,7 @@ * @see AlongTrackAiming * @author Luc Maisonobe */ -class HalfTrackSpanHandler implements EventHandler { +class HalfTrackSpanHandler implements EventHandler { /** Indicator for zone tiling with respect to ascending or descending orbits. */ private final boolean isAscending; @@ -64,9 +64,7 @@ public AbsoluteDate getEnd() { /** {@inheritDoc} */ @Override - public Action eventOccurred(final SpacecraftState s, - final LatitudeExtremumDetector detector, - final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { if (increasing ^ isAscending) { // we have found an end event if (start == null) { diff --git a/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java b/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java index 84bdcecc72..f756d81d87 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java +++ b/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/Mesh.java b/src/main/java/org/orekit/models/earth/tessellation/Mesh.java index 44d892242b..1d1d377ca2 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/Mesh.java +++ b/src/main/java/org/orekit/models/earth/tessellation/Mesh.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/Tile.java b/src/main/java/org/orekit/models/earth/tessellation/Tile.java index da8df8ff48..b4c9105a0f 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/Tile.java +++ b/src/main/java/org/orekit/models/earth/tessellation/Tile.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/TileAiming.java b/src/main/java/org/orekit/models/earth/tessellation/TileAiming.java index 16258124f8..beed7650aa 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/TileAiming.java +++ b/src/main/java/org/orekit/models/earth/tessellation/TileAiming.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/tessellation/package-info.java b/src/main/java/org/orekit/models/earth/tessellation/package-info.java index c6fcfe0c4b..b2c174949e 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/package-info.java +++ b/src/main/java/org/orekit/models/earth/tessellation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java index a1d96ec69a..b79fed704f 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,17 +16,12 @@ */ package org.orekit.models.earth.troposphere; -import java.util.List; - import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; -import org.hipparchus.util.MathArrays; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; /** Defines a tropospheric model, used to calculate the path delay imposed to * electro-magnetic signals between an orbital satellite and a ground station. @@ -43,7 +38,7 @@ * * @author Bryan Cazabonne */ -public interface DiscreteTroposphericModel extends ParametersDriversProvider { +public interface DiscreteTroposphericModel extends ParameterDriversProvider { /** Calculates the tropospheric path delay for the signal path from a ground * station to a satellite. @@ -62,36 +57,10 @@ public interface DiscreteTroposphericModel extends ParametersDriversProvider { * @param type of the elements * @param elevation the elevation of the satellite, in radians * @param point station location - * @param parameters tropospheric model parameters + * @param parameters tropospheric model parameters at current date * @param date current date * @return the path delay due to the troposphere in m */ - > T pathDelay(T elevation, FieldGeodeticPoint point, T[] parameters, FieldAbsoluteDate date); - - /** Get tropospheric model parameters. - * @return tropospheric model parameters - */ - default double[] getParameters() { - final List drivers = getParametersDrivers(); - final double[] parameters = new double[drivers.size()]; - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = drivers.get(i).getValue(); - } - return parameters; - } - - /** Get tropospheric model parameters. - * @param field field to which the elements belong - * @param type of the elements - * @return tropospheric model parameters - */ - default > T[] getParameters(final Field field) { - final List drivers = getParametersDrivers(); - final T[] parameters = MathArrays.buildArray(field, drivers.size()); - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue()); - } - return parameters; - } - + > T pathDelay(T elevation, FieldGeodeticPoint point, T[] parameters, + FieldAbsoluteDate date); } diff --git a/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java index d26548b31f..ece77cc7c0 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java b/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java index 287b9c5554..1c326c24df 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java index 77bbdbe802..1465393f06 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java +++ b/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java b/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java index 7341754cb7..4644e256b2 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java b/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java index fde2cbc71f..a7392f71e1 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java b/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java index 83776d3a38..78c9c99883 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java @@ -20,8 +20,8 @@ import java.util.List; import java.util.regex.Pattern; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction; import org.hipparchus.analysis.interpolation.LinearInterpolator; import org.hipparchus.analysis.polynomials.PolynomialFunction; @@ -179,6 +179,7 @@ public SaastamoinenModel(final double t0, */ private SaastamoinenModel(final double t0, final double p0, final double r0, final BilinearInterpolatingFunction deltaR) { + checkParameterRangeInclusive("humidity", r0, 0.0, 1.0); this.t0 = t0; this.p0 = p0; this.r0 = r0; @@ -202,6 +203,28 @@ public static SaastamoinenModel getStandardModel() { return new SaastamoinenModel(273.16 + 18, 1013.25, 0.5); } + /** Check if the given parameter is within an acceptable range. + * The bounds are inclusive: an exception is raised when either of those conditions are met: + *
          + *
        • The parameter is strictly greater than upperBound
        • + *
        • The parameter is strictly lower than lowerBound
        • + *
        + *

        + * In either of these cases, an OrekitException is raised. + *

        + * @param parameterName name of the parameter + * @param parameter value of the parameter + * @param lowerBound lower bound of the acceptable range (inclusive) + * @param upperBound upper bound of the acceptable range (inclusive) + */ + private void checkParameterRangeInclusive(final String parameterName, final double parameter, + final double lowerBound, final double upperBound) { + if (parameter < lowerBound || parameter > upperBound) { + throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName, + parameter, lowerBound, upperBound); + } + } + /** {@inheritDoc} *

        * The Saastamoinen model is not defined for altitudes below 0.0. for continuity diff --git a/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java index d11057b436..e2abe4a1d3 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.NavigableSet; import org.hipparchus.CalculusFieldElement; import org.hipparchus.util.MathArrays; @@ -32,7 +31,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeSpanMap.Span; -import org.orekit.utils.TimeSpanMap.Transition; /** * Time span estimated tropospheric model. @@ -143,15 +141,6 @@ public EstimatedTroposphericModel getTroposphericModel(final AbsoluteDate date) return troposphericModelMap.get(date); } - /** Get the {@link Transition}s of the tropospheric model time span map. - * @return the {@link Transition}s for the tropospheric model time span map - * @deprecated as of 11.1, replaced by {@link #getFirstSpan()} - */ - @Deprecated - public NavigableSet> getTransitions() { - return troposphericModelMap.getTransitions(); - } - /** Get the first {@link Span time span} of the tropospheric model time span map. * @return the first {@link Span time span} of the tropospheric model time span map * @since 11.1 diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java index c79888f4f7..59d3aac3e0 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaModelCoefficientsLoader.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaModelCoefficientsLoader.java index 4b2b8caa95..58a35a17ed 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ViennaModelCoefficientsLoader.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaModelCoefficientsLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaModelType.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaModelType.java index 84f3a0fa12..7096f840f3 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ViennaModelType.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaModelType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java index 07952de6a5..bf2f0e4a4e 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java index a5ef2d65cd..ac7a704aee 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/troposphere/package-info.java b/src/main/java/org/orekit/models/earth/troposphere/package-info.java index b2ecb2ae14..b27e4177e0 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/package-info.java +++ b/src/main/java/org/orekit/models/earth/troposphere/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java index 03e32bf69c..b64d485a31 100644 --- a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java index dd337c1ac1..f2bbcbb35d 100644 --- a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/weather/WeatherModel.java b/src/main/java/org/orekit/models/earth/weather/WeatherModel.java index 7e21f3aa71..73613fa1d0 100644 --- a/src/main/java/org/orekit/models/earth/weather/WeatherModel.java +++ b/src/main/java/org/orekit/models/earth/weather/WeatherModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/earth/weather/package-info.java b/src/main/java/org/orekit/models/earth/weather/package-info.java index 4ab8c0f731..5ae3d989d4 100644 --- a/src/main/java/org/orekit/models/earth/weather/package-info.java +++ b/src/main/java/org/orekit/models/earth/weather/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/models/package-info.java b/src/main/java/org/orekit/models/package-info.java index 578fd8caf5..2a307bef63 100644 --- a/src/main/java/org/orekit/models/package-info.java +++ b/src/main/java/org/orekit/models/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/orbits/AbstractFieldOrbitInterpolator.java b/src/main/java/org/orekit/orbits/AbstractFieldOrbitInterpolator.java new file mode 100644 index 0000000000..3449b0cff1 --- /dev/null +++ b/src/main/java/org/orekit/orbits/AbstractFieldOrbitInterpolator.java @@ -0,0 +1,90 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.time.AbstractFieldTimeInterpolator; +import org.orekit.time.FieldAbsoluteDate; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Abstract class for orbit interpolator. + * + * @param type of the field element + * + * @author Vincent Cucchietti + */ +public abstract class AbstractFieldOrbitInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** Output inertial frame. */ + private final Frame outputInertialFrame; + + /** + * Constructor. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputInertialFrame output inertial frame + */ + public AbstractFieldOrbitInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputInertialFrame) { + super(interpolationPoints, extrapolationThreshold); + checkFrameIsInertial(outputInertialFrame); + this.outputInertialFrame = outputInertialFrame; + } + + /** {@inheritDoc}. */ + @Override + public FieldOrbit interpolate(final FieldAbsoluteDate interpolationDate, + final Collection> sample) { + + // Convert to orbit list + final List orbits = sample.stream().map(FieldOrbit::toOrbit).collect(Collectors.toList()); + + // Check orbits consistency + AbstractOrbitInterpolator.checkOrbitsConsistency(orbits); + + return super.interpolate(interpolationDate, sample); + } + + /** Get output inertial frame. + * @return output inertial frame + */ + public Frame getOutputInertialFrame() { + return outputInertialFrame; + } + + /** + * Check if given frame is pseudo inertial and throw an error otherwise. + * + * @param frame frame to check + * + * @throws OrekitIllegalArgumentException if given frame is not pseudo inertial + */ + private void checkFrameIsInertial(final Frame frame) { + if (!frame.isPseudoInertial()) { + throw new OrekitIllegalArgumentException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frame.getName()); + } + } +} diff --git a/src/main/java/org/orekit/orbits/AbstractOrbitInterpolator.java b/src/main/java/org/orekit/orbits/AbstractOrbitInterpolator.java new file mode 100644 index 0000000000..3180081966 --- /dev/null +++ b/src/main/java/org/orekit/orbits/AbstractOrbitInterpolator.java @@ -0,0 +1,103 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Abstract class for orbit interpolator. + * + * @author Vincent Cucchietti + */ +public abstract class AbstractOrbitInterpolator extends AbstractTimeInterpolator { + + /** Output inertial frame. */ + private final Frame outputInertialFrame; + + /** + * Constructor. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputInertialFrame output inertial frame + */ + public AbstractOrbitInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputInertialFrame) { + super(interpolationPoints, extrapolationThreshold); + checkFrameIsInertial(outputInertialFrame); + this.outputInertialFrame = outputInertialFrame; + } + + /** + * Check orbits consistency by comparing their gravitational parameters µ. + * + * @param sample orbits sample + */ + public static void checkOrbitsConsistency(final Collection sample) { + // Convert sample to list + final List sampleList = new ArrayList<>(sample); + + // Check consistency + for (int i = 0; i < sampleList.size() - 1; i++) { + final Orbit currentOrbit = sampleList.get(i); + final Orbit nextOrbit = sampleList.get(i + 1); + + if (currentOrbit.getMu() != nextOrbit.getMu()) { + throw new OrekitIllegalArgumentException(OrekitMessages.ORBITS_MUS_MISMATCH, currentOrbit.getMu(), + nextOrbit.getMu()); + } + } + } + + /** {@inheritDoc}. */ + @Override + public Orbit interpolate(final AbsoluteDate interpolationDate, final Collection sample) { + + // Check orbits consistency + checkOrbitsConsistency(sample); + + return super.interpolate(interpolationDate, sample); + } + + /** Get output inertial frame. + * @return output inertial frame + */ + public Frame getOutputInertialFrame() { + return outputInertialFrame; + } + + /** + * Check if given frame is pseudo inertial and throw an error otherwise. + * + * @param frame frame to check + * + * @throws OrekitIllegalArgumentException if given frame is not pseudo inertial + */ + private void checkFrameIsInertial(final Frame frame) { + if (!frame.isPseudoInertial()) { + throw new OrekitIllegalArgumentException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frame.getName()); + } + } +} diff --git a/src/main/java/org/orekit/orbits/CR3BPDifferentialCorrection.java b/src/main/java/org/orekit/orbits/CR3BPDifferentialCorrection.java index f9a487b506..06d954573b 100644 --- a/src/main/java/org/orekit/orbits/CR3BPDifferentialCorrection.java +++ b/src/main/java/org/orekit/orbits/CR3BPDifferentialCorrection.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,8 +23,7 @@ import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; import org.hipparchus.util.FastMath; -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.bodies.CR3BPSystem; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -34,7 +33,6 @@ import org.orekit.propagation.numerical.cr3bp.CR3BPForceModel; import org.orekit.propagation.numerical.cr3bp.STMEquations; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeScale; import org.orekit.utils.AbsolutePVCoordinates; import org.orekit.utils.PVCoordinates; @@ -90,24 +88,6 @@ public CR3BPDifferentialCorrection(final PVCoordinates firstguess, } - /** Simple Constructor. - *

        Standard constructor using DormandPrince853 integrator for the differential correction

        - * @param firstguess first guess PVCoordinates of the point to start differential correction - * @param syst CR3BP System considered - * @param orbitalPeriod Orbital Period of the required orbit - * @param attitudeProvider the attitude law for the numerical propagator - * @param utc UTC time scale - * @deprecated as of 11.1, replaced by {@link #CR3BPDifferentialCorrection(PVCoordinates, CR3BPSystem, double)} - */ - @Deprecated - public CR3BPDifferentialCorrection(final PVCoordinates firstguess, - final CR3BPSystem syst, - final double orbitalPeriod, - final AttitudeProvider attitudeProvider, - final TimeScale utc) { - this(firstguess, syst, orbitalPeriod); - } - /** Build the propagator. * @return propagator * @since 11.1 @@ -132,7 +112,7 @@ private NumericalPropagator buildPropagator() { // Propagator definition final NumericalPropagator propagator = - new NumericalPropagator(integrator, new InertialProvider(Rotation.IDENTITY, syst.getRotatingFrame())); + new NumericalPropagator(integrator, new FrameAlignedProvider(Rotation.IDENTITY, syst.getRotatingFrame())); // CR3BP has no defined orbit type propagator.setOrbitType(null); diff --git a/src/main/java/org/orekit/orbits/CartesianOrbit.java b/src/main/java/org/orekit/orbits/CartesianOrbit.java index 155f80cfb0..c033dd1a69 100644 --- a/src/main/java/org/orekit/orbits/CartesianOrbit.java +++ b/src/main/java/org/orekit/orbits/CartesianOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,20 +17,19 @@ package org.orekit.orbits; import java.io.Serializable; -import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.MatrixUtils; import org.hipparchus.util.FastMath; import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; -import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -76,6 +75,9 @@ public class CartesianOrbit extends Orbit { /** Serializable UID. */ private static final long serialVersionUID = 20170414L; + /** 6x6 identity matrix. */ + private static final double[][] SIX_BY_SIX_IDENTITY = MatrixUtils.createRealIdentityMatrix(6).getData(); + /** Indicator for non-Keplerian derivatives. */ private final transient boolean hasNonKeplerianAcceleration; @@ -154,7 +156,7 @@ private void initEquinoctial() { } else { // get rid of Keplerian acceleration so we don't assume // we have derivatives when in fact we don't have them - equinoctial = new EquinoctialOrbit(new PVCoordinates(getPVCoordinates().getPosition(), + equinoctial = new EquinoctialOrbit(new PVCoordinates(getPosition(), getPVCoordinates().getVelocity()), getFrame(), getDate(), getMu()); } @@ -183,7 +185,7 @@ private FieldPVCoordinates getPVDerivatives() { /** {@inheritDoc} */ public double getA() { - final double r = getPVCoordinates().getPosition().getNorm(); + final double r = getPosition().getNorm(); final double V2 = getPVCoordinates().getVelocity().getNormSq(); return r / (2 - r * V2 / getMu()); } @@ -204,9 +206,9 @@ public double getADot() { /** {@inheritDoc} */ public double getE() { final double muA = getMu() * getA(); - if (muA > 0) { + if (isElliptical()) { // elliptic or circular orbit - final Vector3D pvP = getPVCoordinates().getPosition(); + final Vector3D pvP = getPosition(); final Vector3D pvV = getPVCoordinates().getVelocity(); final double rV2OnMu = pvP.getNorm() * pvV.getNormSq() / getMu(); final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA); @@ -381,6 +383,12 @@ public boolean hasDerivatives() { return hasNonKeplerianAcceleration; } + /** {@inheritDoc} */ + protected Vector3D initPosition() { + // nothing to do here, as the canonical elements are already the Cartesian ones + return getPVCoordinates().getPosition(); + } + /** {@inheritDoc} */ protected TimeStampedPVCoordinates initPVCoordinates() { // nothing to do here, as the canonical elements are already the Cartesian ones @@ -389,42 +397,17 @@ protected TimeStampedPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public CartesianOrbit shiftedBy(final double dt) { - final PVCoordinates shiftedPV = (getA() < 0) ? shiftPVHyperbolic(dt) : shiftPVElliptic(dt); + final PVCoordinates shiftedPV = isElliptical() ? shiftPVElliptic(dt) : shiftPVHyperbolic(dt); return new CartesianOrbit(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu()); } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public CartesianOrbit interpolate(final AbsoluteDate date, final Stream sample) { - final TimeStampedPVCoordinates interpolated = - TimeStampedPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_PVA, - sample.map(orbit -> orbit.getPVCoordinates())); - return new CartesianOrbit(interpolated, getFrame(), date, getMu()); - } - /** Compute shifted position and velocity in elliptic case. * @param dt time shift * @return shifted position and velocity */ private PVCoordinates shiftPVElliptic(final double dt) { + // preliminary computation final PVCoordinates pv = getPVCoordinates(); final Vector3D pvP = pv.getPosition(); final Vector3D pvV = pv.getVelocity(); @@ -567,35 +550,23 @@ private PVCoordinates shiftPVHyperbolic(final double dt) { } - /** Create a 6x6 identity matrix. - * @return 6x6 identity matrix - */ - private double[][] create6x6Identity() { - // this is the fastest way to set the 6x6 identity matrix - final double[][] identity = new double[6][6]; - for (int i = 0; i < 6; i++) { - identity[i][i] = 1.0; - } - return identity; - } - @Override protected double[][] computeJacobianMeanWrtCartesian() { - return create6x6Identity(); + return SIX_BY_SIX_IDENTITY; } @Override protected double[][] computeJacobianEccentricWrtCartesian() { - return create6x6Identity(); + return SIX_BY_SIX_IDENTITY; } @Override protected double[][] computeJacobianTrueWrtCartesian() { - return create6x6Identity(); + return SIX_BY_SIX_IDENTITY; } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final double gm, + public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final PVCoordinates pv = getPVCoordinates(); diff --git a/src/main/java/org/orekit/orbits/CircularOrbit.java b/src/main/java/org/orekit/orbits/CircularOrbit.java index 7ac2df6475..943cbbf535 100644 --- a/src/main/java/org/orekit/orbits/CircularOrbit.java +++ b/src/main/java/org/orekit/orbits/CircularOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,15 +17,10 @@ package org.orekit.orbits; import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.UnivariateDerivative1; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; @@ -77,8 +72,7 @@ * @author Véronique Pommier-Maurussane */ -public class CircularOrbit - extends Orbit { +public class CircularOrbit extends Orbit implements PositionAngleBased { /** Serializable UID. */ private static final long serialVersionUID = 20170414L; @@ -142,7 +136,7 @@ public class CircularOrbit */ public CircularOrbit(final double a, final double ex, final double ey, final double i, final double raan, final double alpha, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { this(a, ex, ey, i, raan, alpha, @@ -175,7 +169,7 @@ public CircularOrbit(final double a, final double ex, final double ey, final double i, final double raan, final double alpha, final double aDot, final double exDot, final double eyDot, final double iDot, final double raanDot, final double alphaDot, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { super(frame, date, mu); @@ -306,14 +300,13 @@ public CircularOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame f final double r = FastMath.sqrt(r2); final double V2 = pvV.getNormSq(); final double rV2OnMu = r * V2 / mu; + a = r / (2 - rV2OnMu); - if (rV2OnMu > 2) { + if (!isElliptical()) { throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); } - a = r / (2 - rV2OnMu); - // compute inclination final Vector3D momentum = pvCoordinates.getMomentum(); i = Vector3D.angle(momentum, Vector3D.PLUS_K); @@ -352,7 +345,7 @@ public CircularOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame f // we have a relevant acceleration, we can compute derivatives final double[][] jacobian = new double[6][6]; - getJacobianWrtCartesian(PositionAngle.MEAN, jacobian); + getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian); final Vector3D keplerianAcceleration = new Vector3D(-mu / (r * r2), pvP); final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration); @@ -636,9 +629,9 @@ public double getAlphaMDot() { * @param type type of the angle * @return latitude argument (rad) */ - public double getAlpha(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getAlphaM() : - ((type == PositionAngle.ECCENTRIC) ? getAlphaE() : + public double getAlpha(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getAlphaM() : + ((type == PositionAngleType.ECCENTRIC) ? getAlphaE() : getAlphaV()); } @@ -650,9 +643,9 @@ public double getAlpha(final PositionAngle type) { * @return latitude argument derivative (rad/s) * @since 9.0 */ - public double getAlphaDot(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getAlphaMDot() : - ((type == PositionAngle.ECCENTRIC) ? getAlphaEDot() : + public double getAlphaDot(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getAlphaMDot() : + ((type == PositionAngleType.ECCENTRIC) ? getAlphaEDot() : getAlphaVDot()); } @@ -864,7 +857,7 @@ private void computePVWithoutA() { private Vector3D nonKeplerianAcceleration() { final double[][] dCdP = new double[6][6]; - getJacobianWrtParameters(PositionAngle.MEAN, dCdP); + getJacobianWrtParameters(PositionAngleType.MEAN, dCdP); final double nonKeplerianMeanMotion = getAlphaMDot() - getKeplerianMeanMotion(); final double nonKeplerianAx = dCdP[3][0] * aDot + dCdP[3][1] * exDot + dCdP[3][2] * eyDot + @@ -878,6 +871,51 @@ private Vector3D nonKeplerianAcceleration() { } + /** {@inheritDoc} */ + protected Vector3D initPosition() { + + // get equinoctial parameters + final double equEx = getEquinoctialEx(); + final double equEy = getEquinoctialEy(); + final double hx = getHx(); + final double hy = getHy(); + final double lE = getLE(); + + // inclination-related intermediate parameters + final double hx2 = hx * hx; + final double hy2 = hy * hy; + final double factH = 1. / (1 + hx2 + hy2); + + // reference axes defining the orbital plane + final double ux = (1 + hx2 - hy2) * factH; + final double uy = 2 * hx * hy * factH; + final double uz = -2 * hy * factH; + + final double vx = uy; + final double vy = (1 - hx2 + hy2) * factH; + final double vz = 2 * hx * factH; + + // eccentricity-related intermediate parameters + final double exey = equEx * equEy; + final double ex2 = equEx * equEx; + final double ey2 = equEy * equEy; + final double e2 = ex2 + ey2; + final double eta = 1 + FastMath.sqrt(1 - e2); + final double beta = 1. / eta; + + // eccentric latitude argument + final SinCos scLe = FastMath.sinCos(lE); + final double cLe = scLe.cos(); + final double sLe = scLe.sin(); + + // coordinates of position and velocity in the orbital plane + final double x = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - equEx); + final double y = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - equEy); + + return new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz); + + } + /** {@inheritDoc} */ protected TimeStampedPVCoordinates initPVCoordinates() { @@ -901,7 +939,7 @@ public CircularOrbit shiftedBy(final double dt) { // use Keplerian-only motion final CircularOrbit keplerianShifted = new CircularOrbit(a, ex, ey, i, raan, getAlphaM() + getKeplerianMeanMotion() * dt, - PositionAngle.MEAN, getFrame(), + PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -932,100 +970,6 @@ PositionAngle.MEAN, getFrame(), } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on circular elements, without derivatives (which means the interpolation - * falls back to Lagrange interpolation only). - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public CircularOrbit interpolate(final AbsoluteDate date, final Stream sample) { - - // first pass to check if derivatives are available throughout the sample - final List list = sample.collect(Collectors.toList()); - boolean useDerivatives = true; - for (final Orbit orbit : list) { - useDerivatives = useDerivatives && orbit.hasDerivatives(); - } - - // set up an interpolator - final HermiteInterpolator interpolator = new HermiteInterpolator(); - - // second pass to feed interpolator - AbsoluteDate previousDate = null; - double previousRAAN = Double.NaN; - double previousAlphaM = Double.NaN; - for (final Orbit orbit : list) { - final CircularOrbit circ = (CircularOrbit) OrbitType.CIRCULAR.convertType(orbit); - final double continuousRAAN; - final double continuousAlphaM; - if (previousDate == null) { - continuousRAAN = circ.getRightAscensionOfAscendingNode(); - continuousAlphaM = circ.getAlphaM(); - } else { - final double dt = circ.getDate().durationFrom(previousDate); - final double keplerAM = previousAlphaM + circ.getKeplerianMeanMotion() * dt; - continuousRAAN = MathUtils.normalizeAngle(circ.getRightAscensionOfAscendingNode(), previousRAAN); - continuousAlphaM = MathUtils.normalizeAngle(circ.getAlphaM(), keplerAM); - } - previousDate = circ.getDate(); - previousRAAN = continuousRAAN; - previousAlphaM = continuousAlphaM; - if (useDerivatives) { - interpolator.addSamplePoint(circ.getDate().durationFrom(date), - new double[] { - circ.getA(), - circ.getCircularEx(), - circ.getCircularEy(), - circ.getI(), - continuousRAAN, - continuousAlphaM - }, new double[] { - circ.getADot(), - circ.getCircularExDot(), - circ.getCircularEyDot(), - circ.getIDot(), - circ.getRightAscensionOfAscendingNodeDot(), - circ.getAlphaMDot() - }); - } else { - interpolator.addSamplePoint(circ.getDate().durationFrom(date), - new double[] { - circ.getA(), - circ.getCircularEx(), - circ.getCircularEy(), - circ.getI(), - continuousRAAN, - continuousAlphaM - }); - } - } - - // interpolate - final double[][] interpolated = interpolator.derivatives(0.0, 1); - - // build a new interpolated instance - return new CircularOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2], - interpolated[0][3], interpolated[0][4], interpolated[0][5], - interpolated[1][0], interpolated[1][1], interpolated[1][2], - interpolated[1][3], interpolated[1][4], interpolated[1][5], - PositionAngle.MEAN, getFrame(), date, getMu()); - - } - /** {@inheritDoc} */ protected double[][] computeJacobianMeanWrtCartesian() { @@ -1232,7 +1176,7 @@ protected double[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final double gm, + public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final double oMe2; final double ksi; @@ -1270,6 +1214,26 @@ public String toString() { append(";}").toString(); } + /** {@inheritDoc} */ + @Override + public PositionAngleType getCachedPositionAngleType() { + return PositionAngleType.TRUE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasRates() { + return hasDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public CircularOrbit removeRates() { + final PositionAngleType positionAngleType = getCachedPositionAngleType(); + return new CircularOrbit(getA(), getCircularEx(), getCircularEy(), getI(), getRightAscensionOfAscendingNode(), + getAlpha(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + } + /** Replace the instance with a data transfer object for serialization. * @return data transfer object that will be serialized */ @@ -1381,11 +1345,11 @@ private Object readResolve() { case 15 : // date + mu + orbit + derivatives return new CircularOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], - PositionAngle.TRUE, + PositionAngleType.TRUE, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); default : // date + mu + orbit - return new CircularOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngle.TRUE, + return new CircularOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngleType.TRUE, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); diff --git a/src/main/java/org/orekit/orbits/EquinoctialOrbit.java b/src/main/java/org/orekit/orbits/EquinoctialOrbit.java index 673094583a..e69fc170bf 100644 --- a/src/main/java/org/orekit/orbits/EquinoctialOrbit.java +++ b/src/main/java/org/orekit/orbits/EquinoctialOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,15 +17,10 @@ package org.orekit.orbits; import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.UnivariateDerivative1; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; @@ -61,7 +56,8 @@ * nor circular. When orbit is either equatorial or circular, the equinoctial * parameters are still unambiguously defined whereas some Keplerian elements * (more precisely ω and Ω) become ambiguous. For this reason, equinoctial - * parameters are the recommended way to represent orbits. + * parameters are the recommended way to represent orbits. Note however than + * * the present implementation does not handle non-elliptical cases. *

        *

        * The instance EquinoctialOrbit is guaranteed to be immutable. @@ -76,7 +72,7 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane */ -public class EquinoctialOrbit extends Orbit { +public class EquinoctialOrbit extends Orbit implements PositionAngleBased { /** Serializable UID. */ private static final long serialVersionUID = 20170414L; @@ -137,7 +133,7 @@ public class EquinoctialOrbit extends Orbit { */ public EquinoctialOrbit(final double a, final double ex, final double ey, final double hx, final double hy, final double l, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { this(a, ex, ey, hx, hy, l, @@ -170,7 +166,7 @@ public EquinoctialOrbit(final double a, final double ex, final double ey, final double hx, final double hy, final double l, final double aDot, final double exDot, final double eyDot, final double hxDot, final double hyDot, final double lDot, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { super(frame, date, mu); @@ -258,7 +254,10 @@ public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates, final double V2 = pvV.getNormSq(); final double rV2OnMu = r * V2 / mu; - if (rV2OnMu > 2) { + // compute semi-major axis + a = r / (2 - rV2OnMu); + + if (!isElliptical()) { throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); } @@ -274,10 +273,6 @@ public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates, final double sLv = (pvP.getY() - d * pvP.getZ() * w.getY()) / r; lv = FastMath.atan2(sLv, cLv); - - // compute semi-major axis - a = r / (2 - rV2OnMu); - // compute eccentricity vector final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(mu * a); final double eCE = rV2OnMu - 1; @@ -293,7 +288,7 @@ public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates, // we have a relevant acceleration, we can compute derivatives final double[][] jacobian = new double[6][6]; - getJacobianWrtCartesian(PositionAngle.MEAN, jacobian); + getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian); final Vector3D keplerianAcceleration = new Vector3D(-mu / (r * r2), pvP); final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration); @@ -468,9 +463,9 @@ public double getLMDot() { * @param type type of the angle * @return longitude argument (rad) */ - public double getL(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getLM() : - ((type == PositionAngle.ECCENTRIC) ? getLE() : + public double getL(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getLM() : + ((type == PositionAngleType.ECCENTRIC) ? getLE() : getLv()); } @@ -478,9 +473,9 @@ public double getL(final PositionAngle type) { * @param type type of the angle * @return longitude argument derivative (rad/s) */ - public double getLDot(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getLMDot() : - ((type == PositionAngle.ECCENTRIC) ? getLEDot() : + public double getLDot(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getLMDot() : + ((type == PositionAngleType.ECCENTRIC) ? getLEDot() : getLvDot()); } @@ -643,7 +638,7 @@ private void computePVWithoutA() { private Vector3D nonKeplerianAcceleration() { final double[][] dCdP = new double[6][6]; - getJacobianWrtParameters(PositionAngle.MEAN, dCdP); + getJacobianWrtParameters(PositionAngleType.MEAN, dCdP); final double nonKeplerianMeanMotion = getLMDot() - getKeplerianMeanMotion(); final double nonKeplerianAx = dCdP[3][0] * aDot + dCdP[3][1] * exDot + dCdP[3][2] * eyDot + @@ -657,6 +652,47 @@ private Vector3D nonKeplerianAcceleration() { } + /** {@inheritDoc} */ + protected Vector3D initPosition() { + + // get equinoctial parameters + final double lE = getLE(); + + // inclination-related intermediate parameters + final double hx2 = hx * hx; + final double hy2 = hy * hy; + final double factH = 1. / (1 + hx2 + hy2); + + // reference axes defining the orbital plane + final double ux = (1 + hx2 - hy2) * factH; + final double uy = 2 * hx * hy * factH; + final double uz = -2 * hy * factH; + + final double vx = uy; + final double vy = (1 - hx2 + hy2) * factH; + final double vz = 2 * hx * factH; + + // eccentricity-related intermediate parameters + final double exey = ex * ey; + final double ex2 = ex * ex; + final double ey2 = ey * ey; + final double e2 = ex2 + ey2; + final double eta = 1 + FastMath.sqrt(1 - e2); + final double beta = 1. / eta; + + // eccentric longitude argument + final SinCos scLe = FastMath.sinCos(lE); + final double cLe = scLe.cos(); + final double sLe = scLe.sin(); + + // coordinates of position and velocity in the orbital plane + final double x = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex); + final double y = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey); + + return new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz); + + } + /** {@inheritDoc} */ protected TimeStampedPVCoordinates initPVCoordinates() { @@ -680,7 +716,7 @@ public EquinoctialOrbit shiftedBy(final double dt) { // use Keplerian-only motion final EquinoctialOrbit keplerianShifted = new EquinoctialOrbit(a, ex, ey, hx, hy, getLM() + getKeplerianMeanMotion() * dt, - PositionAngle.MEAN, getFrame(), + PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -711,96 +747,6 @@ PositionAngle.MEAN, getFrame(), } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on equinoctial elements, without derivatives (which means the interpolation - * falls back to Lagrange interpolation only). - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public EquinoctialOrbit interpolate(final AbsoluteDate date, final Stream sample) { - - // first pass to check if derivatives are available throughout the sample - final List list = sample.collect(Collectors.toList()); - boolean useDerivatives = true; - for (final Orbit orbit : list) { - useDerivatives = useDerivatives && orbit.hasDerivatives(); - } - - // set up an interpolator - final HermiteInterpolator interpolator = new HermiteInterpolator(); - - // second pass to feed interpolator - AbsoluteDate previousDate = null; - double previousLm = Double.NaN; - for (final Orbit orbit : list) { - final EquinoctialOrbit equi = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit); - final double continuousLm; - if (previousDate == null) { - continuousLm = equi.getLM(); - } else { - final double dt = equi.getDate().durationFrom(previousDate); - final double keplerLm = previousLm + equi.getKeplerianMeanMotion() * dt; - continuousLm = MathUtils.normalizeAngle(equi.getLM(), keplerLm); - } - previousDate = equi.getDate(); - previousLm = continuousLm; - if (useDerivatives) { - interpolator.addSamplePoint(equi.getDate().durationFrom(date), - new double[] { - equi.getA(), - equi.getEquinoctialEx(), - equi.getEquinoctialEy(), - equi.getHx(), - equi.getHy(), - continuousLm - }, - new double[] { - equi.getADot(), - equi.getEquinoctialExDot(), - equi.getEquinoctialEyDot(), - equi.getHxDot(), - equi.getHyDot(), - equi.getLMDot() - }); - } else { - interpolator.addSamplePoint(equi.getDate().durationFrom(date), - new double[] { - equi.getA(), - equi.getEquinoctialEx(), - equi.getEquinoctialEy(), - equi.getHx(), - equi.getHy(), - continuousLm - }); - } - } - - // interpolate - final double[][] interpolated = interpolator.derivatives(0.0, 1); - - // build a new interpolated instance - return new EquinoctialOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2], - interpolated[0][3], interpolated[0][4], interpolated[0][5], - interpolated[1][0], interpolated[1][1], interpolated[1][2], - interpolated[1][3], interpolated[1][4], interpolated[1][5], - PositionAngle.MEAN, getFrame(), date, getMu()); - - } - /** {@inheritDoc} */ protected double[][] computeJacobianMeanWrtCartesian() { @@ -964,7 +910,7 @@ protected double[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final double gm, + public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final double oMe2; final double ksi; @@ -1001,6 +947,26 @@ public String toString() { append(";}").toString(); } + /** {@inheritDoc} */ + @Override + public PositionAngleType getCachedPositionAngleType() { + return PositionAngleType.TRUE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasRates() { + return hasDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public EquinoctialOrbit removeRates() { + final PositionAngleType positionAngleType = getCachedPositionAngleType(); + return new EquinoctialOrbit(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(), + getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + } + /** Replace the instance with a data transfer object for serialization. * @return data transfer object that will be serialized */ @@ -1067,12 +1033,12 @@ private Object readResolve() { // we have derivatives return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], - PositionAngle.TRUE, + PositionAngleType.TRUE, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } else { // we don't have derivatives - return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], PositionAngle.TRUE, + return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], PositionAngleType.TRUE, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } diff --git a/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java b/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java index 5c6ab8e7e7..f83ce34f1f 100644 --- a/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,6 @@ import java.util.Arrays; -import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; @@ -33,7 +32,6 @@ import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; @@ -73,6 +71,7 @@ * @author Véronique Pommier-Maurussane * @author Andrew Goetz * @since 9.0 + * @param type of the field elements */ public class FieldCartesianOrbit> extends FieldOrbit { @@ -82,15 +81,6 @@ public class FieldCartesianOrbit> extends Fiel /** Underlying equinoctial orbit to which high-level methods are delegated. */ private transient FieldEquinoctialOrbit equinoctial; - /** Field used by this class.*/ - private final Field field; - - /** Zero. (could be usefull)*/ - private final T zero; - - /** One. (could be useful)*/ - private final T one; - /** Constructor from Cartesian parameters. * *

        The acceleration provided in {@code pvCoordinates} is accessible using @@ -111,9 +101,6 @@ public FieldCartesianOrbit(final TimeStampedFieldPVCoordinates pvaCoordinates super(pvaCoordinates, frame, mu); hasNonKeplerianAcceleration = hasNonKeplerianAcceleration(pvaCoordinates, mu); equinoctial = null; - field = pvaCoordinates.getPosition().getX().getField(); - zero = field.getZero(); - one = field.getOne(); } /** Constructor from Cartesian parameters. @@ -150,9 +137,33 @@ public FieldCartesianOrbit(final FieldOrbit op) { } else { equinoctial = null; } - field = op.getA().getField(); - zero = field.getZero(); - one = field.getOne(); + } + + /** Constructor from Field and CartesianOrbit. + *

        Build a FieldCartesianOrbit from non-Field CartesianOrbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldCartesianOrbit(final Field field, final CartesianOrbit op) { + super(new TimeStampedFieldPVCoordinates<>(field, op.getPVCoordinates()), op.getFrame(), + field.getZero().add(op.getMu())); + hasNonKeplerianAcceleration = op.hasDerivatives(); + if (op.isElliptical()) { + equinoctial = new FieldEquinoctialOrbit<>(field, new EquinoctialOrbit(op)); + } else { + equinoctial = null; + } + } + + /** Constructor from Field and Orbit. + *

        Build a FieldCartesianOrbit from any non-Field Orbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldCartesianOrbit(final Field field, final Orbit op) { + this(field, new CartesianOrbit(op)); } /** {@inheritDoc} */ @@ -169,8 +180,8 @@ private void initEquinoctial() { } else { // get rid of Keplerian acceleration so we don't assume // we have derivatives when in fact we don't have them - equinoctial = new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(getPVCoordinates().getPosition(), - getPVCoordinates().getVelocity()), + final FieldPVCoordinates pva = getPVCoordinates(); + equinoctial = new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(pva.getPosition(), pva.getVelocity()), getFrame(), getDate(), getMu()); } } @@ -190,17 +201,18 @@ private FieldPVCoordinates> getPVDerivatives() { final FieldVector3D> pG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(p.getX(), v.getX(), a.getX()), new FieldUnivariateDerivative2<>(p.getY(), v.getY(), a.getY()), new FieldUnivariateDerivative2<>(p.getZ(), v.getZ(), a.getZ())); - final FieldVector3D> vG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(v.getX(), a.getX(), zero), - new FieldUnivariateDerivative2<>(v.getY(), a.getY(), zero), - new FieldUnivariateDerivative2<>(v.getZ(), a.getZ(), zero)); + final FieldVector3D> vG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(v.getX(), a.getX(), getZero()), + new FieldUnivariateDerivative2<>(v.getY(), a.getY(), getZero()), + new FieldUnivariateDerivative2<>(v.getZ(), a.getZ(), getZero())); return new FieldPVCoordinates<>(pG, vG); } /** {@inheritDoc} */ public T getA() { // lazy evaluation of semi-major axis - final T r = getPVCoordinates().getPosition().getNorm(); - final T V2 = getPVCoordinates().getVelocity().getNormSq(); + final FieldPVCoordinates pva = getPVCoordinates(); + final T r = pva.getPosition().getNorm(); + final T V2 = pva.getVelocity().getNormSq(); return r.divide(r.negate().multiply(V2).divide(getMu()).add(2)); } @@ -220,10 +232,11 @@ public T getADot() { /** {@inheritDoc} */ public T getE() { final T muA = getA().multiply(getMu()); - if (muA.getReal() > 0) { + if (isElliptical()) { // elliptic or circular orbit - final FieldVector3D pvP = getPVCoordinates().getPosition(); - final FieldVector3D pvV = getPVCoordinates().getVelocity(); + final FieldPVCoordinates pva = getPVCoordinates(); + final FieldVector3D pvP = pva.getPosition(); + final FieldVector3D pvV = pva.getVelocity(); final T rV2OnMu = pvP.getNorm().multiply(pvV.getNormSq()).divide(getMu()); final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt()); final T eCE = rV2OnMu.subtract(1); @@ -254,7 +267,7 @@ public T getEDot() { /** {@inheritDoc} */ public T getI() { - return FieldVector3D.angle(new FieldVector3D<>(zero, zero, one), getPVCoordinates().getMomentum()); + return FieldVector3D.angle(new FieldVector3D<>(getZero(), getZero(), getOne()), getPVCoordinates().getMomentum()); } /** {@inheritDoc} */ @@ -302,7 +315,7 @@ public T getHx() { final double y = w.getY().getReal(); final double z = w.getZ().getReal(); if ((x * x + y * y) == 0 && z < 0) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } return w.getY().negate().divide(w.getZ().add(1)); } @@ -318,7 +331,7 @@ public T getHxDot() { final double y = w.getY().getValue().getReal(); final double z = w.getZ().getValue().getReal(); if ((x * x + y * y) == 0 && z < 0) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } final FieldUnivariateDerivative2 hx = w.getY().negate().divide(w.getZ().add(1)); return hx.getDerivative(1); @@ -335,7 +348,7 @@ public T getHy() { final double y = w.getY().getReal(); final double z = w.getZ().getReal(); if ((x * x + y * y) == 0 && z < 0) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } return w.getX().divide(w.getZ().add(1)); } @@ -351,7 +364,7 @@ public T getHyDot() { final double y = w.getY().getValue().getReal(); final double z = w.getZ().getValue().getReal(); if ((x * x + y * y) == 0 && z < 0) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } final FieldUnivariateDerivative2 hy = w.getX().divide(w.getZ().add(1)); return hy.getDerivative(1); @@ -401,6 +414,12 @@ public boolean hasDerivatives() { return hasNonKeplerianAcceleration; } + /** {@inheritDoc} */ + protected FieldVector3D initPosition() { + // nothing to do here, as the canonical elements are already the Cartesian ones + return getPVCoordinates().getPosition(); + } + /** {@inheritDoc} */ protected TimeStampedFieldPVCoordinates initPVCoordinates() { // nothing to do here, as the canonical elements are already the Cartesian ones @@ -409,47 +428,22 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public FieldCartesianOrbit shiftedBy(final double dt) { - return shiftedBy(getDate().getField().getZero().add(dt)); + return shiftedBy(getZero().add(dt)); } /** {@inheritDoc} */ public FieldCartesianOrbit shiftedBy(final T dt) { - final FieldPVCoordinates shiftedPV = (getA().getReal() < 0) ? shiftPVHyperbolic(dt) : shiftPVElliptic(dt); + final FieldPVCoordinates shiftedPV = isElliptical() ? shiftPVElliptic(dt) : shiftPVHyperbolic(dt); return new FieldCartesianOrbit<>(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu()); } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public FieldCartesianOrbit interpolate(final FieldAbsoluteDate date, final Stream> sample) { - final TimeStampedFieldPVCoordinates interpolated = - TimeStampedFieldPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_PVA, - sample.map(orbit -> orbit.getPVCoordinates())); - return new FieldCartesianOrbit<>(interpolated, getFrame(), date, getMu()); - } - /** Compute shifted position and velocity in elliptic case. * @param dt time shift * @return shifted position and velocity */ private FieldPVCoordinates shiftPVElliptic(final T dt) { + // preliminary computation0 final FieldPVCoordinates pva = getPVCoordinates(); final FieldVector3D pvP = pva.getPosition(); final FieldVector3D pvV = pva.getVelocity(); @@ -496,18 +490,18 @@ private FieldPVCoordinates shiftPVElliptic(final T dt) { if (hasNonKeplerianAcceleration) { // extract non-Keplerian part of the initial acceleration - final FieldVector3D nonKeplerianAcceleration = new FieldVector3D<>(one, getPVCoordinates().getAcceleration(), + final FieldVector3D nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(), r.multiply(r2).reciprocal().multiply(getMu()), pvP); // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion - final FieldVector3D fixedP = new FieldVector3D<>(one, shiftedP, + final FieldVector3D fixedP = new FieldVector3D<>(getOne(), shiftedP, dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); - final FieldVector3D fixedV = new FieldVector3D<>(one, shiftedV, + final FieldVector3D fixedV = new FieldVector3D<>(getOne(), shiftedV, dt, nonKeplerianAcceleration); final FieldVector3D fixedA = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP, - one, nonKeplerianAcceleration); + getOne(), nonKeplerianAcceleration); return new FieldPVCoordinates<>(fixedP, fixedV, fixedA); @@ -534,7 +528,7 @@ private FieldPVCoordinates shiftPVHyperbolic(final T dt) { final T rV2OnMu = r.multiply(pvV.getNormSq()).divide(getMu()); final T a = getA(); final T muA = a.multiply(getMu()); - final T e = one.subtract(FieldVector3D.dotProduct(pvM, pvM).divide(muA)).sqrt(); + final T e = getOne().subtract(FieldVector3D.dotProduct(pvM, pvM).divide(muA)).sqrt(); final T sqrt = e.add(1).divide(e.subtract(1)).sqrt(); // compute mean anomaly @@ -569,18 +563,18 @@ private FieldPVCoordinates shiftPVHyperbolic(final T dt) { if (hasNonKeplerianAcceleration) { // extract non-Keplerian part of the initial acceleration - final FieldVector3D nonKeplerianAcceleration = new FieldVector3D<>(one, getPVCoordinates().getAcceleration(), + final FieldVector3D nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(), r.multiply(r2).reciprocal().multiply(getMu()), pvP); // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion - final FieldVector3D fixedP = new FieldVector3D<>(one, shiftedP, + final FieldVector3D fixedP = new FieldVector3D<>(getOne(), shiftedP, dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); - final FieldVector3D fixedV = new FieldVector3D<>(one, shiftedV, + final FieldVector3D fixedV = new FieldVector3D<>(getOne(), shiftedV, dt, nonKeplerianAcceleration); final FieldVector3D fixedA = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP, - one, nonKeplerianAcceleration); + getOne(), nonKeplerianAcceleration); return new FieldPVCoordinates<>(fixedP, fixedV, fixedA); @@ -597,10 +591,10 @@ private FieldPVCoordinates shiftPVHyperbolic(final T dt) { */ private T[][] create6x6Identity() { // this is the fastest way to set the 6x6 identity matrix - final T[][] identity = MathArrays.buildArray(field, 6, 6); + final T[][] identity = MathArrays.buildArray(getField(), 6, 6); for (int i = 0; i < 6; i++) { - Arrays.fill(identity[i], zero); - identity[i][i] = one; + Arrays.fill(identity[i], getZero()); + identity[i][i] = getOne(); } return identity; } @@ -621,7 +615,7 @@ protected T[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final T gm, + public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { final FieldPVCoordinates pv = getPVCoordinates(); diff --git a/src/main/java/org/orekit/orbits/FieldCircularOrbit.java b/src/main/java/org/orekit/orbits/FieldCircularOrbit.java index fe189c0285..cc81382f79 100644 --- a/src/main/java/org/orekit/orbits/FieldCircularOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldCircularOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,18 +16,13 @@ */ package org.orekit.orbits; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathArrays; -import org.hipparchus.util.MathUtils; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; @@ -74,10 +69,10 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @since 9.0 + * @param type of the field elements */ -public class FieldCircularOrbit> - extends FieldOrbit { +public class FieldCircularOrbit> extends FieldOrbit implements PositionAngleBased { /** Semi-major axis (m). */ private final T a; @@ -118,12 +113,6 @@ public class FieldCircularOrbit> /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private FieldPVCoordinates partialPV; - /** one. */ - private final T one; - - /** zero. */ - private final T zero; - /** Creates a new instance. * @param a semi-major axis (m) * @param ex e cos(ω), first component of circular eccentricity vector @@ -141,7 +130,7 @@ public class FieldCircularOrbit> */ public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan, - final T alpha, final PositionAngle type, + final T alpha, final PositionAngleType type, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { this(a, ex, ey, i, raan, alpha, @@ -174,7 +163,7 @@ public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan, final T alpha, final T aDot, final T exDot, final T eyDot, final T iDot, final T raanDot, final T alphaDot, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { super(frame, date, mu); @@ -194,9 +183,6 @@ public FieldCircularOrbit(final T a, final T ex, final T ey, this.raan = raan; this.raanDot = raanDot; - one = a.getField().getOne(); - zero = a.getField().getZero(); - if (hasDerivatives()) { final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); @@ -266,16 +252,13 @@ public FieldCircularOrbit(final TimeStampedFieldPVCoordinates pvCoordinates, final T V2 = pvV.getNormSq(); final T rV2OnMu = r.multiply(V2).divide(mu); - zero = r.getField().getZero(); - one = r.getField().getOne(); + a = r.divide(rV2OnMu.negate().add(2)); - if (rV2OnMu.getReal() > 2) { + if (!isElliptical()) { throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); } - a = r.divide(rV2OnMu.negate().add(2)); - // compute inclination final FieldVector3D momentum = pvCoordinates.getMomentum(); final FieldVector3D plusK = FieldVector3D.getPlusK(r.getField()); @@ -316,7 +299,7 @@ public FieldCircularOrbit(final TimeStampedFieldPVCoordinates pvCoordinates, // we have a relevant acceleration, we can compute derivatives final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6); - getJacobianWrtCartesian(PositionAngle.MEAN, jacobian); + getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian); final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP); final FieldVector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration); @@ -393,7 +376,7 @@ public FieldCircularOrbit(final FieldOrbit op) { final T equiEy = op.getEquinoctialEy(); ex = equiEx.multiply(cosRaan).add(equiEy.multiply(sinRaan)); ey = equiEy.multiply(cosRaan).subtract(equiEx.multiply(sinRaan)); - this.alphaV = op.getLv().subtract(raan); + alphaV = op.getLv().subtract(raan); if (op.hasDerivatives()) { aDot = op.getADot(); @@ -419,11 +402,54 @@ public FieldCircularOrbit(final FieldOrbit op) { partialPV = null; - one = a.getField().getOne(); - zero = a.getField().getZero(); + } + + /** Constructor from Field and CircularOrbit. + *

        Build a FieldCircularOrbit from non-Field CircularOrbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldCircularOrbit(final Field field, final CircularOrbit op) { + super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu())); + + a = getZero().add(op.getA()); + i = getZero().add(op.getI()); + raan = getZero().add(op.getRightAscensionOfAscendingNode()); + ex = getZero().add(op.getCircularEx()); + ey = getZero().add(op.getCircularEy()); + alphaV = getZero().add(op.getAlphaV()); + + if (op.hasDerivatives()) { + aDot = getZero().add(op.getADot()); + iDot = getZero().add(op.getIDot()); + raanDot = getZero().add(op.getRightAscensionOfAscendingNodeDot()); + exDot = getZero().add(op.getCircularExDot()); + eyDot = getZero().add(op.getCircularEyDot()); + alphaVDot = getZero().add(op.getAlphaVDot()); + } else { + aDot = null; + exDot = null; + eyDot = null; + iDot = null; + raanDot = null; + alphaVDot = null; + } + + partialPV = null; } + /** Constructor from Field and Orbit. + *

        Build a FieldCircularOrbit from any non-Field Orbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldCircularOrbit(final Field field, final Orbit op) { + this(field, new CircularOrbit(op)); + } + /** {@inheritDoc} */ public OrbitType getType() { return OrbitType.CIRCULAR; @@ -509,7 +535,7 @@ public T getCircularEyDot() { public T getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } return raan.cos().multiply(i.divide(2).tan()); } @@ -523,7 +549,7 @@ public T getHxDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } final FieldSinCos sc = FastMath.sinCos(raan); @@ -537,7 +563,7 @@ public T getHxDot() { public T getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } return raan.sin().multiply(i.divide(2).tan()); } @@ -551,7 +577,7 @@ public T getHyDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return zero.add(Double.NaN); + return getZero().add(Double.NaN); } final FieldSinCos sc = FastMath.sinCos(raan); @@ -627,9 +653,9 @@ public T getAlphaMDot() { * @param type type of the angle * @return latitude argument (rad) */ - public T getAlpha(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getAlphaM() : - ((type == PositionAngle.ECCENTRIC) ? getAlphaE() : + public T getAlpha(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getAlphaM() : + ((type == PositionAngleType.ECCENTRIC) ? getAlphaE() : getAlphaV()); } @@ -637,9 +663,9 @@ public T getAlpha(final PositionAngle type) { * @param type type of the angle * @return latitude argument derivative (rad/s) */ - public T getAlphaDot(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getAlphaMDot() : - ((type == PositionAngle.ECCENTRIC) ? getAlphaEDot() : + public T getAlphaDot(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getAlphaMDot() : + ((type == PositionAngleType.ECCENTRIC) ? getAlphaEDot() : getAlphaVDot()); } @@ -839,7 +865,7 @@ private void computePVWithoutA() { final T x = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx)); final T y = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy)); - final T factor = one.add(getMu()).divide(a).sqrt().divide(exCeyS.negate().add(1)); + final T factor = getOne().add(getMu()).divide(a).sqrt().divide(exCeyS.negate().add(1)); final T xdot = factor.multiply( beta.multiply(equEy).multiply(exCeyS).subtract(sLe )); final T ydot = factor.multiply( cLe.subtract(beta.multiply(equEx).multiply(exCeyS))); @@ -863,7 +889,7 @@ private void computePVWithoutA() { private FieldVector3D nonKeplerianAcceleration() { final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6); - getJacobianWrtParameters(PositionAngle.MEAN, dCdP); + getJacobianWrtParameters(PositionAngleType.MEAN, dCdP); final T nonKeplerianMeanMotion = getAlphaMDot().subtract(getKeplerianMeanMotion()); final T nonKeplerianAx = dCdP[3][0].multiply(aDot). @@ -889,6 +915,51 @@ private FieldVector3D nonKeplerianAcceleration() { } + /** {@inheritDoc} */ + protected FieldVector3D initPosition() { + // get equinoctial parameters + final T equEx = getEquinoctialEx(); + final T equEy = getEquinoctialEy(); + final T hx = getHx(); + final T hy = getHy(); + final T lE = getLE(); + // inclination-related intermediate parameters + final T hx2 = hx.multiply(hx); + final T hy2 = hy.multiply(hy); + final T factH = (hx2.add(1).add(hy2)).reciprocal(); + + // reference axes defining the orbital plane + final T ux = (hx2.add(1).subtract(hy2)).multiply(factH); + final T uy = hx.multiply(2).multiply(hy).multiply(factH); + final T uz = hy.multiply(-2).multiply(factH); + + final T vx = uy; + final T vy = (hy2.subtract(hx2).add(1)).multiply(factH); + final T vz = hx.multiply(factH).multiply(2); + + // eccentricity-related intermediate parameters + final T exey = equEx.multiply(equEy); + final T ex2 = equEx.multiply(equEx); + final T ey2 = equEy.multiply(equEy); + final T e2 = ex2.add(ey2); + final T eta = e2.negate().add(1).sqrt().add(1); + final T beta = eta.reciprocal(); + + // eccentric latitude argument + final FieldSinCos scLe = FastMath.sinCos(lE); + final T cLe = scLe.cos(); + final T sLe = scLe.sin(); + + // coordinates of position and velocity in the orbital plane + final T x = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx)); + final T y = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy)); + + return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)), + x.multiply(uy).add(y.multiply(vy)), + x.multiply(uz).add(y.multiply(vz))); + + } + /** {@inheritDoc} */ protected TimeStampedFieldPVCoordinates initPVCoordinates() { @@ -909,7 +980,7 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public FieldCircularOrbit shiftedBy(final double dt) { - return shiftedBy(getDate().getField().getZero().add(dt)); + return shiftedBy(getZero().add(dt)); } /** {@inheritDoc} */ @@ -918,7 +989,7 @@ public FieldCircularOrbit shiftedBy(final T dt) { // use Keplerian-only motion final FieldCircularOrbit keplerianShifted = new FieldCircularOrbit<>(a, ex, ey, i, raan, getAlphaM().add(getKeplerianMeanMotion().multiply(dt)), - PositionAngle.MEAN, getFrame(), + PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -928,15 +999,15 @@ PositionAngle.MEAN, getFrame(), // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); - final FieldVector3D fixedP = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(), + final FieldVector3D fixedP = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(), dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); - final FieldVector3D fixedV = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(), + final FieldVector3D fixedV = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(), dt, nonKeplerianAcceleration); final FieldVector3D fixedA = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()), keplerianShifted.partialPV.getPosition(), - one, nonKeplerianAcceleration); + getOne(), nonKeplerianAcceleration); // build a new orbit, taking non-Keplerian acceleration into account return new FieldCircularOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(), @@ -950,97 +1021,10 @@ PositionAngle.MEAN, getFrame(), } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on circular elements, without derivatives (which means the interpolation - * falls back to Lagrange interpolation only). - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public FieldCircularOrbit interpolate(final FieldAbsoluteDate date, final Stream> sample) { - - // first pass to check if derivatives are available throughout the sample - final List> list = sample.collect(Collectors.toList()); - boolean useDerivatives = true; - for (final FieldOrbit orbit : list) { - useDerivatives = useDerivatives && orbit.hasDerivatives(); - } - - // set up an interpolator - final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); - - // second pass to feed interpolator - FieldAbsoluteDate previousDate = null; - T previousRAAN = zero.add(Double.NaN); - T previousAlphaM = zero.add(Double.NaN); - for (final FieldOrbit orbit : list) { - final FieldCircularOrbit circ = (FieldCircularOrbit) OrbitType.CIRCULAR.convertType(orbit); - final T continuousRAAN; - final T continuousAlphaM; - if (previousDate == null) { - continuousRAAN = circ.getRightAscensionOfAscendingNode(); - continuousAlphaM = circ.getAlphaM(); - } else { - final T dt = circ.getDate().durationFrom(previousDate); - final T keplerAM = previousAlphaM .add(circ.getKeplerianMeanMotion().multiply(dt)); - continuousRAAN = MathUtils.normalizeAngle(circ.getRightAscensionOfAscendingNode(), previousRAAN); - continuousAlphaM = MathUtils.normalizeAngle(circ.getAlphaM(), keplerAM); - } - previousDate = circ.getDate(); - previousRAAN = continuousRAAN; - previousAlphaM = continuousAlphaM; - final T[] toAdd = MathArrays.buildArray(one.getField(), 6); - toAdd[0] = circ.getA(); - toAdd[1] = circ.getCircularEx(); - toAdd[2] = circ.getCircularEy(); - toAdd[3] = circ.getI(); - toAdd[4] = continuousRAAN; - toAdd[5] = continuousAlphaM; - if (useDerivatives) { - final T[] toAddDot = MathArrays.buildArray(one.getField(), 6); - toAddDot[0] = circ.getADot(); - toAddDot[1] = circ.getCircularExDot(); - toAddDot[2] = circ.getCircularEyDot(); - toAddDot[3] = circ.getIDot(); - toAddDot[4] = circ.getRightAscensionOfAscendingNodeDot(); - toAddDot[5] = circ.getAlphaMDot(); - interpolator.addSamplePoint(circ.getDate().durationFrom(date), - toAdd, toAddDot); - } else { - interpolator.addSamplePoint(circ.getDate().durationFrom(date), - toAdd); - } - } - - // interpolate - final T[][] interpolated = interpolator.derivatives(zero, 1); - - // build a new interpolated instance - return new FieldCircularOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2], - interpolated[0][3], interpolated[0][4], interpolated[0][5], - interpolated[1][0], interpolated[1][1], interpolated[1][2], - interpolated[1][3], interpolated[1][4], interpolated[1][5], - PositionAngle.MEAN, getFrame(), date, getMu()); - - } - /** {@inheritDoc} */ protected T[][] computeJacobianMeanWrtCartesian() { - final T[][] jacobian = MathArrays.buildArray(one.getField(), 6, 6); + final T[][] jacobian = MathArrays.buildArray(getOne().getField(), 6, 6); // compute various intermediate parameters computePVWithoutA(); @@ -1059,7 +1043,7 @@ protected T[][] computeJacobianMeanWrtCartesian() { final T v2 = velocity.getNormSq(); final T mu = getMu(); - final T oOsqrtMuA = one.divide(a.multiply(mu).sqrt()); + final T oOsqrtMuA = getOne().divide(a.multiply(mu).sqrt()); final T rOa = r.divide(a); final T aOr = a.divide(r); final T aOr2 = a.divide(r2); @@ -1092,27 +1076,27 @@ protected T[][] computeJacobianMeanWrtCartesian() { final T recip2 = recip.multiply(recip); final T recip2N = recip2.negate(); final FieldVector3D dwXP = new FieldVector3D<>(recip, - new FieldVector3D<>(zero, vz, vy.negate()), + new FieldVector3D<>(getZero(), vz, vy.negate()), recip2N.multiply(sinRaan).multiply(sinI), danP); final FieldVector3D dwYP = new FieldVector3D<>(recip, - new FieldVector3D<>(vz.negate(), zero, vx), + new FieldVector3D<>(vz.negate(), getZero(), vx), recip2.multiply(cosRaan).multiply(sinI), danP); final FieldVector3D dwZP = new FieldVector3D<>(recip, - new FieldVector3D<>(vy, vx.negate(), zero), + new FieldVector3D<>(vy, vx.negate(), getZero()), recip2N.multiply(cosI), danP); final FieldVector3D dwXV = new FieldVector3D<>(recip, - new FieldVector3D<>(zero, z.negate(), y), + new FieldVector3D<>(getZero(), z.negate(), y), recip2N.multiply(sinRaan).multiply(sinI), danV); final FieldVector3D dwYV = new FieldVector3D<>(recip, - new FieldVector3D<>(z, zero, x.negate()), + new FieldVector3D<>(z, getZero(), x.negate()), recip2.multiply(cosRaan).multiply(sinI), danV); final FieldVector3D dwZV = new FieldVector3D<>(recip, - new FieldVector3D<>(y.negate(), x, zero), + new FieldVector3D<>(y.negate(), x, getZero()), recip2N.multiply(cosI), danV); @@ -1133,7 +1117,7 @@ protected T[][] computeJacobianMeanWrtCartesian() { // du final FieldVector3D duP = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXP, cv.multiply(sinRaan).divide(sinI), dwYP, - one, new FieldVector3D<>(cosRaan, sinRaan, zero)); + getOne(), new FieldVector3D<>(cosRaan, sinRaan, getZero())); final FieldVector3D duV = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXV, cv.multiply(sinRaan).divide(sinI), dwYV); @@ -1141,7 +1125,7 @@ protected T[][] computeJacobianMeanWrtCartesian() { final FieldVector3D dvP = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXP, u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYP, cv, dwZP, - one, new FieldVector3D<>(sinRaan.negate().multiply(cosI), cosRaan.multiply(cosI), sinI)); + getOne(), new FieldVector3D<>(sinRaan.negate().multiply(cosI), cosRaan.multiply(cosI), sinI)); final FieldVector3D dvV = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXV, u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYV, cv, dwZV); @@ -1149,7 +1133,7 @@ protected T[][] computeJacobianMeanWrtCartesian() { final FieldVector3D dc1P = new FieldVector3D<>(aOr2.multiply(eSinE.multiply(eSinE).multiply(2).add(1).subtract(eCosE)).divide(r2), position, aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), velocity); final FieldVector3D dc1V = new FieldVector3D<>(aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), position, - zero.add(2).divide(mu), velocity); + getZero().add(2).divide(mu), velocity); final FieldVector3D dc2P = new FieldVector3D<>(aOr2.multiply(eSinE).multiply(eSinE.multiply(eSinE).subtract(e2.negate().add(1))).divide(r2.multiply(epsilon)), position, aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), velocity); final FieldVector3D dc2V = new FieldVector3D<>(aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), position, @@ -1161,10 +1145,10 @@ protected T[][] computeJacobianMeanWrtCartesian() { final FieldVector3D dexV = new FieldVector3D<>(u, dc1V, v, dc2V, cof1, duV, cof2, dvV); final FieldVector3D deyP = new FieldVector3D<>(v, dc1P, u.negate(), dc2P, cof1, dvP, cof2.negate(), duP); final FieldVector3D deyV = new FieldVector3D<>(v, dc1V, u.negate(), dc2V, cof1, dvV, cof2.negate(), duV); - fillHalfRow(one, dexP, jacobian[1], 0); - fillHalfRow(one, dexV, jacobian[1], 3); - fillHalfRow(one, deyP, jacobian[2], 0); - fillHalfRow(one, deyV, jacobian[2], 3); + fillHalfRow(getOne(), dexP, jacobian[1], 0); + fillHalfRow(getOne(), dexV, jacobian[1], 3); + fillHalfRow(getOne(), deyP, jacobian[2], 0); + fillHalfRow(getOne(), deyV, jacobian[2], 3); final T cle = u.divide(a).add(ex).subtract(eSinE.multiply(beta).multiply(ey)); final T sle = v.divide(a).add(ey).add(eSinE.multiply(beta).multiply(ex)); @@ -1210,7 +1194,7 @@ protected T[][] computeJacobianEccentricWrtCartesian() { final FieldSinCos scAe = FastMath.sinCos(alphaE); final T cosAe = scAe.cos(); final T sinAe = scAe.sin(); - final T aOr = one.divide(one.subtract(ex.multiply(cosAe)).subtract(ey.multiply(sinAe))); + final T aOr = getOne().divide(getOne().subtract(ex.multiply(cosAe)).subtract(ey.multiply(sinAe))); // update longitude row final T[] rowEx = jacobian[1]; @@ -1248,8 +1232,8 @@ protected T[][] computeJacobianTrueWrtCartesian() { final T eSinE = ex.multiply(sinAe).subtract(ey.multiply(cosAe)); final T ecosE = ex.multiply(cosAe).add(ey.multiply(sinAe)); final T e2 = ex.multiply(ex).add(ey.multiply(ey)); - final T epsilon = (one.subtract(e2)).sqrt(); - final T onePeps = one.add(epsilon); + final T epsilon = (getOne().subtract(e2)).sqrt(); + final T onePeps = getOne().add(epsilon); final T d = onePeps.subtract(ecosE); final T cT = (d.multiply(d).add(eSinE.multiply(eSinE))).divide(2); final T cE = ecosE.multiply(onePeps).subtract(e2); @@ -1271,7 +1255,7 @@ protected T[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final T gm, + public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { final T oMe2; final T ksi; @@ -1282,13 +1266,13 @@ public void addKeplerContribution(final PositionAngle type, final T gm, pDot[5] = pDot[5].add(n); break; case ECCENTRIC : - oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); - ksi = one.add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin())); + oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); + ksi = getOne().add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin())); pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2)); break; case TRUE : - oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); - ksi = one.add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin())); + oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); + ksi = getOne().add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin())); pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()))); break; default : @@ -1309,6 +1293,27 @@ public String toString() { append(";}").toString(); } + /** {@inheritDoc} */ + @Override + public PositionAngleType getCachedPositionAngleType() { + return PositionAngleType.TRUE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasRates() { + return hasDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public FieldCircularOrbit removeRates() { + final PositionAngleType positionAngleType = getCachedPositionAngleType(); + return new FieldCircularOrbit<>(getA(), getCircularEx(), getCircularEy(), + getI(), getRightAscensionOfAscendingNode(), getAlpha(positionAngleType), + positionAngleType, getFrame(), getDate(), getMu()); + } + /** {@inheritDoc} */ @Override public CircularOrbit toOrbit() { @@ -1317,12 +1322,12 @@ public CircularOrbit toOrbit() { i.getReal(), raan.getReal(), alphaV.getReal(), aDot.getReal(), exDot.getReal(), eyDot.getReal(), iDot.getReal(), raanDot.getReal(), alphaVDot.getReal(), - PositionAngle.TRUE, getFrame(), + PositionAngleType.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } else { return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(), i.getReal(), raan.getReal(), alphaV.getReal(), - PositionAngle.TRUE, getFrame(), + PositionAngleType.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } } diff --git a/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java b/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java index 7f25c7d7dc..e526fab34d 100644 --- a/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,19 +16,13 @@ */ package org.orekit.orbits; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathArrays; -import org.hipparchus.util.MathUtils; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; @@ -60,7 +54,8 @@ * nor circular. When orbit is either equatorial or circular, the equinoctial * parameters are still unambiguously defined whereas some Keplerian elements * (more precisely ω and Ω) become ambiguous. For this reason, equinoctial - * parameters are the recommended way to represent orbits. + * parameters are the recommended way to represent orbits. Note however than + * the present implementation does not handle non-elliptical cases. *

        *

        * The instance EquinoctialOrbit is guaranteed to be immutable. @@ -75,8 +70,10 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @since 9.0 + * @param type of the field elements */ -public class FieldEquinoctialOrbit> extends FieldOrbit { +public class FieldEquinoctialOrbit> extends FieldOrbit + implements PositionAngleBased { /** Semi-major axis (m). */ private final T a; @@ -117,15 +114,6 @@ public class FieldEquinoctialOrbit> extends Fi /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private FieldPVCoordinates partialPV; - /** Field used by this class.*/ - private Field field; - - /**Zero.*/ - private T zero; - - /**One.*/ - private T one; - /** Creates a new instance. * @param a semi-major axis (m) * @param ex e cos(ω + Ω), first component of eccentricity vector @@ -143,7 +131,7 @@ public class FieldEquinoctialOrbit> extends Fi */ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, final T hx, final T hy, final T l, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { this(a, ex, ey, hx, hy, l, @@ -176,13 +164,11 @@ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, final T hx, final T hy, final T l, final T aDot, final T exDot, final T eyDot, final T hxDot, final T hyDot, final T lDot, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { super(frame, date, mu); - field = a.getField(); - zero = field.getZero(); - one = field.getOne(); + if (ex.getReal() * ex.getReal() + ey.getReal() * ey.getReal() >= 1.0) { throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); @@ -259,10 +245,6 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate throws IllegalArgumentException { super(pvCoordinates, frame, mu); - field = pvCoordinates.getPosition().getX().getField(); - zero = field.getZero(); - one = field.getOne(); - // compute semi-major axis final FieldVector3D pvP = pvCoordinates.getPosition(); final FieldVector3D pvV = pvCoordinates.getVelocity(); @@ -272,14 +254,17 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate final T V2 = pvV.getNormSq(); final T rV2OnMu = r.multiply(V2).divide(mu); - if (rV2OnMu.getReal() > 2) { + // compute semi-major axis + a = r.divide(rV2OnMu.negate().add(2)); + + if (!isElliptical()) { throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); } // compute inclination vector final FieldVector3D w = pvCoordinates.getMomentum().normalize(); - final T d = one.divide(one.add(w.getZ())); + final T d = getOne().divide(getOne().add(w.getZ())); hx = d.negate().multiply(w.getY()); hy = d.multiply(w.getX()); @@ -288,10 +273,6 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r); lv = sLv.atan2(cLv); - - // compute semi-major axis - a = r.divide(rV2OnMu.negate().add(2)); - // compute eccentricity vector final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt()); final T eCE = rV2OnMu.subtract(1); @@ -307,7 +288,7 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate // we have a relevant acceleration, we can compute derivatives final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6); - getJacobianWrtCartesian(PositionAngle.MEAN, jacobian); + getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian); final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP); final FieldVector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration); @@ -384,10 +365,50 @@ public FieldEquinoctialOrbit(final FieldOrbit op) { hxDot = op.getHxDot(); hyDot = op.getHyDot(); lvDot = op.getLvDot(); + } + + /** Constructor from Field and EquinoctialOrbit. + *

        Build a FieldEquinoctialOrbit from non-Field EquinoctialOrbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldEquinoctialOrbit(final Field field, final EquinoctialOrbit op) { + super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu())); + + a = getZero().add(op.getA()); + ex = getZero().add(op.getEquinoctialEx()); + ey = getZero().add(op.getEquinoctialEy()); + hx = getZero().add(op.getHx()); + hy = getZero().add(op.getHy()); + lv = getZero().add(op.getLv()); + + if (op.hasDerivatives()) { + aDot = getZero().add(op.getADot()); + exDot = getZero().add(op.getEquinoctialExDot()); + eyDot = getZero().add(op.getEquinoctialEyDot()); + hxDot = getZero().add(op.getHxDot()); + hyDot = getZero().add(op.getHyDot()); + lvDot = getZero().add(op.getLvDot()); + } else { + aDot = null; + exDot = null; + eyDot = null; + hxDot = null; + hyDot = null; + lvDot = null; + } + + } - field = a.getField(); - zero = field.getZero(); - one = field.getOne(); + /** Constructor from Field and Orbit. + *

        Build a FieldEquinoctialOrbit from any non-Field Orbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldEquinoctialOrbit(final Field field, final Orbit op) { + this(field, new EquinoctialOrbit(op)); } /** {@inheritDoc} */ @@ -499,9 +520,9 @@ public T getLMDot() { * @param type type of the angle * @return longitude argument (rad) */ - public T getL(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getLM() : - ((type == PositionAngle.ECCENTRIC) ? getLE() : + public T getL(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getLM() : + ((type == PositionAngleType.ECCENTRIC) ? getLE() : getLv()); } @@ -509,9 +530,9 @@ public T getL(final PositionAngle type) { * @param type type of the angle * @return longitude argument derivative (rad/s) */ - public T getLDot(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getLMDot() : - ((type == PositionAngle.ECCENTRIC) ? getLEDot() : + public T getLDot(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getLMDot() : + ((type == PositionAngleType.ECCENTRIC) ? getLEDot() : getLvDot()); } @@ -650,7 +671,7 @@ private void computePVWithoutA() { // inclination-related intermediate parameters final T hx2 = hx.multiply(hx); final T hy2 = hy.multiply(hy); - final T factH = one.divide(hx2.add(1.0).add(hy2)); + final T factH = getOne().divide(hx2.add(1.0).add(hy2)); // reference axes defining the orbital plane final T ux = hx2.add(1.0).subtract(hy2).multiply(factH); @@ -666,8 +687,8 @@ private void computePVWithoutA() { final T exey = ex.multiply(ey); final T ey2 = ey.multiply(ey); final T e2 = ex2.add(ey2); - final T eta = one.subtract(e2).sqrt().add(1); - final T beta = one.divide(eta); + final T eta = getOne().subtract(e2).sqrt().add(1); + final T beta = getOne().divide(eta); // eccentric longitude argument final FieldSinCos scLe = FastMath.sinCos(lE); @@ -676,10 +697,10 @@ private void computePVWithoutA() { final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe)); // coordinates of position and velocity in the orbital plane - final T x = a.multiply(one.subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex)); - final T y = a.multiply(one.subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey)); + final T x = a.multiply(getOne().subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex)); + final T y = a.multiply(getOne().subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey)); - final T factor = getMu().divide(a).sqrt().divide(one.subtract(exCeyS)); + final T factor = getMu().divide(a).sqrt().divide(getOne().subtract(exCeyS)); final T xdot = factor.multiply(sLe.negate().add(beta.multiply(ey).multiply(exCeyS))); final T ydot = factor.multiply(cLe.subtract(beta.multiply(ex).multiply(exCeyS))); @@ -703,7 +724,7 @@ private void computePVWithoutA() { private FieldVector3D nonKeplerianAcceleration() { final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6); - getJacobianWrtParameters(PositionAngle.MEAN, dCdP); + getJacobianWrtParameters(PositionAngleType.MEAN, dCdP); final T nonKeplerianMeanMotion = getLMDot().subtract(getKeplerianMeanMotion()); final T nonKeplerianAx = dCdP[3][0].multiply(aDot). @@ -729,6 +750,49 @@ private FieldVector3D nonKeplerianAcceleration() { } + /** {@inheritDoc} */ + protected FieldVector3D initPosition() { + + // get equinoctial parameters + final T lE = getLE(); + + // inclination-related intermediate parameters + final T hx2 = hx.multiply(hx); + final T hy2 = hy.multiply(hy); + final T factH = getOne().divide(hx2.add(1.0).add(hy2)); + + // reference axes defining the orbital plane + final T ux = hx2.add(1.0).subtract(hy2).multiply(factH); + final T uy = hx.multiply(hy).multiply(factH).multiply(2); + final T uz = hy.multiply(-2).multiply(factH); + + final T vx = uy; + final T vy = (hy2.subtract(hx2).add(1)).multiply(factH); + final T vz = hx.multiply(factH).multiply(2); + + // eccentricity-related intermediate parameters + final T ex2 = ex.multiply(ex); + final T exey = ex.multiply(ey); + final T ey2 = ey.multiply(ey); + final T e2 = ex2.add(ey2); + final T eta = getOne().subtract(e2).sqrt().add(1); + final T beta = getOne().divide(eta); + + // eccentric longitude argument + final FieldSinCos scLe = FastMath.sinCos(lE); + final T cLe = scLe.cos(); + final T sLe = scLe.sin(); + + // coordinates of position and velocity in the orbital plane + final T x = a.multiply(getOne().subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex)); + final T y = a.multiply(getOne().subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey)); + + return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)), + x.multiply(uy).add(y.multiply(vy)), + x.multiply(uz).add(y.multiply(vz))); + + } + /** {@inheritDoc} */ protected TimeStampedFieldPVCoordinates initPVCoordinates() { @@ -749,7 +813,7 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public FieldEquinoctialOrbit shiftedBy(final double dt) { - return shiftedBy(getDate().getField().getZero().add(dt)); + return shiftedBy(getZero().add(dt)); } /** {@inheritDoc} */ @@ -758,7 +822,7 @@ public FieldEquinoctialOrbit shiftedBy(final T dt) { // use Keplerian-only motion final FieldEquinoctialOrbit keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy, getLM().add(getKeplerianMeanMotion().multiply(dt)), - PositionAngle.MEAN, getFrame(), + PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -768,15 +832,15 @@ PositionAngle.MEAN, getFrame(), // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); - final FieldVector3D fixedP = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(), + final FieldVector3D fixedP = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(), dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); - final FieldVector3D fixedV = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(), + final FieldVector3D fixedV = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(), dt, nonKeplerianAcceleration); final FieldVector3D fixedA = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()), keplerianShifted.partialPV.getPosition(), - one, nonKeplerianAcceleration); + getOne(), nonKeplerianAcceleration); // build a new orbit, taking non-Keplerian acceleration into account return new FieldEquinoctialOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(), @@ -790,92 +854,10 @@ PositionAngle.MEAN, getFrame(), } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on equinoctial elements, without derivatives (which means the interpolation - * falls back to Lagrange interpolation only). - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public FieldEquinoctialOrbit interpolate(final FieldAbsoluteDate date, final Stream> sample) { - - // first pass to check if derivatives are available throughout the sample - final List> list = sample.collect(Collectors.toList()); - boolean useDerivatives = true; - for (final FieldOrbit orbit : list) { - useDerivatives = useDerivatives && orbit.hasDerivatives(); - } - - // set up an interpolator - final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); - - // second pass to feed interpolator - FieldAbsoluteDate previousDate = null; - T previousLm = zero.add(Double.NaN); - for (final FieldOrbit orbit : list) { - final FieldEquinoctialOrbit equi = (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit); - final T continuousLm; - if (previousDate == null) { - continuousLm = (T) equi.getLM(); - } else { - final T dt = (T) equi.getDate().durationFrom(previousDate); - final T keplerLm = previousLm.add((T) equi.getKeplerianMeanMotion().multiply(dt)); - continuousLm = MathUtils.normalizeAngle((T) equi.getLM(), keplerLm); - } - previousDate = equi.getDate(); - previousLm = continuousLm; - final T[] toAdd = MathArrays.buildArray(field, 6); - toAdd[0] = (T) equi.getA(); - toAdd[1] = (T) equi.getEquinoctialEx(); - toAdd[2] = (T) equi.getEquinoctialEy(); - toAdd[3] = (T) equi.getHx(); - toAdd[4] = (T) equi.getHy(); - toAdd[5] = (T) continuousLm; - if (useDerivatives) { - final T[] toAddDot = MathArrays.buildArray(one.getField(), 6); - toAddDot[0] = equi.getADot(); - toAddDot[1] = equi.getEquinoctialExDot(); - toAddDot[2] = equi.getEquinoctialEyDot(); - toAddDot[3] = equi.getHxDot(); - toAddDot[4] = equi.getHyDot(); - toAddDot[5] = equi.getLMDot(); - interpolator.addSamplePoint(equi.getDate().durationFrom(date), - toAdd, toAddDot); - } else { - interpolator.addSamplePoint((T) equi.getDate().durationFrom(date), - toAdd); - } - } - - // interpolate - final T[][] interpolated = interpolator.derivatives(zero, 1); - - // build a new interpolated instance - return new FieldEquinoctialOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2], - interpolated[0][3], interpolated[0][4], interpolated[0][5], - interpolated[1][0], interpolated[1][1], interpolated[1][2], - interpolated[1][3], interpolated[1][4], interpolated[1][5], - PositionAngle.MEAN, getFrame(), date, getMu()); - - } - /** {@inheritDoc} */ protected T[][] computeJacobianMeanWrtCartesian() { - final T[][] jacobian = MathArrays.buildArray(field, 6, 6); + final T[][] jacobian = MathArrays.buildArray(getField(), 6, 6); // compute various intermediate parameters computePVWithoutA(); @@ -890,9 +872,9 @@ protected T[][] computeJacobianMeanWrtCartesian() { final T a2 = a.multiply(a); final T e2 = ex.multiply(ex).add(ey.multiply(ey)); - final T oMe2 = one.subtract(e2); + final T oMe2 = getOne().subtract(e2); final T epsilon = oMe2.sqrt(); - final T beta = one.divide(epsilon.add(1)); + final T beta = getOne().divide(epsilon.add(1)); final T ratio = epsilon.multiply(beta); final T hx2 = hx.multiply(hx); @@ -925,8 +907,8 @@ protected T[][] computeJacobianMeanWrtCartesian() { // da final FieldVector3D vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position); final FieldVector3D vectorARDot = new FieldVector3D<>(a2.multiply(2).divide(mu), velocity); - fillHalfRow(one, vectorAR, jacobian[0], 0); - fillHalfRow(one, vectorARDot, jacobian[0], 3); + fillHalfRow(getOne(), vectorAR, jacobian[0], 0); + fillHalfRow(getOne(), vectorARDot, jacobian[0], 3); // dEx final T d1 = a.negate().multiply(ratio).divide(r3); @@ -935,13 +917,13 @@ protected T[][] computeJacobianMeanWrtCartesian() { final FieldVector3D vectorExRDot = new FieldVector3D<>(x.multiply(2).multiply(yDot).subtract(xDot.multiply(y)).divide(mu), g, y.negate().multiply(yDot).divide(mu), f, ey.negate().multiply(d3).divide(epsilon), w); fillHalfRow(ex.multiply(d1), position, ey.negate().multiply(d2), w, epsilon.divide(sqrtMuA), drDotSdEy, jacobian[1], 0); - fillHalfRow(one, vectorExRDot, jacobian[1], 3); + fillHalfRow(getOne(), vectorExRDot, jacobian[1], 3); // dEy final FieldVector3D vectorEyRDot = new FieldVector3D<>(xDot.multiply(2).multiply(y).subtract(x.multiply(yDot)).divide(mu), f, x.negate().multiply(xDot).divide(mu), g, ex.multiply(d3).divide(epsilon), w); fillHalfRow(ey.multiply(d1), position, ex.multiply(d2), w, epsilon.negate().divide(sqrtMuA), drDotSdEx, jacobian[2], 0); - fillHalfRow(one, vectorEyRDot, jacobian[2], 3); + fillHalfRow(getOne(), vectorEyRDot, jacobian[2], 3); // dHx final T h = (hx2.add(1).add(hy2)).divide(sqrtMuA.multiply(2).multiply(epsilon)); @@ -954,8 +936,8 @@ protected T[][] computeJacobianMeanWrtCartesian() { // dLambdaM final T l = ratio.negate().divide(sqrtMuA); - fillHalfRow(one.negate().divide(sqrtMuA), velocity, d2, w, l.multiply(ex), drDotSdEx, l.multiply(ey), drDotSdEy, jacobian[5], 0); - fillHalfRow(zero.add(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3); + fillHalfRow(getOne().negate().divide(sqrtMuA), velocity, d2, w, l.multiply(ex), drDotSdEx, l.multiply(ey), drDotSdEy, jacobian[5], 0); + fillHalfRow(getZero().add(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3); return jacobian; @@ -974,7 +956,7 @@ protected T[][] computeJacobianEccentricWrtCartesian() { final FieldSinCos scLe = FastMath.sinCos(getLE()); final T cosLe = scLe.cos(); final T sinLe = scLe.sin(); - final T aOr = one.divide(one.subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe))); + final T aOr = getOne().divide(getOne().subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe))); // update longitude row final T[] rowEx = jacobian[1]; @@ -1012,7 +994,7 @@ protected T[][] computeJacobianTrueWrtCartesian() { final T eSinE = ex.multiply(sinLe).subtract(ey.multiply(cosLe)); final T ecosE = ex.multiply(cosLe).add(ey.multiply(sinLe)); final T e2 = ex.multiply(ex).add(ey.multiply(ey)); - final T epsilon = one.subtract(e2).sqrt(); + final T epsilon = getOne().subtract(e2).sqrt(); final T onePeps = epsilon.add(1); final T d = onePeps.subtract(ecosE); final T cT = d.multiply(d).add(eSinE.multiply(eSinE)).divide(2); @@ -1036,7 +1018,7 @@ protected T[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final T gm, + public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { final T oMe2; final T ksi; @@ -1047,12 +1029,12 @@ public void addKeplerContribution(final PositionAngle type, final T gm, pDot[5] = pDot[5].add(n); break; case ECCENTRIC : - oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); + oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); ksi = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin())); pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2)); break; case TRUE : - oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); + oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); ksi = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin())); pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()))); break; @@ -1073,6 +1055,26 @@ public String toString() { append(";}").toString(); } + /** {@inheritDoc} */ + @Override + public PositionAngleType getCachedPositionAngleType() { + return PositionAngleType.TRUE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasRates() { + return hasDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public FieldEquinoctialOrbit removeRates() { + final PositionAngleType positionAngleType = getCachedPositionAngleType(); + return new FieldEquinoctialOrbit<>(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(), + getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + } + /** {@inheritDoc} */ @Override public EquinoctialOrbit toOrbit() { @@ -1081,12 +1083,12 @@ public EquinoctialOrbit toOrbit() { hx.getReal(), hy.getReal(), lv.getReal(), aDot.getReal(), exDot.getReal(), eyDot.getReal(), hxDot.getReal(), hyDot.getReal(), lvDot.getReal(), - PositionAngle.TRUE, getFrame(), + PositionAngleType.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } else { return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(), hx.getReal(), hy.getReal(), lv.getReal(), - PositionAngle.TRUE, getFrame(), + PositionAngleType.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } } diff --git a/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java b/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java index f189d270a3..9f04bdf30b 100644 --- a/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java +++ b/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -47,7 +47,9 @@ public class FieldKeplerianAnomalyUtility { B = k3 * k3 / (6 * k1); } + /** Private constructor for utility class. */ private FieldKeplerianAnomalyUtility() { + // Nothing to do } /** diff --git a/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java b/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java index ce98f23e56..1945680ced 100644 --- a/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,18 +17,15 @@ package org.orekit.orbits; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.lang.reflect.Array; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathArrays; -import org.hipparchus.util.MathUtils; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitInternalError; @@ -78,8 +75,10 @@ * @author Véronique Pommier-Maurussane * @author Andrea Antolino * @since 9.0 + * @param type of the field elements */ -public class FieldKeplerianOrbit> extends FieldOrbit { +public class FieldKeplerianOrbit> extends FieldOrbit + implements PositionAngleBased { /** Name of the eccentricity parameter. */ private static final String ECCENTRICITY = "eccentricity"; @@ -123,13 +122,7 @@ public class FieldKeplerianOrbit> extends Fiel /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private FieldPVCoordinates partialPV; - /** Identity element. */ - private final T one; - - /** Zero element. */ - private final T zero; - - /** Third Canonical Vector. */ + /** PThird Canonical Vector. */ private final FieldVector3D PLUS_K; /** Creates a new instance. @@ -150,7 +143,7 @@ public class FieldKeplerianOrbit> extends Fiel */ public FieldKeplerianOrbit(final T a, final T e, final T i, final T pa, final T raan, - final T anomaly, final PositionAngle type, + final T anomaly, final PositionAngleType type, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { this(a, e, i, pa, raan, anomaly, @@ -184,7 +177,7 @@ public FieldKeplerianOrbit(final T a, final T e, final T i, final T pa, final T raan, final T anomaly, final T aDot, final T eDot, final T iDot, final T paDot, final T raanDot, final T anomalyDot, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { super(frame, date, mu); @@ -206,14 +199,7 @@ public FieldKeplerianOrbit(final T a, final T e, final T i, this.raan = raan; this.raanDot = raanDot; - /** Identity element. */ - this.one = a.getField().getOne(); - - /** Zero element. */ - this.zero = a.getField().getZero(); - - /**Third canonical vector. */ - this.PLUS_K = FieldVector3D.getPlusK(a.getField()); + this.PLUS_K = FieldVector3D.getPlusK(a.getField()); // third canonical vector if (hasDerivatives()) { final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); @@ -315,14 +301,8 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates super(pvCoordinates, frame, mu); - // identity element - this.one = pvCoordinates.getPosition().getX().getField().getOne(); - - // zero element - this.zero = one.getField().getZero(); - // third canonical vector - this.PLUS_K = FieldVector3D.getPlusK(one.getField()); + this.PLUS_K = FieldVector3D.getPlusK(getOne().getField()); // compute inclination final FieldVector3D momentum = pvCoordinates.getMomentum(); @@ -346,7 +326,7 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates final T muA = a.multiply(mu); // compute true anomaly - if (a.getReal() > 0) { + if (isElliptical()) { // elliptic or circular orbit final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt()); final T eCE = rV2OnMu.subtract(1); @@ -364,7 +344,7 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates checkParameterRangeInclusive(ECCENTRICITY, e.getReal(), 0.0, Double.POSITIVE_INFINITY); // compute perigee argument - final FieldVector3D node = new FieldVector3D<>(raan, zero); + final FieldVector3D node = new FieldVector3D<>(raan, getZero()); final T px = FieldVector3D.dotProduct(pvP, node); final T py = FieldVector3D.dotProduct(pvP, FieldVector3D.crossProduct(momentum, node)).divide(m2.sqrt()); pa = py.atan2(px).subtract(v); @@ -375,7 +355,7 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates // we have a relevant acceleration, we can compute derivatives final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6); - getJacobianWrtCartesian(PositionAngle.MEAN, jacobian); + getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian); final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP); final FieldVector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration); @@ -441,6 +421,35 @@ public FieldKeplerianOrbit(final FieldOrbit op) { this(op.getPVCoordinates(), op.getFrame(), op.getMu(), op.hasDerivatives()); } + /** Constructor from Field and KeplerianOrbit. + *

        Build a FieldKeplerianOrbit from non-Field KeplerianOrbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldKeplerianOrbit(final Field field, final KeplerianOrbit op) { + this(field.getZero().add(op.getA()), field.getZero().add(op.getE()), field.getZero().add(op.getI()), + field.getZero().add(op.getPerigeeArgument()), field.getZero().add(op.getRightAscensionOfAscendingNode()), + field.getZero().add(op.getTrueAnomaly()), + (op.hasDerivatives()) ? field.getZero().add(op.getADot()) : null, + (op.hasDerivatives()) ? field.getZero().add(op.getEDot()) : null, + (op.hasDerivatives()) ? field.getZero().add(op.getIDot()) : null, + (op.hasDerivatives()) ? field.getZero().add(op.getPerigeeArgumentDot()) : null, + (op.hasDerivatives()) ? field.getZero().add(op.getRightAscensionOfAscendingNodeDot()) : null, + (op.hasDerivatives()) ? field.getZero().add(op.getTrueAnomalyDot()) : null, PositionAngleType.TRUE, + op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu())); + } + + /** Constructor from Field and Orbit. + *

        Build a FieldKeplerianOrbit from any non-Field Orbit.

        + * @param field CalculusField to base object on + * @param op non-field orbit with only "constant" terms + * @since 12.0 + */ + public FieldKeplerianOrbit(final Field field, final Orbit op) { + this(field, new KeplerianOrbit(op)); + } + /** {@inheritDoc} */ public OrbitType getType() { return OrbitType.KEPLERIAN; @@ -591,9 +600,9 @@ public T getMeanAnomalyDot() { * @param type type of the angle * @return anomaly (rad) */ - public T getAnomaly(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getMeanAnomaly() : - ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomaly() : + public T getAnomaly(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getMeanAnomaly() : + ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomaly() : getTrueAnomaly()); } @@ -604,9 +613,9 @@ public T getAnomaly(final PositionAngle type) { * @param type type of the angle * @return anomaly derivative (rad/s) */ - public T getAnomalyDot(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getMeanAnomalyDot() : - ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomalyDot() : + public T getAnomalyDot(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getMeanAnomalyDot() : + ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomalyDot() : getTrueAnomalyDot()); } @@ -616,102 +625,6 @@ public boolean hasDerivatives() { return aDot != null; } - /** Computes the true anomaly from the elliptic eccentric anomaly. - * @param E eccentric anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return v the true anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#ellipticEccentricToTrue(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T ellipticEccentricToTrue(final T E, final T e) { - return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, E); - } - - /** Computes the elliptic eccentric anomaly from the true anomaly. - * @param v true anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return E the elliptic eccentric anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#ellipticTrueToEccentric(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T trueToEllipticEccentric(final T v, final T e) { - return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(e, v); - } - - /** Computes the true anomaly from the hyperbolic eccentric anomaly. - * @param H hyperbolic eccentric anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return v the true anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#hyperbolicEccentricToTrue(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T hyperbolicEccentricToTrue(final T H, final T e) { - return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, H); - } - - /** Computes the hyperbolic eccentric anomaly from the true anomaly. - * @param v true anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return H the hyperbolic eccentric anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#hyperbolicTrueToEccentric(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T trueToHyperbolicEccentric(final T v, final T e) { - return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, v); - } - - /** Computes the mean anomaly from the hyperbolic eccentric anomaly. - * @param H hyperbolic eccentric anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return M the mean anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#hyperbolicEccentricToMean(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T hyperbolicEccentricToMean(final T H, final T e) { - return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(e, H); - } - - /** Computes the elliptic eccentric anomaly from the mean anomaly. - * @param M mean anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return E the eccentric anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#ellipticMeanToEccentric(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T meanToEllipticEccentric(final T M, final T e) { - return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, M); - } - - /** Computes the hyperbolic eccentric anomaly from the mean anomaly. - * @param M mean anomaly (rad) - * @param e eccentricity - * @param Type of the field elements - * @return H the hyperbolic eccentric anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#hyperbolicMeanToEccentric(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T meanToHyperbolicEccentric(final T M, final T e) { - return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M); - } - - /** Computes the mean anomaly from the elliptic eccentric anomaly. - * @param E eccentric anomaly (rad) - * @param e eccentricity - * @param type of the field elements - * @return M the mean anomaly - * @deprecated As of 11.3, replaced by - * {@link FieldKeplerianAnomalyUtility#ellipticEccentricToMean(CalculusFieldElement, CalculusFieldElement)}. - */ - public static > T ellipticEccentricToMean(final T E, final T e) { - return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(e, E); - } - /** {@inheritDoc} */ public T getEquinoctialEx() { return e.multiply(pa.add(raan).cos()); @@ -754,7 +667,7 @@ public T getEquinoctialEyDot() { public T getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return this.zero.add(Double.NaN); + return getZero().add(Double.NaN); } return raan.cos().multiply(i.divide(2).tan()); } @@ -768,7 +681,7 @@ public T getHxDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return this.zero.add(Double.NaN); + return getZero().add(Double.NaN); } final FieldUnivariateDerivative1 iUD = new FieldUnivariateDerivative1<>(i, iDot); @@ -781,7 +694,7 @@ public T getHxDot() { public T getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return this.zero.add(Double.NaN); + return getZero().add(Double.NaN); } return raan.sin().multiply(i.divide(2).tan()); } @@ -795,7 +708,7 @@ public T getHyDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { - return this.zero.add(Double.NaN); + return getZero().add(Double.NaN); } final FieldUnivariateDerivative1 iUD = new FieldUnivariateDerivative1<>(i, iDot); @@ -840,14 +753,11 @@ public T getLMDot() { null; } - /** Compute position and velocity but not acceleration. + /** Compute reference axes. + * @return referecne axes + * @since 12.0 */ - private void computePVWithoutA() { - - if (partialPV != null) { - // already computed - return; - } + private FieldVector3D[] referenceAxes() { // preliminary variables final FieldSinCos scRaan = FastMath.sinCos(raan); @@ -865,10 +775,27 @@ private void computePVWithoutA() { final T srsp = sinRaan.multiply(sinPa); // reference axes defining the orbital plane - final FieldVector3D p = new FieldVector3D<>(crcp.subtract(cosI.multiply(srsp)), srcp.add(cosI.multiply(crsp)), sinI.multiply(sinPa)); - final FieldVector3D q = new FieldVector3D<>(crsp.add(cosI.multiply(srcp)).negate(), cosI.multiply(crcp).subtract(srsp), sinI.multiply(cosPa)); + @SuppressWarnings("unchecked") + final FieldVector3D[] axes = (FieldVector3D[]) Array.newInstance(FieldVector3D.class, 2); + axes[0] = new FieldVector3D<>(crcp.subtract(cosI.multiply(srsp)), srcp.add(cosI.multiply(crsp)), sinI.multiply(sinPa)); + axes[1] = new FieldVector3D<>(crsp.add(cosI.multiply(srcp)).negate(), cosI.multiply(crcp).subtract(srsp), sinI.multiply(cosPa)); + + return axes; + + } + + /** Compute position and velocity but not acceleration. + */ + private void computePVWithoutA() { + + if (partialPV != null) { + // already computed + return; + } + + final FieldVector3D[] axes = referenceAxes(); - if (a.getReal() > 0) { + if (isElliptical()) { // elliptical case @@ -886,8 +813,8 @@ private void computePVWithoutA() { final T xDot = sinE.negate().multiply(factor); final T yDot = cosE.multiply(s1Me2).multiply(factor); - final FieldVector3D position = new FieldVector3D<>(x, p, y, q); - final FieldVector3D velocity = new FieldVector3D<>(xDot, p, yDot, q); + final FieldVector3D position = new FieldVector3D<>(x, axes[0], y, axes[1]); + final FieldVector3D velocity = new FieldVector3D<>(xDot, axes[0], yDot, axes[1]); partialPV = new FieldPVCoordinates<>(position, velocity); } else { @@ -902,8 +829,8 @@ private void computePVWithoutA() { final T posFactor = f.divide(e.multiply(cosV).add(1)); final T velFactor = FastMath.sqrt(getMu().divide(f)); - final FieldVector3D position = new FieldVector3D<>(posFactor.multiply(cosV), p, posFactor.multiply(sinV), q); - final FieldVector3D velocity = new FieldVector3D<>(velFactor.multiply(sinV).negate(), p, velFactor.multiply(e.add(cosV)), q); + final FieldVector3D position = new FieldVector3D<>(posFactor.multiply(cosV), axes[0], posFactor.multiply(sinV), axes[1]); + final FieldVector3D velocity = new FieldVector3D<>(velFactor.multiply(sinV).negate(), axes[0], velFactor.multiply(e.add(cosV)), axes[1]); partialPV = new FieldPVCoordinates<>(position, velocity); } @@ -919,7 +846,7 @@ private void computePVWithoutA() { private FieldVector3D nonKeplerianAcceleration() { final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6); - getJacobianWrtParameters(PositionAngle.MEAN, dCdP); + getJacobianWrtParameters(PositionAngleType.MEAN, dCdP); final T nonKeplerianMeanMotion = getMeanAnomalyDot().subtract(getKeplerianMeanMotion()); final T nonKeplerianAx = dCdP[3][0].multiply(aDot). @@ -945,6 +872,40 @@ private FieldVector3D nonKeplerianAcceleration() { } + /** {@inheritDoc} */ + protected FieldVector3D initPosition() { + final FieldVector3D[] axes = referenceAxes(); + + if (isElliptical()) { + + // elliptical case + + // elliptic eccentric anomaly + final T uME2 = e.negate().add(1).multiply(e.add(1)); + final T s1Me2 = uME2.sqrt(); + final FieldSinCos scE = FastMath.sinCos(getEccentricAnomaly()); + final T cosE = scE.cos(); + final T sinE = scE.sin(); + + return new FieldVector3D<>(a.multiply(cosE.subtract(e)), axes[0], a.multiply(sinE).multiply(s1Me2), axes[1]); + + } else { + + // hyperbolic case + + // compute position and velocity factors + final FieldSinCos scV = FastMath.sinCos(v); + final T sinV = scV.sin(); + final T cosV = scV.cos(); + final T f = a.multiply(e.multiply(e).negate().add(1)); + final T posFactor = f.divide(e.multiply(cosV).add(1)); + + return new FieldVector3D<>(posFactor.multiply(cosV), axes[0], posFactor.multiply(sinV), axes[1]); + + } + + } + /** {@inheritDoc} */ protected TimeStampedFieldPVCoordinates initPVCoordinates() { @@ -965,7 +926,7 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public FieldKeplerianOrbit shiftedBy(final double dt) { - return shiftedBy(getDate().getField().getZero().add(dt)); + return shiftedBy(getZero().add(dt)); } /** {@inheritDoc} */ @@ -974,7 +935,7 @@ public FieldKeplerianOrbit shiftedBy(final T dt) { // use Keplerian-only motion final FieldKeplerianOrbit keplerianShifted = new FieldKeplerianOrbit<>(a, e, i, pa, raan, getKeplerianMeanMotion().multiply(dt).add(getMeanAnomaly()), - PositionAngle.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); + PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -983,15 +944,15 @@ public FieldKeplerianOrbit shiftedBy(final T dt) { // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); - final FieldVector3D fixedP = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(), + final FieldVector3D fixedP = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(), dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); - final FieldVector3D fixedV = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(), + final FieldVector3D fixedV = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(), dt, nonKeplerianAcceleration); final FieldVector3D fixedA = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()), keplerianShifted.partialPV.getPosition(), - one, nonKeplerianAcceleration); + getOne(), nonKeplerianAcceleration); // build a new orbit, taking non-Keplerian acceleration into account return new FieldKeplerianOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(), @@ -1005,101 +966,9 @@ public FieldKeplerianOrbit shiftedBy(final T dt) { } - /** {@inheritDoc} - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on Keplerian elements, without derivatives (which means the interpolation - * falls back to Lagrange interpolation only). - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - *

        - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

        - */ - public FieldKeplerianOrbit interpolate(final FieldAbsoluteDate date, final Stream> sample) { - - // first pass to check if derivatives are available throughout the sample - final List> list = sample.collect(Collectors.toList()); - boolean useDerivatives = true; - for (final FieldOrbit orbit : list) { - useDerivatives = useDerivatives && orbit.hasDerivatives(); - } - - // set up an interpolator - final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); - - // second pass to feed interpolator - FieldAbsoluteDate previousDate = null; - T previousPA = zero.add(Double.NaN); - T previousRAAN = zero.add(Double.NaN); - T previousM = zero.add(Double.NaN); - for (final FieldOrbit orbit : list) { - final FieldKeplerianOrbit kep = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); - final T continuousPA; - final T continuousRAAN; - final T continuousM; - if (previousDate == null) { - continuousPA = kep.getPerigeeArgument(); - continuousRAAN = kep.getRightAscensionOfAscendingNode(); - continuousM = kep.getMeanAnomaly(); - } else { - final T dt = kep.getDate().durationFrom(previousDate); - final T keplerM = previousM.add(kep.getKeplerianMeanMotion().multiply(dt)); - continuousPA = MathUtils.normalizeAngle(kep.getPerigeeArgument(), previousPA); - continuousRAAN = MathUtils.normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN); - continuousM = MathUtils.normalizeAngle(kep.getMeanAnomaly(), keplerM); - } - previousDate = kep.getDate(); - previousPA = continuousPA; - previousRAAN = continuousRAAN; - previousM = continuousM; - final T[] toAdd = MathArrays.buildArray(getA().getField(), 6); - toAdd[0] = kep.getA(); - toAdd[1] = kep.getE(); - toAdd[2] = kep.getI(); - toAdd[3] = continuousPA; - toAdd[4] = continuousRAAN; - toAdd[5] = continuousM; - if (useDerivatives) { - final T[] toAddDot = MathArrays.buildArray(one.getField(), 6); - toAddDot[0] = kep.getADot(); - toAddDot[1] = kep.getEDot(); - toAddDot[2] = kep.getIDot(); - toAddDot[3] = kep.getPerigeeArgumentDot(); - toAddDot[4] = kep.getRightAscensionOfAscendingNodeDot(); - toAddDot[5] = kep.getMeanAnomalyDot(); - interpolator.addSamplePoint(kep.getDate().durationFrom(date), - toAdd, toAddDot); - } else { - interpolator.addSamplePoint(this.zero.add(kep.getDate().durationFrom(date)), - toAdd); - } - } - - // interpolate - final T[][] interpolated = interpolator.derivatives(zero, 1); - - // build a new interpolated instance - return new FieldKeplerianOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2], - interpolated[0][3], interpolated[0][4], interpolated[0][5], - interpolated[1][0], interpolated[1][1], interpolated[1][2], - interpolated[1][3], interpolated[1][4], interpolated[1][5], - PositionAngle.MEAN, getFrame(), date, getMu()); - - } - /** {@inheritDoc} */ protected T[][] computeJacobianMeanWrtCartesian() { - if (a.getReal() > 0) { + if (isElliptical()) { return computeJacobianMeanWrtCartesianElliptical(); } else { return computeJacobianMeanWrtCartesianHyperbolic(); @@ -1163,8 +1032,8 @@ private T[][] computeJacobianMeanWrtCartesianElliptical() { // da final FieldVector3D vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position); final FieldVector3D vectorARDot = velocity.scalarMultiply(a2.multiply(mu.divide(2.).reciprocal())); - fillHalfRow(this.one, vectorAR, jacobian[0], 0); - fillHalfRow(this.one, vectorARDot, jacobian[0], 3); + fillHalfRow(getOne(), vectorAR, jacobian[0], 0); + fillHalfRow(getOne(), vectorARDot, jacobian[0], 3); // de final T factorER3 = pv.divide(twoA); @@ -1174,8 +1043,8 @@ private T[][] computeJacobianMeanWrtCartesianElliptical() { final FieldVector3D vectorERDot = new FieldVector3D<>(sinE.divide(sqrtMuA), position, cosE.multiply(mu.divide(2.).reciprocal()).multiply(r), velocity, factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorARDot); - fillHalfRow(this.one, vectorER, jacobian[1], 0); - fillHalfRow(this.one, vectorERDot, jacobian[1], 3); + fillHalfRow(getOne(), vectorER, jacobian[1], 0); + fillHalfRow(getOne(), vectorERDot, jacobian[1], 3); // dE / dr (Eccentric anomaly) final T coefE = cosE.divide(e.multiply(sqrtMuA)); @@ -1220,9 +1089,9 @@ private T[][] computeJacobianMeanWrtCartesianElliptical() { final T i3 = factorI1.multiply(mz).multiply(e).divide(oMe2); final T i4 = cosI.multiply(sinPA); final T i5 = cosI.multiply(cosPA); - fillHalfRow(i1, new FieldVector3D<>(vy, vx.negate(), this.zero), i2, vectorAR, i3, vectorER, i4, s, i5, t, + fillHalfRow(i1, new FieldVector3D<>(vy, vx.negate(), getZero()), i2, vectorAR, i3, vectorER, i4, s, i5, t, jacobian[2], 0); - fillHalfRow(i1, new FieldVector3D<>(py.negate(), px, this.zero), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot, + fillHalfRow(i1, new FieldVector3D<>(py.negate(), px, getZero()), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot, jacobian[2], 3); // dpa @@ -1231,11 +1100,11 @@ private T[][] computeJacobianMeanWrtCartesianElliptical() { // dRaan final T factorRaanR = (a.multiply(mu).multiply(oMe2).multiply(sinI).multiply(sinI)).reciprocal(); - fillHalfRow( factorRaanR.negate().multiply(my), new FieldVector3D<>(zero, vz, vy.negate()), - factorRaanR.multiply(mx), new FieldVector3D<>(vz.negate(), zero, vx), + fillHalfRow( factorRaanR.negate().multiply(my), new FieldVector3D<>(getZero(), vz, vy.negate()), + factorRaanR.multiply(mx), new FieldVector3D<>(vz.negate(), getZero(), vx), jacobian[4], 0); - fillHalfRow(factorRaanR.negate().multiply(my), new FieldVector3D<>(zero, pz.negate(), py), - factorRaanR.multiply(mx), new FieldVector3D<>(pz, zero, px.negate()), + fillHalfRow(factorRaanR.negate().multiply(my), new FieldVector3D<>(getZero(), pz.negate(), py), + factorRaanR.multiply(mx), new FieldVector3D<>(pz, getZero(), px.negate()), jacobian[4], 3); // dM @@ -1292,18 +1161,18 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { // da final FieldVector3D vectorAR = new FieldVector3D<>(a2.multiply(-2).divide(r3), position); final FieldVector3D vectorARDot = velocity.scalarMultiply(a2.multiply(-2).divide(mu)); - fillHalfRow(this.one.negate(), vectorAR, jacobian[0], 0); - fillHalfRow(this.one.negate(), vectorARDot, jacobian[0], 3); + fillHalfRow(getOne().negate(), vectorAR, jacobian[0], 0); + fillHalfRow(getOne().negate(), vectorARDot, jacobian[0], 3); // differentials of the momentum final T m = momentum.getNorm(); final T oOm = m.reciprocal(); - final FieldVector3D dcXP = new FieldVector3D<>(this.zero, vz, vy.negate()); - final FieldVector3D dcYP = new FieldVector3D<>(vz.negate(), this.zero, vx); - final FieldVector3D dcZP = new FieldVector3D<>( vy, vx.negate(), this.zero); - final FieldVector3D dcXV = new FieldVector3D<>( this.zero, z.negate(), y); - final FieldVector3D dcYV = new FieldVector3D<>( z, this.zero, x.negate()); - final FieldVector3D dcZV = new FieldVector3D<>( y.negate(), x, this.zero); + final FieldVector3D dcXP = new FieldVector3D<>(getZero(), vz, vy.negate()); + final FieldVector3D dcYP = new FieldVector3D<>(vz.negate(), getZero(), vx); + final FieldVector3D dcZP = new FieldVector3D<>( vy, vx.negate(), getZero()); + final FieldVector3D dcXV = new FieldVector3D<>( getZero(), z.negate(), y); + final FieldVector3D dcYV = new FieldVector3D<>( z, getZero(), x.negate()); + final FieldVector3D dcZV = new FieldVector3D<>( y.negate(), x, getZero()); final FieldVector3D dCP = new FieldVector3D<>(mx.multiply(oOm), dcXP, my.multiply(oOm), dcYP, mz.multiply(oOm), dcZP); final FieldVector3D dCV = new FieldVector3D<>(mx.multiply(oOm), dcXV, my.multiply(oOm), dcYV, mz.multiply(oOm), dcZV); @@ -1334,7 +1203,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { final T cP5 = r2.multiply(sinI).multiply(sinI).negate().reciprocal(); final T cP6 = z.multiply(cP5); final T cP7 = cP3.multiply(cP5); - final FieldVector3D dacP = new FieldVector3D<>(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new FieldVector3D<>(my.negate(), mx, this.zero)); + final FieldVector3D dacP = new FieldVector3D<>(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new FieldVector3D<>(my.negate(), mx, getZero())); final FieldVector3D dacV = new FieldVector3D<>(cP1, dcXV, cP2, dcYV, cP4, dCV); final FieldVector3D dpoP = new FieldVector3D<>(cP6, dacP, cP7, this.PLUS_K); final FieldVector3D dpoV = new FieldVector3D<>(cP6, dacV); @@ -1346,8 +1215,8 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { final FieldVector3D dreV = new FieldVector3D<>(mOMu, position, pv.divide(mu), dCV); final FieldVector3D davP = new FieldVector3D<>(resOre2.negate(), dpP, recOre2, dreP, resOre2.divide(r), position); final FieldVector3D davV = new FieldVector3D<>(resOre2.negate(), dpV, recOre2, dreV); - fillHalfRow(this.one, dpoP, this.one.negate(), davP, jacobian[3], 0); - fillHalfRow(this.one, dpoV, this.one.negate(), davV, jacobian[3], 3); + fillHalfRow(getOne(), dpoP, getOne().negate(), davP, jacobian[3], 0); + fillHalfRow(getOne(), dpoV, getOne().negate(), davV, jacobian[3], 3); // dRAAN final T cO0 = cI1.multiply(cI1); @@ -1366,8 +1235,8 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { final FieldVector3D dbuV = new FieldVector3D<>(oObux.multiply(mu.divide(2.)), vectorARDot, m.multiply(oObux), dCV); final FieldVector3D dcuP = new FieldVector3D<>(oObux, velocity, scasbu.negate().multiply(oObux), dbuP); final FieldVector3D dcuV = new FieldVector3D<>(oObux, position, scasbu.negate().multiply(oObux), dbuV); - fillHalfRow(this.one, dauP, e.negate().divide(rOa.add(1)), dcuP, jacobian[5], 0); - fillHalfRow(this.one, dauV, e.negate().divide(rOa.add(1)), dcuV, jacobian[5], 3); + fillHalfRow(getOne(), dauP, e.negate().divide(rOa.add(1)), dcuP, jacobian[5], 0); + fillHalfRow(getOne(), dauV, e.negate().divide(rOa.add(1)), dcuV, jacobian[5], 3); return jacobian; @@ -1375,7 +1244,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { /** {@inheritDoc} */ protected T[][] computeJacobianEccentricWrtCartesian() { - if (a.getReal() > 0) { + if (isElliptical()) { return computeJacobianEccentricWrtCartesianElliptical(); } else { return computeJacobianEccentricWrtCartesianHyperbolic(); @@ -1449,7 +1318,7 @@ private T[][] computeJacobianEccentricWrtCartesianHyperbolic() { /** {@inheritDoc} */ protected T[][] computeJacobianTrueWrtCartesian() { - if (a.getReal() > 0) { + if (isElliptical()) { return computeJacobianTrueWrtCartesianElliptical(); } else { return computeJacobianTrueWrtCartesianHyperbolic(); @@ -1531,7 +1400,7 @@ private T[][] computeJacobianTrueWrtCartesianHyperbolic() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final T gm, + public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { final T oMe2; final T ksi; @@ -1570,6 +1439,27 @@ public String toString() { append(";}").toString(); } + /** {@inheritDoc} */ + @Override + public PositionAngleType getCachedPositionAngleType() { + return PositionAngleType.TRUE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasRates() { + return hasDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public FieldKeplerianOrbit removeRates() { + final PositionAngleType positionAngleType = getCachedPositionAngleType(); + return new FieldKeplerianOrbit<>(getA(), getE(), getI(), getPerigeeArgument(), getRightAscensionOfAscendingNode(), + getAnomaly(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + } + + /** Check if the given parameter is within an acceptable range. * The bounds are inclusive: an exception is raised when either of those conditions are met: *
          @@ -1600,12 +1490,12 @@ public KeplerianOrbit toOrbit() { pa.getReal(), raan.getReal(), v.getReal(), aDot.getReal(), eDot.getReal(), iDot.getReal(), paDot.getReal(), raanDot.getReal(), vDot.getReal(), - PositionAngle.TRUE, + PositionAngleType.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } else { return new KeplerianOrbit(a.getReal(), e.getReal(), i.getReal(), pa.getReal(), raan.getReal(), v.getReal(), - PositionAngle.TRUE, + PositionAngleType.TRUE, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } } diff --git a/src/main/java/org/orekit/orbits/FieldOrbit.java b/src/main/java/org/orekit/orbits/FieldOrbit.java index f71e933fdf..356e208137 100644 --- a/src/main/java/org/orekit/orbits/FieldOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.linear.FieldDecompositionSolver; import org.hipparchus.linear.FieldLUDecomposition; @@ -28,10 +29,10 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldStaticTransform; +import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; -import org.orekit.frames.Transform; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.time.FieldTimeInterpolable; import org.orekit.time.FieldTimeShiftable; import org.orekit.time.FieldTimeStamped; import org.orekit.utils.FieldPVCoordinates; @@ -62,9 +63,10 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @since 9.0 + * @param type of the field elements */ public abstract class FieldOrbit> - implements FieldPVCoordinatesProvider, FieldTimeStamped, FieldTimeShiftable, T>, FieldTimeInterpolable, T> { + implements FieldPVCoordinatesProvider, FieldTimeStamped, FieldTimeShiftable, T> { /** Frame in which are defined the orbital parameters. */ private final Frame frame; @@ -75,6 +77,26 @@ public abstract class FieldOrbit> /** Value of mu used to compute position and velocity (m³/s²). */ private final T mu; + /** Field-valued one. + * @since 12.0 + * */ + private final T one; + + /** Field-valued zero. + * @since 12.0 + * */ + private final T zero; + + /** Field used by this class. + * @since 12.0 + */ + private final Field field; + + /** Computed position. + * @since 12.0 + */ + private transient FieldVector3D position; + /** Computed PVCoordinates. */ private transient TimeStampedFieldPVCoordinates pvCoordinates; @@ -108,6 +130,9 @@ public abstract class FieldOrbit> protected FieldOrbit(final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { ensurePseudoInertialFrame(frame); + this.field = mu.getField(); + this.zero = this.field.getZero(); + this.one = this.field.getOne(); this.date = date; this.mu = mu; this.pvCoordinates = null; @@ -137,6 +162,9 @@ protected FieldOrbit(final Frame frame, final FieldAbsoluteDate date, final T protected FieldOrbit(final TimeStampedFieldPVCoordinates FieldPVCoordinates, final Frame frame, final T mu) throws IllegalArgumentException { ensurePseudoInertialFrame(frame); + this.field = mu.getField(); + this.zero = this.field.getZero(); + this.one = this.field.getOne(); this.date = FieldPVCoordinates.getDate(); this.mu = mu; if (FieldPVCoordinates.getAcceleration().getNormSq().getReal() == 0.0) { @@ -181,6 +209,14 @@ protected static > boolean hasNonKeplerianAcce } + /** Returns true if and only if the orbit is elliptical i.e. has a non-negative semi-major axis. + * @return true if getA() is strictly greater than 0 + * @since 12.0 + */ + public boolean isElliptical() { + return getA().getReal() > 0.; + } + /** Get the orbit type. * @return orbit type */ @@ -342,10 +378,6 @@ public Frame getFrame() { */ public abstract T getIDot(); - /** Get the central acceleration constant. - * @return central acceleration constant - */ - /** Check if orbit includes derivatives. * @return true if orbit includes derivatives * @see #getADot() @@ -376,7 +408,9 @@ public T getMu() { */ public T getKeplerianPeriod() { final T a = getA(); - return (a.getReal() < 0) ? getA().getField().getZero().add(Double.POSITIVE_INFINITY) : a.multiply(a.getPi().multiply(2.0)).multiply(a.divide(mu).sqrt()); + return isElliptical() ? + a.multiply(a.getPi().multiply(2.0)).multiply(a.divide(mu).sqrt()) : + zero.add(Double.POSITIVE_INFINITY); } /** Get the Keplerian mean motion. @@ -389,6 +423,13 @@ public T getKeplerianMeanMotion() { return absA.reciprocal().multiply(mu).sqrt().divide(absA); } + /** Get the derivative of the mean anomaly with respect to the semi major axis. + * @return derivative of the mean anomaly with respect to the semi major axis + */ + public T getMeanAnomalyDotWrtA() { + return getKeplerianMeanMotion().divide(getA()).multiply(-1.5); + } + /** Get the date of orbital parameters. * @return date of the orbital parameters */ @@ -413,7 +454,7 @@ public TimeStampedFieldPVCoordinates getPVCoordinates(final Frame outputFrame } // Else, PV coordinates are transformed to output frame - final Transform t = frame.getTransformTo(outputFrame, date.toAbsoluteDate()); //TODO CHECK THIS + final FieldTransform t = frame.getTransformTo(outputFrame, date); return t.transformPVCoordinates(pvCoordinates); } @@ -422,6 +463,40 @@ public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame); } + /** Get the position in a specified frame. + * @param outputFrame frame in which the position coordinates shall be computed + * @return position in the specified output frame + * @see #getPosition() + * @since 12.0 + */ + public FieldVector3D getPosition(final Frame outputFrame) { + if (position == null) { + position = initPosition(); + } + + // If output frame requested is the same as definition frame, + // Position vector is returned directly + if (outputFrame == frame) { + return position; + } + + // Else, position vector is transformed to output frame + final FieldStaticTransform t = frame.getStaticTransformTo(outputFrame, date); + return t.transformPosition(position); + + } + + /** Get the position in definition frame. + * @return position in the definition frame + * @see #getPVCoordinates() + * @since 12.0 + */ + public FieldVector3D getPosition() { + if (position == null) { + position = initPosition(); + } + return position; + } /** Get the {@link TimeStampedPVCoordinates} in definition frame. * @return FieldPVCoordinates in the definition frame @@ -430,11 +505,38 @@ public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate public TimeStampedFieldPVCoordinates getPVCoordinates() { if (pvCoordinates == null) { pvCoordinates = initPVCoordinates(); - + position = pvCoordinates.getPosition(); } return pvCoordinates; } + /** Getter for Field-valued one. + * @return one + * */ + protected T getOne() { + return one; + } + + /** Getter for Field-valued zero. + * @return zero + * */ + protected T getZero() { + return zero; + } + + /** Getter for Field. + * @return CalculusField + * */ + protected Field getField() { + return field; + } + + /** Compute the position coordinates from the canonical parameters. + * @return computed position coordinates + * @since 12.0 + */ + protected abstract FieldVector3D initPosition(); + /** Compute the position/velocity coordinates from the canonical parameters. * @return computed position/velocity coordinates */ @@ -462,7 +564,7 @@ public TimeStampedFieldPVCoordinates getPVCoordinates() { * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix * is larger than 6x6, only the 6x6 upper left corner will be modified */ - public void getJacobianWrtCartesian(final PositionAngle type, final T[][] jacobian) { + public void getJacobianWrtCartesian(final PositionAngleType type, final T[][] jacobian) { final T[][] cachedJacobian; synchronized (this) { @@ -510,7 +612,7 @@ public void getJacobianWrtCartesian(final PositionAngle type, final T[][] jacobi * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix * is larger than 6x6, only the 6x6 upper left corner will be modified */ - public void getJacobianWrtParameters(final PositionAngle type, final T[][] jacobian) { + public void getJacobianWrtParameters(final PositionAngleType type, final T[][] jacobian) { final T[][] cachedJacobian; synchronized (this) { @@ -552,7 +654,7 @@ public void getJacobianWrtParameters(final PositionAngle type, final T[][] jacob * @param type type of the position angle to use * @return inverse Jacobian */ - private T[][] createInverseJacobian(final PositionAngle type) { + private T[][] createInverseJacobian(final PositionAngleType type) { // get the direct Jacobian final T[][] directJacobian = MathArrays.buildArray(getA().getField(), 6, 6); @@ -612,7 +714,7 @@ private T[][] createInverseJacobian(final PositionAngle type) { * part must be added to the array components, as the array may already * contain some non-zero elements corresponding to non-Keplerian parts) */ - public abstract void addKeplerContribution(PositionAngle type, T gm, T[] pDot); + public abstract void addKeplerContribution(PositionAngleType type, T gm, T[] pDot); /** Fill a Jacobian half row with a single vector. * @param a coefficient of the vector @@ -640,9 +742,7 @@ protected void fillHalfRow(final T a1, final FieldVector3D v1, final T a2, fi row[j] = a1.linearCombination(a1, v1.getX(), a2, v2.getX()); row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY()); row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ()); -// row[j] = a1.multiply(v1.getX()).add(a2.multiply(v2.getX())); -// row[j + 1] = a1.multiply(v1.getY()).add(a2.multiply(v2.getY())); -// row[j + 2] = a1.multiply(v1.getZ()).add(a2.multiply(v2.getZ())); + } /** Fill a Jacobian half row with a linear combination of vectors. @@ -663,11 +763,6 @@ protected void fillHalfRow(final T a1, final FieldVector3D v1, row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY()); row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ()); - - -// row[j] = a1.multiply(v1.getX()).add(a2.multiply(v2.getX())).add(a3.multiply(v3.getX())); -// row[j + 1] = a1.multiply(v1.getY()).add(a2.multiply(v2.getY())).add(a3.multiply(v3.getY())); -// row[j + 2] = a1.multiply(v1.getZ()).add(a2.multiply(v2.getZ())).add(a3.multiply(v3.getZ())); } /** Fill a Jacobian half row with a linear combination of vectors. @@ -688,9 +783,7 @@ protected void fillHalfRow(final T a1, final FieldVector3D v1, final T a2, fi row[j] = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX()); row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY()); row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ()); -// row[j] = a1.multiply(v1.getX()).add(a2.multiply(v2.getX())).add(a3.multiply(v3.getX())).add(a4.multiply(v4.getX())); -// row[j + 1] = a1.multiply(v1.getY()).add(a2.multiply(v2.getY())).add(a3.multiply(v3.getY())).add(a4.multiply(v4.getY())); -// row[j + 2] = a1.multiply(v1.getZ()).add(a2.multiply(v2.getZ())).add(a3.multiply(v3.getZ())).add(a4.multiply(v4.getZ())); + } /** Fill a Jacobian half row with a linear combination of vectors. @@ -715,9 +808,6 @@ protected void fillHalfRow(final T a1, final FieldVector3D v1, final T a2, fi row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY()).add(a5.multiply(v5.getY())); row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ()).add(a5.multiply(v5.getZ())); -// row[j] = a1.multiply(v1.getX()).add(a2.multiply(v2.getX())).add(a3.multiply(v3.getX())).add(a4.multiply(v4.getX())).add(a5.multiply(v5.getX())); -// row[j + 1] = a1.multiply(v1.getY()).add(a2.multiply(v2.getY())).add(a3.multiply(v3.getY())).add(a4.multiply(v4.getY())).add(a5.multiply(v5.getY())); -// row[j + 2] = a1.multiply(v1.getZ()).add(a2.multiply(v2.getZ())).add(a3.multiply(v3.getZ())).add(a4.multiply(v4.getZ())).add(a5.multiply(v5.getZ())); } /** Fill a Jacobian half row with a linear combination of vectors. @@ -744,10 +834,6 @@ protected void fillHalfRow(final T a1, final FieldVector3D v1, final T a2, fi row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY()).add(a1.linearCombination(a5, v5.getY(), a6, v6.getY())); row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ()).add(a1.linearCombination(a5, v5.getZ(), a6, v6.getZ())); - -// row[j] = a1.multiply(v1.getX()).add(a2.multiply(v2.getX())).add(a3.multiply(v3.getX())).add(a4.multiply(v4.getX())).add(a5.multiply(v5.getX())).add(a6.multiply(v6.getX())); -// row[j + 1] = a1.multiply(v1.getY()).add(a2.multiply(v2.getY())).add(a3.multiply(v3.getY())).add(a4.multiply(v4.getY())).add(a5.multiply(v5.getY())).add(a6.multiply(v6.getY())); -// row[j + 2] = a1.multiply(v1.getZ()).add(a2.multiply(v2.getZ())).add(a3.multiply(v3.getZ())).add(a4.multiply(v4.getZ())).add(a5.multiply(v5.getZ())).add(a6.multiply(v6.getZ())); } diff --git a/src/main/java/org/orekit/orbits/FieldOrbitBlender.java b/src/main/java/org/orekit/orbits/FieldOrbitBlender.java new file mode 100644 index 0000000000..f635578417 --- /dev/null +++ b/src/main/java/org/orekit/orbits/FieldOrbitBlender.java @@ -0,0 +1,122 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.polynomials.SmoothStepFactory; +import org.orekit.errors.OrekitException; +import org.orekit.frames.Frame; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; +import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; + +import java.util.List; + +/** + * Orbit blender. + *

          + * Its purpose is to interpolate orbit state between tabulated orbit states using the concept of blending, exposed in : + * "Efficient Covariance Interpolation using Blending of Approximate State Error Transitions" by Sergei Tanygin, and applying + * it to orbit states instead of covariances. + *

          + * It propagates tabulated values to the interpolating time using given analytical propagator and then blend each propagated + * states using a smoothstep function. It gives especially good results as explained + * here + * compared to Hermite interpolation when time steps between tabulated values get significant (In LEO, > 10 mn for + * example). + * + * @param type of field element + * + * @author Vincent Cucchietti + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory.FieldSmoothStepFunction + */ +public class FieldOrbitBlender> extends AbstractFieldOrbitInterpolator { + + /** Analytical propagator used to propagate tabulated orbits to interpolating time. */ + private final FieldAbstractAnalyticalPropagator analyticalPropagator; + + /** Blending function. */ + private final SmoothStepFactory.FieldSmoothStepFunction blendingFunction; + + /** + * Default constructor. + * + * @param blendingFunction + * {@link org.hipparchus.analysis.polynomials.SmoothStepFactory.SmoothStepFunction smoothstep function} used for + * blending + * @param analyticalPropagator analytical propagator used to propagate tabulated orbits to interpolating time + * @param outputInertialFrame output inertial frame + * + * @throws OrekitException if output frame is not inertial + */ + public FieldOrbitBlender(final SmoothStepFactory.FieldSmoothStepFunction blendingFunction, + final FieldAbstractAnalyticalPropagator analyticalPropagator, + final Frame outputInertialFrame) { + super(DEFAULT_INTERPOLATION_POINTS, 0., outputInertialFrame); + this.blendingFunction = blendingFunction; + this.analyticalPropagator = analyticalPropagator; + } + + /** {@inheritDoc} */ + @Override + public FieldOrbit interpolate(final InterpolationData interpolationData) { + + // Get interpolation date + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + + // Get first and last entry + final List> neighborList = interpolationData.getNeighborList(); + final FieldOrbit previousOrbit = neighborList.get(0); + final FieldOrbit nextOrbit = neighborList.get(1); + + // Propagate orbits + final FieldOrbit forwardedOrbit = propagateOrbitAnalytically(previousOrbit, interpolationDate); + final FieldOrbit backwardedOrbit = propagateOrbitAnalytically(nextOrbit, interpolationDate); + + // Extract position-velocity-acceleration coordinates + final FieldPVCoordinates forwardedPV = forwardedOrbit.getPVCoordinates(getOutputInertialFrame()); + final FieldPVCoordinates backwardedPV = backwardedOrbit.getPVCoordinates(getOutputInertialFrame()); + + // Blend PV coordinates + final KK timeParameter = getTimeParameter(interpolationDate, previousOrbit.getDate(), nextOrbit.getDate()); + final KK blendingValue = blendingFunction.value(timeParameter); + + final FieldPVCoordinates blendedPV = forwardedPV.blendArithmeticallyWith(backwardedPV, blendingValue); + + // Output new blended instance + return new FieldCartesianOrbit<>(blendedPV, getOutputInertialFrame(), interpolationDate, previousOrbit.getMu()); + } + + /** + * Propagate orbit using predefined {@link AbstractAnalyticalPropagator analytical propagator}. + * + * @param tabulatedOrbit tabulated orbit to propagate + * @param propagationDate propagation date + * + * @return orbit propagated to propagation date + */ + private FieldOrbit propagateOrbitAnalytically(final FieldOrbit tabulatedOrbit, + final FieldAbsoluteDate propagationDate) { + + analyticalPropagator.resetInitialState(new FieldSpacecraftState<>(tabulatedOrbit)); + + return analyticalPropagator.propagate(propagationDate).getOrbit(); + } +} diff --git a/src/main/java/org/orekit/orbits/FieldOrbitHermiteInterpolator.java b/src/main/java/org/orekit/orbits/FieldOrbitHermiteInterpolator.java new file mode 100644 index 0000000000..38d58ca7dc --- /dev/null +++ b/src/main/java/org/orekit/orbits/FieldOrbitHermiteInterpolator.java @@ -0,0 +1,471 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.MathUtils; +import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.Frame; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator; + +import java.util.List; +import java.util.stream.Stream; + +/** + * Class using a Hermite interpolator to interpolate orbits. + *

          + * Depending on given sample orbit type, the interpolation may differ : + *

            + *
          • For Keplerian, Circular and Equinoctial orbits, the interpolated instance is created by polynomial Hermite + * interpolation, using derivatives when available.
          • + *
          • For Cartesian orbits, the interpolated instance is created using the cartesian derivatives filter given at + * instance construction. Hence, it will fall back to Lagrange interpolation if this instance has been designed to not + * use derivatives. + *
          + *

          + * In any case, it should be used only with small number of interpolation points (about 10-20 points) in order to avoid + * Runge's phenomenon and numerical problems + * (including NaN appearing). + * + * @param type of the field element + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see FieldOrbit + * @see FieldHermiteInterpolator + */ +public class FieldOrbitHermiteInterpolator> extends AbstractFieldOrbitInterpolator { + + /** Filter for derivatives from the sample to use in position-velocity-acceleration interpolation. */ + private final CartesianDerivativesFilter pvaFilter; + + /** Field of the elements. */ + private Field field; + + /** Fielded zero. */ + private KK zero; + + /** Fielded one. */ + private KK one; + + /** + * Constructor with : + *

            + *
          • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
          • + *
          • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
          • + *
          • Use of position and two time derivatives during interpolation
          • + *
          + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param outputInertialFrame output inertial frame + */ + public FieldOrbitHermiteInterpolator(final Frame outputInertialFrame) { + this(DEFAULT_INTERPOLATION_POINTS, outputInertialFrame); + } + + /** + * Constructor with : + *
            + *
          • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
          • + *
          • Use of position and two time derivatives during interpolation
          • + *
          + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputInertialFrame output inertial frame + */ + public FieldOrbitHermiteInterpolator(final int interpolationPoints, final Frame outputInertialFrame) { + this(interpolationPoints, outputInertialFrame, CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

          + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputInertialFrame output inertial frame + * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation + */ + public FieldOrbitHermiteInterpolator(final int interpolationPoints, final Frame outputInertialFrame, + final CartesianDerivativesFilter pvaFilter) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputInertialFrame, pvaFilter); + } + + /** + * Constructor. + *

          + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputInertialFrame output inertial frame + * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation + */ + public FieldOrbitHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputInertialFrame, final CartesianDerivativesFilter pvaFilter) { + super(interpolationPoints, extrapolationThreshold, outputInertialFrame); + this.pvaFilter = pvaFilter; + } + + /** Get filter for derivatives from the sample to use in position-velocity-acceleration interpolation. + * @return filter for derivatives from the sample to use in position-velocity-acceleration interpolation + */ + public CartesianDerivativesFilter getPVAFilter() { + return pvaFilter; + } + + /** + * {@inheritDoc} + *

          + * Depending on given sample orbit type, the interpolation may differ : + *

            + *
          • For Keplerian, Circular and Equinoctial orbits, the interpolated instance is created by polynomial Hermite + * interpolation, using derivatives when available.
          • + *
          • For Cartesian orbits, the interpolated instance is created using the cartesian derivatives filter given at + * instance construction. Hence, it will fall back to Lagrange interpolation if this instance has been designed to not + * use derivatives. + *
          + * If orbit interpolation on large samples is needed, using the {@link + * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this + * low-level interpolation. The Ephemeris class automatically handles selection of + * a neighboring sub-sample with a predefined number of point from a large global sample + * in a thread-safe way. + * + * @param interpolationData interpolation data + * + * @return interpolated instance for given interpolation data + */ + @Override + protected FieldOrbit interpolate(final InterpolationData interpolationData) { + + // Get interpolation date + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + + // Get orbit sample + final List> sample = interpolationData.getNeighborList(); + + // Get first entry + final FieldOrbit firstEntry = sample.get(0); + + // Get orbit type for interpolation + final OrbitType orbitType = firstEntry.getType(); + + // Extract field + this.field = firstEntry.getA().getField(); + this.zero = field.getZero(); + this.one = field.getOne(); + + if (orbitType == OrbitType.CARTESIAN) { + return interpolateCartesian(interpolationDate, sample); + } + else { + return interpolateCommon(interpolationDate, sample, orbitType); + } + + } + + /** + * Interpolate Cartesian orbit using specific method for Cartesian orbit. + * + * @param interpolationDate interpolation date + * @param sample orbits sample + * + * @return interpolated Cartesian orbit + */ + private FieldCartesianOrbit interpolateCartesian(final FieldAbsoluteDate interpolationDate, + final List> sample) { + + // Create time stamped position-velocity-acceleration Hermite interpolator + final FieldTimeInterpolator, KK> interpolator = + new TimeStampedFieldPVCoordinatesHermiteInterpolator<>(getNbInterpolationPoints(), + getExtrapolationThreshold(), + pvaFilter); + + // Convert sample to stream + final Stream> sampleStream = sample.stream(); + + // Map time stamped position-velocity-acceleration coordinates + final Stream> sampleTimeStampedPV = sampleStream.map(FieldOrbit::getPVCoordinates); + + // Interpolate PVA + final TimeStampedFieldPVCoordinates interpolated = + interpolator.interpolate(interpolationDate, sampleTimeStampedPV); + + // Use first entry gravitational parameter + final KK mu = sample.get(0).getMu(); + + return new FieldCartesianOrbit<>(interpolated, getOutputInertialFrame(), interpolationDate, mu); + } + + /** + * Method gathering common parts of interpolation between circular, equinoctial and keplerian orbit. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param orbitType interpolation method to use + * + * @return interpolated orbit + */ + private FieldOrbit interpolateCommon(final FieldAbsoluteDate interpolationDate, + final List> orbits, + final OrbitType orbitType) { + + // First pass to check if derivatives are available throughout the sample + boolean useDerivatives = true; + for (final FieldOrbit orbit : orbits) { + useDerivatives = useDerivatives && orbit.hasDerivatives(); + } + + // Use first entry gravitational parameter + final KK mu = orbits.get(0).getMu(); + + // Interpolate and build a new instance + final KK[][] interpolated; + switch (orbitType) { + case CIRCULAR: + interpolated = interpolateCircular(interpolationDate, orbits, useDerivatives); + return new FieldCircularOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2], + interpolated[0][3], interpolated[0][4], interpolated[0][5], + interpolated[1][0], interpolated[1][1], interpolated[1][2], + interpolated[1][3], interpolated[1][4], interpolated[1][5], + PositionAngleType.MEAN, getOutputInertialFrame(), interpolationDate, mu); + case KEPLERIAN: + interpolated = interpolateKeplerian(interpolationDate, orbits, useDerivatives); + return new FieldKeplerianOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2], + interpolated[0][3], interpolated[0][4], interpolated[0][5], + interpolated[1][0], interpolated[1][1], interpolated[1][2], + interpolated[1][3], interpolated[1][4], interpolated[1][5], + PositionAngleType.MEAN, getOutputInertialFrame(), interpolationDate, mu); + case EQUINOCTIAL: + interpolated = interpolateEquinoctial(interpolationDate, orbits, useDerivatives); + return new FieldEquinoctialOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2], + interpolated[0][3], interpolated[0][4], interpolated[0][5], + interpolated[1][0], interpolated[1][1], interpolated[1][2], + interpolated[1][3], interpolated[1][4], interpolated[1][5], + PositionAngleType.MEAN, getOutputInertialFrame(), interpolationDate, mu); + default: + // Should never happen + throw new OrekitInternalError(null); + } + + } + + /** + * Build interpolating functions for circular orbit parameters. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param useDerivatives flag defining if derivatives are available throughout the sample + * + * @return interpolating functions for circular orbit parameters + */ + private KK[][] interpolateCircular(final FieldAbsoluteDate interpolationDate, final List> orbits, + final boolean useDerivatives) { + + // set up an interpolator + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // second pass to feed interpolator + FieldAbsoluteDate previousDate = null; + KK previousRAAN = zero.add(Double.NaN); + KK previousAlphaM = zero.add(Double.NaN); + for (final FieldOrbit orbit : orbits) { + final FieldCircularOrbit circ = (FieldCircularOrbit) OrbitType.CIRCULAR.convertType(orbit); + final KK continuousRAAN; + final KK continuousAlphaM; + if (previousDate == null) { + continuousRAAN = circ.getRightAscensionOfAscendingNode(); + continuousAlphaM = circ.getAlphaM(); + } + else { + final KK dt = circ.getDate().durationFrom(previousDate); + final KK keplerAM = previousAlphaM.add(circ.getKeplerianMeanMotion().multiply(dt)); + continuousRAAN = MathUtils.normalizeAngle(circ.getRightAscensionOfAscendingNode(), previousRAAN); + continuousAlphaM = MathUtils.normalizeAngle(circ.getAlphaM(), keplerAM); + } + previousDate = circ.getDate(); + previousRAAN = continuousRAAN; + previousAlphaM = continuousAlphaM; + final KK[] toAdd = MathArrays.buildArray(one.getField(), 6); + toAdd[0] = circ.getA(); + toAdd[1] = circ.getCircularEx(); + toAdd[2] = circ.getCircularEy(); + toAdd[3] = circ.getI(); + toAdd[4] = continuousRAAN; + toAdd[5] = continuousAlphaM; + if (useDerivatives) { + final KK[] toAddDot = MathArrays.buildArray(one.getField(), 6); + toAddDot[0] = circ.getADot(); + toAddDot[1] = circ.getCircularExDot(); + toAddDot[2] = circ.getCircularEyDot(); + toAddDot[3] = circ.getIDot(); + toAddDot[4] = circ.getRightAscensionOfAscendingNodeDot(); + toAddDot[5] = circ.getAlphaMDot(); + interpolator.addSamplePoint(circ.getDate().durationFrom(interpolationDate), + toAdd, toAddDot); + } + else { + interpolator.addSamplePoint(circ.getDate().durationFrom(interpolationDate), + toAdd); + } + } + + return interpolator.derivatives(zero, 1); + } + + /** + * Build interpolating functions for keplerian orbit parameters. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param useDerivatives flag defining if derivatives are available throughout the sample + * + * @return interpolating functions for keplerian orbit parameters + */ + private KK[][] interpolateKeplerian(final FieldAbsoluteDate interpolationDate, final List> orbits, + final boolean useDerivatives) { + + // Set up an interpolator + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // Second pass to feed interpolator + FieldAbsoluteDate previousDate = null; + KK previousPA = zero.add(Double.NaN); + KK previousRAAN = zero.add(Double.NaN); + KK previousM = zero.add(Double.NaN); + for (final FieldOrbit orbit : orbits) { + final FieldKeplerianOrbit kep = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); + final KK continuousPA; + final KK continuousRAAN; + final KK continuousM; + if (previousDate == null) { + continuousPA = kep.getPerigeeArgument(); + continuousRAAN = kep.getRightAscensionOfAscendingNode(); + continuousM = kep.getMeanAnomaly(); + } + else { + final KK dt = kep.getDate().durationFrom(previousDate); + final KK keplerM = previousM.add(kep.getKeplerianMeanMotion().multiply(dt)); + continuousPA = MathUtils.normalizeAngle(kep.getPerigeeArgument(), previousPA); + continuousRAAN = MathUtils.normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN); + continuousM = MathUtils.normalizeAngle(kep.getMeanAnomaly(), keplerM); + } + previousDate = kep.getDate(); + previousPA = continuousPA; + previousRAAN = continuousRAAN; + previousM = continuousM; + final KK[] toAdd = MathArrays.buildArray(field, 6); + toAdd[0] = kep.getA(); + toAdd[1] = kep.getE(); + toAdd[2] = kep.getI(); + toAdd[3] = continuousPA; + toAdd[4] = continuousRAAN; + toAdd[5] = continuousM; + if (useDerivatives) { + final KK[] toAddDot = MathArrays.buildArray(field, 6); + toAddDot[0] = kep.getADot(); + toAddDot[1] = kep.getEDot(); + toAddDot[2] = kep.getIDot(); + toAddDot[3] = kep.getPerigeeArgumentDot(); + toAddDot[4] = kep.getRightAscensionOfAscendingNodeDot(); + toAddDot[5] = kep.getMeanAnomalyDot(); + interpolator.addSamplePoint(kep.getDate().durationFrom(interpolationDate), + toAdd, toAddDot); + } + else { + interpolator.addSamplePoint(this.zero.add(kep.getDate().durationFrom(interpolationDate)), + toAdd); + } + } + + return interpolator.derivatives(zero, 1); + } + + /** + * Build interpolating functions for equinoctial orbit parameters. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param useDerivatives flag defining if derivatives are available throughout the sample + * + * @return interpolating functions for equinoctial orbit parameters + */ + private KK[][] interpolateEquinoctial(final FieldAbsoluteDate interpolationDate, final List> orbits, + final boolean useDerivatives) { + + // set up an interpolator + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // second pass to feed interpolator + FieldAbsoluteDate previousDate = null; + KK previousLm = zero.add(Double.NaN); + for (final FieldOrbit orbit : orbits) { + final FieldEquinoctialOrbit equi = (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit); + final KK continuousLm; + if (previousDate == null) { + continuousLm = equi.getLM(); + } + else { + final KK dt = equi.getDate().durationFrom(previousDate); + final KK keplerLm = previousLm.add(equi.getKeplerianMeanMotion().multiply(dt)); + continuousLm = MathUtils.normalizeAngle(equi.getLM(), keplerLm); + } + previousDate = equi.getDate(); + previousLm = continuousLm; + final KK[] toAdd = MathArrays.buildArray(field, 6); + toAdd[0] = equi.getA(); + toAdd[1] = equi.getEquinoctialEx(); + toAdd[2] = equi.getEquinoctialEy(); + toAdd[3] = equi.getHx(); + toAdd[4] = equi.getHy(); + toAdd[5] = continuousLm; + if (useDerivatives) { + final KK[] toAddDot = MathArrays.buildArray(one.getField(), 6); + toAddDot[0] = equi.getADot(); + toAddDot[1] = equi.getEquinoctialExDot(); + toAddDot[2] = equi.getEquinoctialEyDot(); + toAddDot[3] = equi.getHxDot(); + toAddDot[4] = equi.getHyDot(); + toAddDot[5] = equi.getLMDot(); + interpolator.addSamplePoint(equi.getDate().durationFrom(interpolationDate), + toAdd, toAddDot); + } + else { + interpolator.addSamplePoint(equi.getDate().durationFrom(interpolationDate), + toAdd); + } + } + + return interpolator.derivatives(zero, 1); + } +} diff --git a/src/main/java/org/orekit/orbits/HaloOrbit.java b/src/main/java/org/orekit/orbits/HaloOrbit.java index a4f889dcd1..2aaaa601da 100644 --- a/src/main/java/org/orekit/orbits/HaloOrbit.java +++ b/src/main/java/org/orekit/orbits/HaloOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java b/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java index 16ccec0bbd..8ffa0a6d2d 100644 --- a/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java +++ b/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -43,7 +43,9 @@ public final class KeplerianAnomalyUtility { B = k3 * k3 / (6 * k1); } + /** Private constructor for utility class. */ private KeplerianAnomalyUtility() { + // Nothing to do } /** diff --git a/src/main/java/org/orekit/orbits/KeplerianOrbit.java b/src/main/java/org/orekit/orbits/KeplerianOrbit.java index 4365809788..5d6123b92e 100644 --- a/src/main/java/org/orekit/orbits/KeplerianOrbit.java +++ b/src/main/java/org/orekit/orbits/KeplerianOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,15 +17,10 @@ package org.orekit.orbits; import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.UnivariateDerivative1; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; @@ -78,7 +73,7 @@ * @author Fabien Maussion * @author Véronique Pommier-Maurussane */ -public class KeplerianOrbit extends Orbit { +public class KeplerianOrbit extends Orbit implements PositionAngleBased { /** Serializable UID. */ private static final long serialVersionUID = 20170414L; @@ -143,7 +138,7 @@ public class KeplerianOrbit extends Orbit { */ public KeplerianOrbit(final double a, final double e, final double i, final double pa, final double raan, final double anomaly, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { this(a, e, i, pa, raan, anomaly, @@ -178,7 +173,7 @@ public KeplerianOrbit(final double a, final double e, final double i, final double pa, final double raan, final double anomaly, final double aDot, final double eDot, final double iDot, final double paDot, final double raanDot, final double anomalyDot, - final PositionAngle type, + final PositionAngleType type, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { super(frame, date, mu); @@ -319,7 +314,7 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, final double muA = mu * a; // compute true anomaly - if (a > 0) { + if (isElliptical()) { // elliptic or circular orbit final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA); final double eCE = rV2OnMu - 1; @@ -348,7 +343,7 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, // we have a relevant acceleration, we can compute derivatives final double[][] jacobian = new double[6][6]; - getJacobianWrtCartesian(PositionAngle.MEAN, jacobian); + getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian); final Vector3D keplerianAcceleration = new Vector3D(-mu / (r * r2), pvP); final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration); @@ -547,9 +542,9 @@ public double getMeanAnomalyDot() { * @param type type of the angle * @return anomaly (rad) */ - public double getAnomaly(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getMeanAnomaly() : - ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomaly() : + public double getAnomaly(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getMeanAnomaly() : + ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomaly() : getTrueAnomaly()); } @@ -558,82 +553,12 @@ public double getAnomaly(final PositionAngle type) { * @return anomaly derivative (rad/s) * @since 9.0 */ - public double getAnomalyDot(final PositionAngle type) { - return (type == PositionAngle.MEAN) ? getMeanAnomalyDot() : - ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomalyDot() : + public double getAnomalyDot(final PositionAngleType type) { + return (type == PositionAngleType.MEAN) ? getMeanAnomalyDot() : + ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomalyDot() : getTrueAnomalyDot()); } - /** Computes the true anomaly from the elliptic eccentric anomaly. - * @param E eccentric anomaly (rad) - * @param e eccentricity - * @return v the true anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticEccentricToTrue(double, double)}. - */ - public static double ellipticEccentricToTrue(final double E, final double e) { - return KeplerianAnomalyUtility.ellipticEccentricToTrue(e, E); - } - - /** Computes the elliptic eccentric anomaly from the true anomaly. - * @param v true anomaly (rad) - * @param e eccentricity - * @return E the elliptic eccentric anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticTrueToEccentric(double, double)}. - */ - public static double trueToEllipticEccentric(final double v, final double e) { - return KeplerianAnomalyUtility.ellipticTrueToEccentric(e, v); - } - - /** Computes the true anomaly from the hyperbolic eccentric anomaly. - * @param H hyperbolic eccentric anomaly (rad) - * @param e eccentricity - * @return v the true anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#hyperbolicEccentricToTrue(double, double)}. - */ - public static double hyperbolicEccentricToTrue(final double H, final double e) { - return KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, H); - } - - /** Computes the hyperbolic eccentric anomaly from the true anomaly. - * @param v true anomaly (rad) - * @param e eccentricity - * @return H the hyperbolic eccentric anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#hyperbolicTrueToEccentric(double, double)}. - */ - public static double trueToHyperbolicEccentric(final double v, final double e) { - return KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, v); - } - - /** Computes the elliptic eccentric anomaly from the mean anomaly. - * @param M mean anomaly (rad) - * @param e eccentricity - * @return E the eccentric anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticMeanToEccentric(double, double)}. - */ - public static double meanToEllipticEccentric(final double M, final double e) { - return KeplerianAnomalyUtility.ellipticMeanToEccentric(e, M); - } - - /** Computes the mean anomaly from the elliptic eccentric anomaly. - * @param E eccentric anomaly (rad) - * @param e eccentricity - * @return M the mean anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticEccentricToMean(double, double)}. - */ - public static double ellipticEccentricToMean(final double E, final double e) { - return KeplerianAnomalyUtility.ellipticEccentricToMean(e, E); - } - - /** Computes the mean anomaly from the hyperbolic eccentric anomaly. - * @param H hyperbolic eccentric anomaly (rad) - * @param e eccentricity - * @return M the mean anomaly - * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#hyperbolicEccentricToMean(double, double)}. - */ - public static double hyperbolicEccentricToMean(final double H, final double e) { - return KeplerianAnomalyUtility.hyperbolicEccentricToMean(e, H); - } - /** {@inheritDoc} */ public double getEquinoctialEx() { return e * FastMath.cos(pa + raan); @@ -728,15 +653,11 @@ public double getLMDot() { return paDot + raanDot + getMeanAnomalyDot(); } - /** Compute position and velocity but not acceleration. + /** Compute reference axes. + * @return referecne axes + * @since 12.0 */ - private void computePVWithoutA() { - - if (partialPV != null) { - // already computed - return; - } - + private Vector3D[] referenceAxes() { // preliminary variables final SinCos scRaan = FastMath.sinCos(raan); final SinCos scPa = FastMath.sinCos(pa); @@ -754,10 +675,25 @@ private void computePVWithoutA() { final double srsp = sinRaan * sinPa; // reference axes defining the orbital plane - final Vector3D p = new Vector3D( crcp - cosI * srsp, srcp + cosI * crsp, sinI * sinPa); - final Vector3D q = new Vector3D(-crsp - cosI * srcp, -srsp + cosI * crcp, sinI * cosPa); + return new Vector3D[] { + new Vector3D( crcp - cosI * srsp, srcp + cosI * crsp, sinI * sinPa), + new Vector3D(-crsp - cosI * srcp, -srsp + cosI * crcp, sinI * cosPa) + }; - if (a > 0) { + } + + /** Compute position and velocity but not acceleration. + */ + private void computePVWithoutA() { + + if (partialPV != null) { + // already computed + return; + } + + final Vector3D[] axes = referenceAxes(); + + if (isElliptical()) { // elliptical case @@ -775,8 +711,8 @@ private void computePVWithoutA() { final double xDot = -sinE * factor; final double yDot = cosE * s1Me2 * factor; - final Vector3D position = new Vector3D(x, p, y, q); - final Vector3D velocity = new Vector3D(xDot, p, yDot, q); + final Vector3D position = new Vector3D(x, axes[0], y, axes[1]); + final Vector3D velocity = new Vector3D(xDot, axes[0], yDot, axes[1]); partialPV = new PVCoordinates(position, velocity); } else { @@ -796,8 +732,8 @@ private void computePVWithoutA() { final double xDot = -velFactor * sinV; final double yDot = velFactor * (e + cosV); - final Vector3D position = new Vector3D(x, p, y, q); - final Vector3D velocity = new Vector3D(xDot, p, yDot, q); + final Vector3D position = new Vector3D(x, axes[0], y, axes[1]); + final Vector3D velocity = new Vector3D(xDot, axes[0], yDot, axes[1]); partialPV = new PVCoordinates(position, velocity); } @@ -813,7 +749,7 @@ private void computePVWithoutA() { private Vector3D nonKeplerianAcceleration() { final double[][] dCdP = new double[6][6]; - getJacobianWrtParameters(PositionAngle.MEAN, dCdP); + getJacobianWrtParameters(PositionAngleType.MEAN, dCdP); final double nonKeplerianMeanMotion = getMeanAnomalyDot() - getKeplerianMeanMotion(); final double nonKeplerianAx = dCdP[3][0] * aDot + dCdP[3][1] * eDot + dCdP[3][2] * iDot + @@ -827,6 +763,41 @@ private Vector3D nonKeplerianAcceleration() { } + /** {@inheritDoc} */ + protected Vector3D initPosition() { + + final Vector3D[] axes = referenceAxes(); + + if (isElliptical()) { + + // elliptical case + + // elliptic eccentric anomaly + final double uME2 = (1 - e) * (1 + e); + final double s1Me2 = FastMath.sqrt(uME2); + final SinCos scE = FastMath.sinCos(getEccentricAnomaly()); + final double cosE = scE.cos(); + final double sinE = scE.sin(); + + return new Vector3D(a * (cosE - e), axes[0], a * sinE * s1Me2, axes[1]); + + } else { + + // hyperbolic case + + // compute position and velocity factors + final SinCos scV = FastMath.sinCos(v); + final double sinV = scV.sin(); + final double cosV = scV.cos(); + final double f = a * (1 - e * e); + final double posFactor = f / (1 + e * cosV); + + return new Vector3D(posFactor * cosV, axes[0], posFactor * sinV, axes[1]); + + } + + } + /** {@inheritDoc} */ protected TimeStampedPVCoordinates initPVCoordinates() { @@ -850,7 +821,7 @@ public KeplerianOrbit shiftedBy(final double dt) { // use Keplerian-only motion final KeplerianOrbit keplerianShifted = new KeplerianOrbit(a, e, i, pa, raan, getMeanAnomaly() + getKeplerianMeanMotion() * dt, - PositionAngle.MEAN, getFrame(), + PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -881,108 +852,9 @@ PositionAngle.MEAN, getFrame(), } - /** {@inheritDoc} - *

          - * The interpolated instance is created by polynomial Hermite interpolation - * on Keplerian elements, without derivatives (which means the interpolation - * falls back to Lagrange interpolation only). - *

          - *

          - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

          - *

          - * If orbit interpolation on large samples is needed, using the {@link - * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this - * low-level interpolation. The Ephemeris class automatically handles selection of - * a neighboring sub-sample with a predefined number of point from a large global sample - * in a thread-safe way. - *

          - */ - public KeplerianOrbit interpolate(final AbsoluteDate date, final Stream sample) { - - // first pass to check if derivatives are available throughout the sample - final List list = sample.collect(Collectors.toList()); - boolean useDerivatives = true; - for (final Orbit orbit : list) { - useDerivatives = useDerivatives && orbit.hasDerivatives(); - } - - // set up an interpolator - final HermiteInterpolator interpolator = new HermiteInterpolator(); - - // second pass to feed interpolator - AbsoluteDate previousDate = null; - double previousPA = Double.NaN; - double previousRAAN = Double.NaN; - double previousM = Double.NaN; - for (final Orbit orbit : list) { - final KeplerianOrbit kep = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); - final double continuousPA; - final double continuousRAAN; - final double continuousM; - if (previousDate == null) { - continuousPA = kep.getPerigeeArgument(); - continuousRAAN = kep.getRightAscensionOfAscendingNode(); - continuousM = kep.getMeanAnomaly(); - } else { - final double dt = kep.getDate().durationFrom(previousDate); - final double keplerM = previousM + kep.getKeplerianMeanMotion() * dt; - continuousPA = MathUtils.normalizeAngle(kep.getPerigeeArgument(), previousPA); - continuousRAAN = MathUtils.normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN); - continuousM = MathUtils.normalizeAngle(kep.getMeanAnomaly(), keplerM); - } - previousDate = kep.getDate(); - previousPA = continuousPA; - previousRAAN = continuousRAAN; - previousM = continuousM; - if (useDerivatives) { - interpolator.addSamplePoint(kep.getDate().durationFrom(date), - new double[] { - kep.getA(), - kep.getE(), - kep.getI(), - continuousPA, - continuousRAAN, - continuousM - }, new double[] { - kep.getADot(), - kep.getEDot(), - kep.getIDot(), - kep.getPerigeeArgumentDot(), - kep.getRightAscensionOfAscendingNodeDot(), - kep.getMeanAnomalyDot() - }); - } else { - interpolator.addSamplePoint(kep.getDate().durationFrom(date), - new double[] { - kep.getA(), - kep.getE(), - kep.getI(), - continuousPA, - continuousRAAN, - continuousM - }); - } - } - - // interpolate - final double[][] interpolated = interpolator.derivatives(0.0, 1); - - // build a new interpolated instance - return new KeplerianOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2], - interpolated[0][3], interpolated[0][4], interpolated[0][5], - interpolated[1][0], interpolated[1][1], interpolated[1][2], - interpolated[1][3], interpolated[1][4], interpolated[1][5], - PositionAngle.MEAN, getFrame(), date, getMu()); - - } - /** {@inheritDoc} */ protected double[][] computeJacobianMeanWrtCartesian() { - if (a > 0) { + if (isElliptical()) { return computeJacobianMeanWrtCartesianElliptical(); } else { return computeJacobianMeanWrtCartesianHyperbolic(); @@ -1257,7 +1129,7 @@ private double[][] computeJacobianMeanWrtCartesianHyperbolic() { /** {@inheritDoc} */ protected double[][] computeJacobianEccentricWrtCartesian() { - if (a > 0) { + if (isElliptical()) { return computeJacobianEccentricWrtCartesianElliptical(); } else { return computeJacobianEccentricWrtCartesianHyperbolic(); @@ -1330,7 +1202,7 @@ private double[][] computeJacobianEccentricWrtCartesianHyperbolic() { /** {@inheritDoc} */ protected double[][] computeJacobianTrueWrtCartesian() { - if (a > 0) { + if (isElliptical()) { return computeJacobianTrueWrtCartesianElliptical(); } else { return computeJacobianTrueWrtCartesianHyperbolic(); @@ -1414,7 +1286,7 @@ private double[][] computeJacobianTrueWrtCartesianHyperbolic() { } /** {@inheritDoc} */ - public void addKeplerContribution(final PositionAngle type, final double gm, + public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final double oMe2; final double ksi; @@ -1453,6 +1325,26 @@ public String toString() { append(";}").toString(); } + /** {@inheritDoc} */ + @Override + public PositionAngleType getCachedPositionAngleType() { + return PositionAngleType.TRUE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasRates() { + return hasDerivatives(); + } + + /** {@inheritDoc} */ + @Override + public KeplerianOrbit removeRates() { + final PositionAngleType positionAngleType = getCachedPositionAngleType(); + return new KeplerianOrbit(getA(), getE(), getI(), getPerigeeArgument(), getRightAscensionOfAscendingNode(), + getAnomaly(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + } + /** Check if the given parameter is within an acceptable range. * The bounds are inclusive: an exception is raised when either of those conditions are met: *
            @@ -1541,12 +1433,12 @@ private Object readResolve() { // we have derivatives return new KeplerianOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], - PositionAngle.TRUE, + PositionAngleType.TRUE, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } else { // we don't have derivatives - return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngle.TRUE, + return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngleType.TRUE, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } diff --git a/src/main/java/org/orekit/orbits/LibrationOrbit.java b/src/main/java/org/orekit/orbits/LibrationOrbit.java index 405b591877..61ddc321f9 100644 --- a/src/main/java/org/orekit/orbits/LibrationOrbit.java +++ b/src/main/java/org/orekit/orbits/LibrationOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,15 +16,16 @@ */ package org.orekit.orbits; +import org.hipparchus.complex.Complex; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.linear.EigenDecomposition; +import org.hipparchus.linear.EigenDecompositionNonSymmetric; +import org.hipparchus.linear.FieldVector; +import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.hipparchus.linear.RealVector; -import org.orekit.attitudes.AttitudeProvider; import org.orekit.bodies.CR3BPSystem; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.numerical.cr3bp.STMEquations; -import org.orekit.time.TimeScale; import org.orekit.utils.PVCoordinates; /** @@ -91,80 +92,41 @@ public void applyDifferentialCorrection() { orbitalPeriod = diff.getOrbitalPeriod(); } - /** Apply differential correction. - *

            - * This will update {@link #initialPV} and - * {@link #orbitalPeriod} parameters. - *

            - * @param attitudeProvider the attitude law for the numerocal propagator - * @param utc UTC time scale - * @deprecated as of 11.1, replaced by {@link #applyDifferentialCorrection()} - */ - @Deprecated - public void applyDifferentialCorrection(final AttitudeProvider attitudeProvider, final TimeScale utc) { - applyDifferentialCorrection(); - } - /** Return a manifold direction from one position on a libration Orbit. * @param s SpacecraftState with additional equations * @param isStable true if the manifold is stable * @return manifold first guess Position-Velocity of a point on the libration Orbit */ - public PVCoordinates getManifolds(final SpacecraftState s, - final boolean isStable) { - return isStable ? getStableManifolds(s) : getUnstableManifolds(s); - } + public PVCoordinates getManifolds(final SpacecraftState s, final boolean isStable) { - /** Return the stable manifold direction for several positions on a libration Orbit. - * @param s SpacecraftStates (with STM equations) to compute from - * @return Stable manifold first direction from a point on the libration Orbit - */ - private PVCoordinates getStableManifolds(final SpacecraftState s) { + // Get the index of the eigen vector of the state transition matrix, + // depending on the stability or unstability of the manifold + final int eigenVectorIndex = isStable ? 1 : 0; // Small delta, linked to the characteristic velocity of the CR3BP system final double epsilon = syst.getVdim() * 1E2 / syst.getDdim(); - // Get Normalize eigen vector linked to the stability of the manifold - final RealMatrix phi = new STMEquations(syst).getStateTransitionMatrix(s); - final RealVector eigenVector = new EigenDecomposition(phi).getEigenvector(1).unitVector(); - - // New PVCoordinates following the manifold - return new PVCoordinates(s.getPVCoordinates().getPosition() - .add(new Vector3D(eigenVector.getEntry(0), eigenVector - .getEntry(1), eigenVector.getEntry(2)) - .scalarMultiply(epsilon)), s.getPVCoordinates() - .getVelocity() - .add(new Vector3D(eigenVector.getEntry(3), - eigenVector.getEntry(4), - eigenVector.getEntry(5)) - .scalarMultiply(epsilon))); - } - - /** Get the Unstable manifold direction for several positions on a libration Orbit. - * @param s spacecraft state (with STM equations) to compute from - * @return pv coordinates representing the unstable manifold first direction - * from a point on the libration Orbit - */ - private PVCoordinates getUnstableManifolds(final SpacecraftState s) { + // Get monodromy (i.e. state transition) matrix and its eigen decomposition + final RealMatrix phi = new STMEquations(syst).getStateTransitionMatrix(s); + final EigenDecompositionNonSymmetric eigen = new EigenDecompositionNonSymmetric(phi); - // Small delta, linked to the characteristic velocity of the CR3BP system - final double epsilon = - syst.getVdim() * 1E2 / syst.getDdim(); + // Get normalized eigen vector linked to the stability of the manifold + final FieldVector cv = eigen.getEigenvector(eigenVectorIndex); - // Get Normalize eigen vector linked to the stability of the manifold - final RealMatrix phi = new STMEquations(syst).getStateTransitionMatrix(s); - final RealVector eigenVector = new EigenDecomposition(phi).getEigenvector(0).unitVector(); + // Get real vector value and normalize + final RealVector rv = MatrixUtils.createRealVector(cv.getDimension()); + for (int i = 0; i < cv.getDimension(); ++i) { + rv.setEntry(i, cv.getEntry(i).getRealPart()); + } + final RealVector eigenVector = rv.unitVector(); // New PVCoordinates following the manifold - return new PVCoordinates(s.getPVCoordinates().getPosition() - .add(new Vector3D(eigenVector.getEntry(0), eigenVector - .getEntry(1), eigenVector.getEntry(2)) - .scalarMultiply(epsilon)), s.getPVCoordinates() - .getVelocity() - .add(new Vector3D(eigenVector.getEntry(3), - eigenVector.getEntry(4), - eigenVector.getEntry(5)) - .scalarMultiply(epsilon))); + return new PVCoordinates(s.getPosition().add(new Vector3D(eigenVector.getEntry(0), + eigenVector.getEntry(1), + eigenVector.getEntry(2)).scalarMultiply(epsilon)), + s.getPVCoordinates().getVelocity().add(new Vector3D(eigenVector.getEntry(3), + eigenVector.getEntry(4), + eigenVector.getEntry(5)).scalarMultiply(epsilon))); } /** diff --git a/src/main/java/org/orekit/orbits/LibrationOrbitFamily.java b/src/main/java/org/orekit/orbits/LibrationOrbitFamily.java index c467f494b8..a558f391ab 100644 --- a/src/main/java/org/orekit/orbits/LibrationOrbitFamily.java +++ b/src/main/java/org/orekit/orbits/LibrationOrbitFamily.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/orbits/LibrationOrbitType.java b/src/main/java/org/orekit/orbits/LibrationOrbitType.java index fb7b179e31..562054af2b 100644 --- a/src/main/java/org/orekit/orbits/LibrationOrbitType.java +++ b/src/main/java/org/orekit/orbits/LibrationOrbitType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/orbits/LyapunovOrbit.java b/src/main/java/org/orekit/orbits/LyapunovOrbit.java index 45d1488a40..f4c41e2fae 100644 --- a/src/main/java/org/orekit/orbits/LyapunovOrbit.java +++ b/src/main/java/org/orekit/orbits/LyapunovOrbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/orbits/Orbit.java b/src/main/java/org/orekit/orbits/Orbit.java index 0cd94bc7b3..ed61668926 100644 --- a/src/main/java/org/orekit/orbits/Orbit.java +++ b/src/main/java/org/orekit/orbits/Orbit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,9 +29,9 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeInterpolable; import org.orekit.time.TimeShiftable; import org.orekit.time.TimeStamped; import org.orekit.utils.PVCoordinates; @@ -62,8 +62,7 @@ * @author Véronique Pommier-Maurussane */ public abstract class Orbit - implements TimeStamped, TimeShiftable, TimeInterpolable, - Serializable, PVCoordinatesProvider { + implements TimeStamped, TimeShiftable, Serializable, PVCoordinatesProvider { /** Serializable UID. */ private static final long serialVersionUID = 438733454597999578L; @@ -77,6 +76,11 @@ public abstract class Orbit /** Value of mu used to compute position and velocity (m³/s²). */ private final double mu; + /** Computed position. + * @since 12.0 + */ + private transient Vector3D position; + /** Computed PVCoordinates. */ private transient TimeStampedPVCoordinates pvCoordinates; @@ -185,6 +189,14 @@ protected static boolean hasNonKeplerianAcceleration(final PVCoordinates pva, fi } } + /** Returns true if and only if the orbit is elliptical i.e. has a non-negative semi-major axis. + * @return true if getA() is strictly greater than 0 + * @since 12.0 + */ + public boolean isElliptical() { + return getA() > 0.; + } + /** Get the orbit type. * @return orbit type */ @@ -396,7 +408,7 @@ public double getMu() { */ public double getKeplerianPeriod() { final double a = getA(); - return (a < 0) ? Double.POSITIVE_INFINITY : 2.0 * FastMath.PI * a * FastMath.sqrt(a / mu); + return isElliptical() ? 2.0 * FastMath.PI * a * FastMath.sqrt(a / mu) : Double.POSITIVE_INFINITY; } /** Get the Keplerian mean motion. @@ -409,6 +421,13 @@ public double getKeplerianMeanMotion() { return FastMath.sqrt(mu / absA) / absA; } + /** Get the derivative of the mean anomaly with respect to the semi major axis. + * @return derivative of the mean anomaly with respect to the semi major axis + */ + public double getMeanAnomalyDotWrtA() { + return -1.5 * getKeplerianMeanMotion() / getA(); + } + /** Get the date of orbital parameters. * @return date of the orbital parameters */ @@ -442,6 +461,40 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate otherDate, f return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame); } + /** Get the position in a specified frame. + * @param outputFrame frame in which the position coordinates shall be computed + * @return position in the specified output frame + * @see #getPosition() + * @since 12.0 + */ + public Vector3D getPosition(final Frame outputFrame) { + if (position == null) { + position = initPosition(); + } + + // If output frame requested is the same as definition frame, + // Position vector is returned directly + if (outputFrame == frame) { + return position; + } + + // Else, position vector is transformed to output frame + final StaticTransform t = frame.getStaticTransformTo(outputFrame, date); + return t.transformPosition(position); + + } + + /** Get the position in definition frame. + * @return position in the definition frame + * @see #getPVCoordinates() + * @since 12.0 + */ + public Vector3D getPosition() { + if (position == null) { + position = initPosition(); + } + return position; + } /** Get the {@link TimeStampedPVCoordinates} in definition frame. * @return pvCoordinates in the definition frame @@ -450,10 +503,17 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate otherDate, f public TimeStampedPVCoordinates getPVCoordinates() { if (pvCoordinates == null) { pvCoordinates = initPVCoordinates(); + position = pvCoordinates.getPosition(); } return pvCoordinates; } + /** Compute the position coordinates from the canonical parameters. + * @return computed position coordinates + * @since 12.0 + */ + protected abstract Vector3D initPosition(); + /** Compute the position/velocity coordinates from the canonical parameters. * @return computed position/velocity coordinates */ @@ -483,7 +543,7 @@ public TimeStampedPVCoordinates getPVCoordinates() { * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix * is larger than 6x6, only the 6x6 upper left corner will be modified */ - public void getJacobianWrtCartesian(final PositionAngle type, final double[][] jacobian) { + public void getJacobianWrtCartesian(final PositionAngleType type, final double[][] jacobian) { final double[][] cachedJacobian; synchronized (this) { @@ -531,7 +591,7 @@ public void getJacobianWrtCartesian(final PositionAngle type, final double[][] j * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix * is larger than 6x6, only the 6x6 upper left corner will be modified */ - public void getJacobianWrtParameters(final PositionAngle type, final double[][] jacobian) { + public void getJacobianWrtParameters(final PositionAngleType type, final double[][] jacobian) { final double[][] cachedJacobian; synchronized (this) { @@ -573,7 +633,7 @@ public void getJacobianWrtParameters(final PositionAngle type, final double[][] * @param type type of the position angle to use * @return inverse Jacobian */ - private double[][] createInverseJacobian(final PositionAngle type) { + private double[][] createInverseJacobian(final PositionAngleType type) { // get the direct Jacobian final double[][] directJacobian = new double[6][6]; @@ -592,6 +652,9 @@ private double[][] createInverseJacobian(final PositionAngle type) { * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot. *

            + *

            + * The array returned by this method will not be modified. + *

            * @return 6x6 Jacobian matrix * @see #computeJacobianEccentricWrtCartesian() * @see #computeJacobianTrueWrtCartesian() @@ -604,6 +667,9 @@ private double[][] createInverseJacobian(final PositionAngle type) { * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot. *

            + *

            + * The array returned by this method will not be modified. + *

            * @return 6x6 Jacobian matrix * @see #computeJacobianMeanWrtCartesian() * @see #computeJacobianTrueWrtCartesian() @@ -616,6 +682,9 @@ private double[][] createInverseJacobian(final PositionAngle type) { * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot. *

            + *

            + * The array returned by this method will not be modified. + *

            * @return 6x6 Jacobian matrix * @see #computeJacobianMeanWrtCartesian() * @see #computeJacobianEccentricWrtCartesian() @@ -633,7 +702,7 @@ private double[][] createInverseJacobian(final PositionAngle type) { * part must be added to the array components, as the array may already * contain some non-zero elements corresponding to non-Keplerian parts) */ - public abstract void addKeplerContribution(PositionAngle type, double gm, double[] pDot); + public abstract void addKeplerContribution(PositionAngleType type, double gm, double[] pDot); /** Fill a Jacobian half row with a single vector. * @param a coefficient of the vector diff --git a/src/main/java/org/orekit/orbits/OrbitBlender.java b/src/main/java/org/orekit/orbits/OrbitBlender.java new file mode 100644 index 0000000000..72b97ced91 --- /dev/null +++ b/src/main/java/org/orekit/orbits/OrbitBlender.java @@ -0,0 +1,134 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +import org.hipparchus.analysis.polynomials.SmoothStepFactory; +import org.orekit.errors.OrekitException; +import org.orekit.frames.Frame; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinates; + +import java.util.List; + +/** + * Orbit blender. + *

            + * Its purpose is to interpolate orbit state between tabulated orbit states using the concept of blending, exposed in : + * "Efficient Covariance Interpolation using Blending of Approximate State Error Transitions" by Sergei Tanygin, and applying + * it to orbit states instead of covariances. + *

            + * It propagates tabulated values to the interpolating time using given propagator and then blend each propagated + * states using a smoothstep function. It gives especially good results as explained + * here + * compared to Hermite interpolation when time steps between tabulated values get significant (In LEO, > 10 mn for + * example). + *

            + * In most cases, an analytical propagator would be used to quickly fill the gap between tabulated values and recreate + * a dense ephemeris. + *

            + * However, a fully configured and accurate numerical propagator can be used to recreate an even + * more precise ephemeris in case the initial tabulated values were obtained from an external source. + *

            + * Note that in the current implementation, the returned blended orbit is necessarily Cartesian. + * + * @author Vincent Cucchietti + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory.SmoothStepFunction + * @see Propagator + * @see AbstractAnalyticalPropagator + * + * @since 12.0 + */ +public class OrbitBlender extends AbstractOrbitInterpolator { + + /** Propagator used to propagate tabulated orbits to interpolating time. */ + private final Propagator blendingPropagator; + + /** Blending function. */ + private final SmoothStepFactory.SmoothStepFunction blendingFunction; + + /** + * Default constructor. + *

            + * In most cases, an analytical propagator would be used to quickly fill the gap between tabulated values and recreate + * a dense ephemeris. + *

            + * However, a fully configured and accurate numerical propagator can be used to recreate an even + * more precise ephemeris in case the initial tabulated values were obtained from an external source. + * + * @param blendingFunction + * {@link org.hipparchus.analysis.polynomials.SmoothStepFactory.SmoothStepFunction smoothstep function} used for + * blending + * @param blendingPropagator propagator used to propagate tabulated orbits to interpolating time + * @param outputInertialFrame output inertial frame + * + * @throws OrekitException if output frame is not inertial + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory.SmoothStepFunction + */ + public OrbitBlender(final SmoothStepFactory.SmoothStepFunction blendingFunction, + final Propagator blendingPropagator, + final Frame outputInertialFrame) { + super(DEFAULT_INTERPOLATION_POINTS, 0., outputInertialFrame); + this.blendingFunction = blendingFunction; + this.blendingPropagator = blendingPropagator; + } + + /** {@inheritDoc} */ + @Override + protected Orbit interpolate(final InterpolationData interpolationData) { + + // Get first and last entry + final List neighborList = interpolationData.getNeighborList(); + final Orbit previousOrbit = neighborList.get(0); + final Orbit nextOrbit = neighborList.get(1); + + // Propagate orbits + final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final Orbit forwardedOrbit = propagateOrbit(previousOrbit, interpolationDate); + final Orbit backwardedOrbit = propagateOrbit(nextOrbit, interpolationDate); + + // Extract position-velocity-acceleration coordinates + final PVCoordinates forwardedPV = forwardedOrbit.getPVCoordinates(getOutputInertialFrame()); + final PVCoordinates backwardedPV = backwardedOrbit.getPVCoordinates(getOutputInertialFrame()); + + // Blend PV coordinates + final double timeParameter = getTimeParameter(interpolationDate, previousOrbit.getDate(), nextOrbit.getDate()); + final double blendingValue = blendingFunction.value(timeParameter); + + final PVCoordinates blendedPV = forwardedPV.blendArithmeticallyWith(backwardedPV, blendingValue); + + // Output new blended instance + return new CartesianOrbit(blendedPV, getOutputInertialFrame(), interpolationDate, previousOrbit.getMu()); + } + + /** + * Propagate orbit using predefined {@link Propagator propagator}. + * + * @param tabulatedOrbit tabulated orbit to propagate + * @param propagationDate propagation date + * + * @return orbit propagated to propagation date + */ + private Orbit propagateOrbit(final Orbit tabulatedOrbit, + final AbsoluteDate propagationDate) { + blendingPropagator.resetInitialState(new SpacecraftState(tabulatedOrbit)); + return blendingPropagator.propagate(propagationDate).getOrbit(); + } +} diff --git a/src/main/java/org/orekit/orbits/OrbitHermiteInterpolator.java b/src/main/java/org/orekit/orbits/OrbitHermiteInterpolator.java new file mode 100644 index 0000000000..67ef30cc06 --- /dev/null +++ b/src/main/java/org/orekit/orbits/OrbitHermiteInterpolator.java @@ -0,0 +1,450 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.util.MathUtils; +import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeInterpolator; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.TimeStampedPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; + +import java.util.List; +import java.util.stream.Stream; + +/** + * Class using a Hermite interpolator to interpolate orbits. + *

            + * Depending on given sample orbit type, the interpolation may differ : + *

              + *
            • For Keplerian, Circular and Equinoctial orbits, the interpolated instance is created by polynomial Hermite + * interpolation, using derivatives when available.
            • + *
            • For Cartesian orbits, the interpolated instance is created using the cartesian derivatives filter given at + * instance construction. Hence, it will fall back to Lagrange interpolation if this instance has been designed to not + * use derivatives. + *
            + *

            + * In any case, it should be used only with small number of interpolation points (about 10-20 points) in order to avoid + * Runge's phenomenon and numerical problems + * (including NaN appearing). + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see Orbit + * @see HermiteInterpolator + */ +public class OrbitHermiteInterpolator extends AbstractOrbitInterpolator { + + /** Filter for derivatives from the sample to use in position-velocity-acceleration interpolation. */ + private final CartesianDerivativesFilter pvaFilter; + + /** + * Constructor with : + *

              + *
            • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
            • + *
            • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
            • + *
            • Use of position and two time derivatives during interpolation
            • + *
            + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param outputInertialFrame output inertial frame + */ + public OrbitHermiteInterpolator(final Frame outputInertialFrame) { + this(DEFAULT_INTERPOLATION_POINTS, outputInertialFrame); + } + + /** + * Constructor with : + *
              + *
            • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
            • + *
            • Use of position and two time derivatives during interpolation
            • + *
            + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputInertialFrame output inertial frame + */ + public OrbitHermiteInterpolator(final int interpolationPoints, final Frame outputInertialFrame) { + this(interpolationPoints, outputInertialFrame, CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

            + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputInertialFrame output inertial frame + * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation. Used + * only when interpolating Cartesian orbits. + */ + public OrbitHermiteInterpolator(final int interpolationPoints, final Frame outputInertialFrame, + final CartesianDerivativesFilter pvaFilter) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputInertialFrame, pvaFilter); + } + + /** + * Constructor. + *

            + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputInertialFrame output inertial frame + * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation. Used + * only when interpolating Cartesian orbits. + */ + public OrbitHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputInertialFrame, final CartesianDerivativesFilter pvaFilter) { + super(interpolationPoints, extrapolationThreshold, outputInertialFrame); + this.pvaFilter = pvaFilter; + } + + /** Get filter for derivatives from the sample to use in position-velocity-acceleration interpolation. + * @return filter for derivatives from the sample to use in position-velocity-acceleration interpolation + */ + public CartesianDerivativesFilter getPVAFilter() { + return pvaFilter; + } + + /** + * {@inheritDoc} + *

            + * Depending on given sample orbit type, the interpolation may differ : + *

              + *
            • For Keplerian, Circular and Equinoctial orbits, the interpolated instance is created by polynomial Hermite + * interpolation, using derivatives when available.
            • + *
            • For Cartesian orbits, the interpolated instance is created using the cartesian derivatives filter given at + * instance construction. Hence, it will fall back to Lagrange interpolation if this instance has been designed to not + * use derivatives. + *
            + * If orbit interpolation on large samples is needed, using the {@link + * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this + * low-level interpolation. The Ephemeris class automatically handles selection of + * a neighboring sub-sample with a predefined number of point from a large global sample + * in a thread-safe way. + * + * @param interpolationData interpolation data + * + * @return interpolated instance at given date + */ + @Override + protected Orbit interpolate(final InterpolationData interpolationData) { + // Get orbit sample + final List sample = interpolationData.getNeighborList(); + + // Get orbit type for interpolation + final OrbitType orbitType = sample.get(0).getType(); + + if (orbitType == OrbitType.CARTESIAN) { + return interpolateCartesian(interpolationData.getInterpolationDate(), sample); + } + else { + return interpolateCommon(interpolationData.getInterpolationDate(), sample, orbitType); + } + + } + + /** + * Interpolate Cartesian orbit using specific method for Cartesian orbit. + * + * @param interpolationDate interpolation date + * @param sample orbits sample + * + * @return interpolated Cartesian orbit + */ + private CartesianOrbit interpolateCartesian(final AbsoluteDate interpolationDate, final List sample) { + + // Create time stamped position-velocity-acceleration Hermite interpolator + final TimeInterpolator interpolator = + new TimeStampedPVCoordinatesHermiteInterpolator(getNbInterpolationPoints(), getExtrapolationThreshold(), + pvaFilter); + + // Convert sample to stream + final Stream sampleStream = sample.stream(); + + // Map time stamped position-velocity-acceleration coordinates + final Stream sampleTimeStampedPV = sampleStream.map(Orbit::getPVCoordinates); + + // Interpolate PVA + final TimeStampedPVCoordinates interpolated = interpolator.interpolate(interpolationDate, sampleTimeStampedPV); + + // Use first entry gravitational parameter + final double mu = sample.get(0).getMu(); + + return new CartesianOrbit(interpolated, getOutputInertialFrame(), interpolationDate, mu); + } + + /** + * Method gathering common parts of interpolation between circular, equinoctial and keplerian orbit. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param orbitType interpolation method to use + * + * @return interpolated orbit + */ + private Orbit interpolateCommon(final AbsoluteDate interpolationDate, final List orbits, + final OrbitType orbitType) { + + // First pass to check if derivatives are available throughout the sample + boolean useDerivatives = true; + for (final Orbit orbit : orbits) { + useDerivatives = useDerivatives && orbit.hasDerivatives(); + } + + // Use first entry gravitational parameter + final double mu = orbits.get(0).getMu(); + + // Interpolate and build a new instance + final double[][] interpolated; + switch (orbitType) { + case CIRCULAR: + interpolated = interpolateCircular(interpolationDate, orbits, useDerivatives); + return new CircularOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2], + interpolated[0][3], interpolated[0][4], interpolated[0][5], + interpolated[1][0], interpolated[1][1], interpolated[1][2], + interpolated[1][3], interpolated[1][4], interpolated[1][5], + PositionAngleType.MEAN, getOutputInertialFrame(), interpolationDate, mu); + case KEPLERIAN: + interpolated = interpolateKeplerian(interpolationDate, orbits, useDerivatives); + return new KeplerianOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2], + interpolated[0][3], interpolated[0][4], interpolated[0][5], + interpolated[1][0], interpolated[1][1], interpolated[1][2], + interpolated[1][3], interpolated[1][4], interpolated[1][5], + PositionAngleType.MEAN, getOutputInertialFrame(), interpolationDate, mu); + case EQUINOCTIAL: + interpolated = interpolateEquinoctial(interpolationDate, orbits, useDerivatives); + return new EquinoctialOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2], + interpolated[0][3], interpolated[0][4], interpolated[0][5], + interpolated[1][0], interpolated[1][1], interpolated[1][2], + interpolated[1][3], interpolated[1][4], interpolated[1][5], + PositionAngleType.MEAN, getOutputInertialFrame(), interpolationDate, mu); + default: + // Should never happen + throw new OrekitInternalError(null); + } + + } + + /** + * Build interpolating functions for circular orbit parameters. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param useDerivatives flag defining if derivatives are available throughout the sample + * + * @return interpolating functions for circular orbit parameters + */ + private double[][] interpolateCircular(final AbsoluteDate interpolationDate, final List orbits, + final boolean useDerivatives) { + + // Set up an interpolator + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Second pass to feed interpolator + AbsoluteDate previousDate = null; + double previousRAAN = Double.NaN; + double previousAlphaM = Double.NaN; + for (final Orbit orbit : orbits) { + final CircularOrbit circ = (CircularOrbit) OrbitType.CIRCULAR.convertType(orbit); + final double continuousRAAN; + final double continuousAlphaM; + if (previousDate == null) { + continuousRAAN = circ.getRightAscensionOfAscendingNode(); + continuousAlphaM = circ.getAlphaM(); + } + else { + final double dt = circ.getDate().durationFrom(previousDate); + final double keplerAM = previousAlphaM + circ.getKeplerianMeanMotion() * dt; + continuousRAAN = MathUtils.normalizeAngle(circ.getRightAscensionOfAscendingNode(), previousRAAN); + continuousAlphaM = MathUtils.normalizeAngle(circ.getAlphaM(), keplerAM); + } + previousDate = circ.getDate(); + previousRAAN = continuousRAAN; + previousAlphaM = continuousAlphaM; + if (useDerivatives) { + interpolator.addSamplePoint(circ.getDate().durationFrom(interpolationDate), + new double[] { circ.getA(), + circ.getCircularEx(), + circ.getCircularEy(), + circ.getI(), + continuousRAAN, + continuousAlphaM }, + new double[] { circ.getADot(), + circ.getCircularExDot(), + circ.getCircularEyDot(), + circ.getIDot(), + circ.getRightAscensionOfAscendingNodeDot(), + circ.getAlphaMDot() }); + } + else { + interpolator.addSamplePoint(circ.getDate().durationFrom(interpolationDate), + new double[] { circ.getA(), + circ.getCircularEx(), + circ.getCircularEy(), + circ.getI(), + continuousRAAN, + continuousAlphaM }); + } + } + + return interpolator.derivatives(0.0, 1); + } + + /** + * Build interpolating functions for keplerian orbit parameters. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param useDerivatives flag defining if derivatives are available throughout the sample + * + * @return interpolating functions for keplerian orbit parameters + */ + private double[][] interpolateKeplerian(final AbsoluteDate interpolationDate, final List orbits, + final boolean useDerivatives) { + + // Set up an interpolator + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Second pass to feed interpolator + AbsoluteDate previousDate = null; + double previousPA = Double.NaN; + double previousRAAN = Double.NaN; + double previousM = Double.NaN; + for (final Orbit orbit : orbits) { + final KeplerianOrbit kep = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); + final double continuousPA; + final double continuousRAAN; + final double continuousM; + if (previousDate == null) { + continuousPA = kep.getPerigeeArgument(); + continuousRAAN = kep.getRightAscensionOfAscendingNode(); + continuousM = kep.getMeanAnomaly(); + } + else { + final double dt = kep.getDate().durationFrom(previousDate); + final double keplerM = previousM + kep.getKeplerianMeanMotion() * dt; + continuousPA = MathUtils.normalizeAngle(kep.getPerigeeArgument(), previousPA); + continuousRAAN = MathUtils.normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN); + continuousM = MathUtils.normalizeAngle(kep.getMeanAnomaly(), keplerM); + } + previousDate = kep.getDate(); + previousPA = continuousPA; + previousRAAN = continuousRAAN; + previousM = continuousM; + if (useDerivatives) { + interpolator.addSamplePoint(kep.getDate().durationFrom(interpolationDate), + new double[] { kep.getA(), + kep.getE(), + kep.getI(), + continuousPA, + continuousRAAN, + continuousM }, + new double[] { kep.getADot(), + kep.getEDot(), + kep.getIDot(), + kep.getPerigeeArgumentDot(), + kep.getRightAscensionOfAscendingNodeDot(), + kep.getMeanAnomalyDot() }); + } + else { + interpolator.addSamplePoint(kep.getDate().durationFrom(interpolationDate), + new double[] { kep.getA(), + kep.getE(), + kep.getI(), + continuousPA, + continuousRAAN, + continuousM }); + } + } + + return interpolator.derivatives(0.0, 1); + } + + /** + * Build interpolating functions for equinoctial orbit parameters. + * + * @param interpolationDate interpolation date + * @param orbits orbits sample + * @param useDerivatives flag defining if derivatives are available throughout the sample + * + * @return interpolating functions for equinoctial orbit parameters + */ + private double[][] interpolateEquinoctial(final AbsoluteDate interpolationDate, final List orbits, + final boolean useDerivatives) { + + // Set up an interpolator + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Second pass to feed interpolator + AbsoluteDate previousDate = null; + double previousLm = Double.NaN; + for (final Orbit orbit : orbits) { + final EquinoctialOrbit equi = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit); + final double continuousLm; + if (previousDate == null) { + continuousLm = equi.getLM(); + } + else { + final double dt = equi.getDate().durationFrom(previousDate); + final double keplerLm = previousLm + equi.getKeplerianMeanMotion() * dt; + continuousLm = MathUtils.normalizeAngle(equi.getLM(), keplerLm); + } + previousDate = equi.getDate(); + previousLm = continuousLm; + if (useDerivatives) { + interpolator.addSamplePoint(equi.getDate().durationFrom(interpolationDate), + new double[] { equi.getA(), + equi.getEquinoctialEx(), + equi.getEquinoctialEy(), + equi.getHx(), + equi.getHy(), + continuousLm }, + new double[] { + equi.getADot(), + equi.getEquinoctialExDot(), + equi.getEquinoctialEyDot(), + equi.getHxDot(), + equi.getHyDot(), + equi.getLMDot() }); + } + else { + interpolator.addSamplePoint(equi.getDate().durationFrom(interpolationDate), + new double[] { equi.getA(), + equi.getEquinoctialEx(), + equi.getEquinoctialEy(), + equi.getHx(), + equi.getHy(), + continuousLm }); + } + } + + return interpolator.derivatives(0.0, 1); + } +} diff --git a/src/main/java/org/orekit/orbits/OrbitType.java b/src/main/java/org/orekit/orbits/OrbitType.java index 83136d2c3f..c81693d380 100644 --- a/src/main/java/org/orekit/orbits/OrbitType.java +++ b/src/main/java/org/orekit/orbits/OrbitType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import java.util.Arrays; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; @@ -34,11 +35,11 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.TimeStampedFieldPVCoordinates; -/** Enumerate for {@link Orbit orbital} parameters types. +/** Enumerate for {@link Orbit} and {@link FieldOrbit} parameters types. */ public enum OrbitType { - /** Type for propagation in {@link CartesianOrbit Cartesian parameters}. */ + /** Type for orbital representation in {@link CartesianOrbit} and {@link FieldCartesianOrbit} parameters. */ CARTESIAN { /** {@inheritDoc} */ @@ -49,7 +50,7 @@ public CartesianOrbit convertType(final Orbit orbit) { /** {@inheritDoc} */ @Override - public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, + public void mapOrbitToArray(final Orbit orbit, final PositionAngleType type, final double[] stateVector, final double[] stateVectorDot) { final PVCoordinates pv = orbit.getPVCoordinates(); @@ -77,7 +78,7 @@ public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, /** {@inheritDoc} */ @Override - public CartesianOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngle type, + public CartesianOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngleType type, final AbsoluteDate date, final double mu, final Frame frame) { final Vector3D p = new Vector3D(stateVector[0], stateVector[1], stateVector[2]); @@ -103,7 +104,7 @@ public > FieldCartesianOrbit convertType(fi /** {@inheritDoc} */ @Override public > void mapOrbitToArray(final FieldOrbit orbit, - final PositionAngle type, + final PositionAngleType type, final T[] stateVector, final T[] stateVectorDot) { @@ -134,7 +135,7 @@ public > void mapOrbitToArray(final FieldOrbit @Override public > FieldCartesianOrbit mapArrayToOrbit(final T[] stateVector, final T[] stateVectorDot, - final PositionAngle type, + final PositionAngleType type, final FieldAbsoluteDate date, final T mu, final Frame frame) { final FieldVector3D p = new FieldVector3D<>(stateVector[0], stateVector[1], stateVector[2]); @@ -153,7 +154,14 @@ public > FieldCartesianOrbit mapArrayToOrbi /** {@inheritDoc} */ @Override - public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngle type) { + public > FieldCartesianOrbit convertToFieldOrbit(final Field field, + final Orbit orbit) { + return new FieldCartesianOrbit<>(field, CARTESIAN.convertType(orbit)); + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngleType type) { final ParameterDriversList drivers = new ParameterDriversList(); final double[] array = new double[6]; mapOrbitToArray(orbit, type, array, null); @@ -181,9 +189,15 @@ public > FieldCartesianOrbit normalize(fina return convertType(orbit); } + /** {@inheritDoc} */ + @Override + public boolean isPositionAngleBased() { + return false; + } + }, - /** Type for propagation in {@link CircularOrbit circular parameters}. */ + /** Type for orbital representation in {@link CircularOrbit} and {@link FieldCircularOrbit} parameters. */ CIRCULAR { /** {@inheritDoc} */ @@ -194,7 +208,7 @@ public CircularOrbit convertType(final Orbit orbit) { /** {@inheritDoc} */ @Override - public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, + public void mapOrbitToArray(final Orbit orbit, final PositionAngleType type, final double[] stateVector, final double[] stateVectorDot) { final CircularOrbit circularOrbit = (CircularOrbit) OrbitType.CIRCULAR.convertType(orbit); @@ -223,7 +237,7 @@ public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, /** {@inheritDoc} */ @Override - public CircularOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngle type, + public CircularOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngleType type, final AbsoluteDate date, final double mu, final Frame frame) { if (stateVectorDot == null) { // we don't have orbit derivatives @@ -249,7 +263,7 @@ public > FieldCircularOrbit convertType(fin /** {@inheritDoc} */ @Override public > void mapOrbitToArray(final FieldOrbit orbit, - final PositionAngle type, + final PositionAngleType type, final T[] stateVector, final T[] stateVectorDot) { @@ -271,7 +285,7 @@ public > void mapOrbitToArray(final FieldOrbit stateVectorDot[4] = circularOrbit.getRightAscensionOfAscendingNodeDot(); stateVectorDot[5] = circularOrbit.getAlphaDot(type); } else { - Arrays.fill(stateVectorDot, 0, 6, orbit.getDate().getField().getZero().add(Double.NaN)); + Arrays.fill(stateVectorDot, 0, 6, orbit.getZero().add(Double.NaN)); } } @@ -280,7 +294,7 @@ public > void mapOrbitToArray(final FieldOrbit /** {@inheritDoc} */ @Override public > FieldCircularOrbit mapArrayToOrbit(final T[] stateVector, - final T[] stateVectorDot, final PositionAngle type, + final T[] stateVectorDot, final PositionAngleType type, final FieldAbsoluteDate date, final T mu, final Frame frame) { if (stateVectorDot == null) { @@ -300,14 +314,21 @@ public > FieldCircularOrbit mapArrayToOrbit /** {@inheritDoc} */ @Override - public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngle type) { + public > FieldCircularOrbit convertToFieldOrbit(final Field field, + final Orbit orbit) { + return new FieldCircularOrbit<>(field, CIRCULAR.convertType(orbit)); + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngleType type) { final ParameterDriversList drivers = new ParameterDriversList(); final double[] array = new double[6]; mapOrbitToArray(orbit, type, array, null); final double[] scale = scale(dP, orbit); - final String name = type == PositionAngle.MEAN ? + final String name = type == PositionAngleType.MEAN ? MEAN_LAT_ARG : - type == PositionAngle.ECCENTRIC ? ECC_LAT_ARG : TRUE_LAT_ARG; + type == PositionAngleType.ECCENTRIC ? ECC_LAT_ARG : TRUE_LAT_ARG; drivers.add(new ParameterDriver(A, array[0], scale[0], 0.0, Double.POSITIVE_INFINITY)); drivers.add(new ParameterDriver(E_X, array[1], scale[1], -1.0, 1.0)); drivers.add(new ParameterDriver(E_Y, array[2], scale[2], -1.0, 1.0)); @@ -339,7 +360,7 @@ public CircularOrbit normalize(final Orbit orbit, final Orbit reference) { cO.getIDot(), cO.getRightAscensionOfAscendingNodeDot(), cO.getAlphaVDot(), - PositionAngle.TRUE, + PositionAngleType.TRUE, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -350,7 +371,7 @@ public CircularOrbit normalize(final Orbit orbit, final Orbit reference) { cO.getI(), MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), cR.getRightAscensionOfAscendingNode()), MathUtils.normalizeAngle(cO.getAlphaV(), cR.getAlphaV()), - PositionAngle.TRUE, + PositionAngleType.TRUE, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -380,7 +401,7 @@ public > FieldCircularOrbit normalize(final cO.getIDot(), cO.getRightAscensionOfAscendingNodeDot(), cO.getAlphaVDot(), - PositionAngle.TRUE, + PositionAngleType.TRUE, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -391,7 +412,7 @@ public > FieldCircularOrbit normalize(final cO.getI(), MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), cR.getRightAscensionOfAscendingNode()), MathUtils.normalizeAngle(cO.getAlphaV(), cR.getAlphaV()), - PositionAngle.TRUE, + PositionAngleType.TRUE, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -399,9 +420,15 @@ public > FieldCircularOrbit normalize(final } + /** {@inheritDoc} */ + @Override + public boolean isPositionAngleBased() { + return true; + } + }, - /** Type for propagation in {@link EquinoctialOrbit equinoctial parameters}. */ + /** Type for orbital representation in {@link EquinoctialOrbit} and {@link FieldEquinoctialOrbit} parameters. */ EQUINOCTIAL { /** {@inheritDoc} */ @@ -412,7 +439,7 @@ public EquinoctialOrbit convertType(final Orbit orbit) { /** {@inheritDoc} */ @Override - public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, + public void mapOrbitToArray(final Orbit orbit, final PositionAngleType type, final double[] stateVector, final double[] stateVectorDot) { final EquinoctialOrbit equinoctialOrbit = @@ -442,7 +469,7 @@ public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, /** {@inheritDoc} */ @Override - public EquinoctialOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngle type, + public EquinoctialOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngleType type, final AbsoluteDate date, final double mu, final Frame frame) { if (stateVectorDot == null) { // we don't have orbit derivatives @@ -468,7 +495,7 @@ public > FieldEquinoctialOrbit convertType( /** {@inheritDoc} */ @Override public > void mapOrbitToArray(final FieldOrbit orbit, - final PositionAngle type, + final PositionAngleType type, final T[] stateVector, final T[] stateVectorDot) { @@ -491,7 +518,7 @@ public > void mapOrbitToArray(final FieldOrbit stateVectorDot[4] = equinoctialOrbit.getHyDot(); stateVectorDot[5] = equinoctialOrbit.getLDot(type); } else { - Arrays.fill(stateVectorDot, 0, 6, orbit.getDate().getField().getZero().add(Double.NaN)); + Arrays.fill(stateVectorDot, 0, 6, orbit.getZero().add(Double.NaN)); } } @@ -501,7 +528,7 @@ public > void mapOrbitToArray(final FieldOrbit @Override public > FieldEquinoctialOrbit mapArrayToOrbit(final T[] stateVector, final T[] stateVectorDot, - final PositionAngle type, + final PositionAngleType type, final FieldAbsoluteDate date, final T mu, final Frame frame) { if (stateVectorDot == null) { @@ -521,14 +548,21 @@ public > FieldEquinoctialOrbit mapArrayToOr /** {@inheritDoc} */ @Override - public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngle type) { + public > FieldEquinoctialOrbit convertToFieldOrbit(final Field field, + final Orbit orbit) { + return new FieldEquinoctialOrbit<>(field, EQUINOCTIAL.convertType(orbit)); + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngleType type) { final ParameterDriversList drivers = new ParameterDriversList(); final double[] array = new double[6]; mapOrbitToArray(orbit, type, array, null); final double[] scale = scale(dP, orbit); - final String name = type == PositionAngle.MEAN ? + final String name = type == PositionAngleType.MEAN ? MEAN_LON_ARG : - type == PositionAngle.ECCENTRIC ? ECC_LON_ARG : TRUE_LON_ARG; + type == PositionAngleType.ECCENTRIC ? ECC_LON_ARG : TRUE_LON_ARG; drivers.add(new ParameterDriver(A, array[0], scale[0], 0.0, Double.POSITIVE_INFINITY)); drivers.add(new ParameterDriver(E_X, array[1], scale[1], -1.0, 1.0)); drivers.add(new ParameterDriver(E_Y, array[2], scale[2], -1.0, 1.0)); @@ -560,7 +594,7 @@ public EquinoctialOrbit normalize(final Orbit orbit, final Orbit reference) { eO.getHxDot(), eO.getHyDot(), eO.getLvDot(), - PositionAngle.TRUE, + PositionAngleType.TRUE, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -571,7 +605,7 @@ public EquinoctialOrbit normalize(final Orbit orbit, final Orbit reference) { eO.getHx(), eO.getHy(), MathUtils.normalizeAngle(eO.getLv(), eR.getLv()), - PositionAngle.TRUE, + PositionAngleType.TRUE, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -601,7 +635,7 @@ public > FieldEquinoctialOrbit normalize(fi eO.getHxDot(), eO.getHyDot(), eO.getLvDot(), - PositionAngle.TRUE, + PositionAngleType.TRUE, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -612,7 +646,7 @@ public > FieldEquinoctialOrbit normalize(fi eO.getHx(), eO.getHy(), MathUtils.normalizeAngle(eO.getLv(), eR.getLv()), - PositionAngle.TRUE, + PositionAngleType.TRUE, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -620,9 +654,15 @@ public > FieldEquinoctialOrbit normalize(fi } + /** {@inheritDoc} */ + @Override + public boolean isPositionAngleBased() { + return true; + } + }, - /** Type for propagation in {@link KeplerianOrbit Keplerian parameters}. */ + /** Type for orbital representation in {@link KeplerianOrbit} and {@link FieldKeplerianOrbit} parameters. */ KEPLERIAN { /** {@inheritDoc} */ @@ -633,7 +673,7 @@ public KeplerianOrbit convertType(final Orbit orbit) { /** {@inheritDoc} */ @Override - public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, + public void mapOrbitToArray(final Orbit orbit, final PositionAngleType type, final double[] stateVector, final double[] stateVectorDot) { final KeplerianOrbit keplerianOrbit = @@ -663,7 +703,7 @@ public void mapOrbitToArray(final Orbit orbit, final PositionAngle type, /** {@inheritDoc} */ @Override - public KeplerianOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngle type, + public KeplerianOrbit mapArrayToOrbit(final double[] stateVector, final double[] stateVectorDot, final PositionAngleType type, final AbsoluteDate date, final double mu, final Frame frame) { if (stateVectorDot == null) { // we don't have orbit derivatives @@ -689,7 +729,7 @@ public > FieldKeplerianOrbit convertType(fi /** {@inheritDoc} */ @Override public > void mapOrbitToArray(final FieldOrbit orbit, - final PositionAngle type, + final PositionAngleType type, final T[] stateVector, final T[] stateVectorDot) { final FieldKeplerianOrbit keplerianOrbit = @@ -711,7 +751,7 @@ public > void mapOrbitToArray(final FieldOrbit stateVectorDot[4] = keplerianOrbit.getRightAscensionOfAscendingNodeDot(); stateVectorDot[5] = keplerianOrbit.getAnomalyDot(type); } else { - Arrays.fill(stateVectorDot, 0, 6, orbit.getDate().getField().getZero().add(Double.NaN)); + Arrays.fill(stateVectorDot, 0, 6, orbit.getZero().add(Double.NaN)); } } @@ -721,7 +761,7 @@ public > void mapOrbitToArray(final FieldOrbit @Override public > FieldKeplerianOrbit mapArrayToOrbit(final T[] stateVector, final T[] stateVectorDot, - final PositionAngle type, + final PositionAngleType type, final FieldAbsoluteDate date, final T mu, final Frame frame) { if (stateVectorDot == null) { @@ -741,14 +781,21 @@ public > FieldKeplerianOrbit mapArrayToOrbi /** {@inheritDoc} */ @Override - public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngle type) { + public > FieldKeplerianOrbit convertToFieldOrbit(final Field field, + final Orbit orbit) { + return new FieldKeplerianOrbit<>(field, KEPLERIAN.convertType(orbit)); + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getDrivers(final double dP, final Orbit orbit, final PositionAngleType type) { final ParameterDriversList drivers = new ParameterDriversList(); final double[] array = new double[6]; mapOrbitToArray(orbit, type, array, null); final double[] scale = scale(dP, orbit); - final String name = type == PositionAngle.MEAN ? + final String name = type == PositionAngleType.MEAN ? MEAN_ANOM : - type == PositionAngle.ECCENTRIC ? ECC_ANOM : TRUE_ANOM; + type == PositionAngleType.ECCENTRIC ? ECC_ANOM : TRUE_ANOM; drivers.add(new ParameterDriver(A, array[0], scale[0], 0.0, Double.POSITIVE_INFINITY)); drivers.add(new ParameterDriver(ECC, array[1], scale[1], 0.0, 1.0)); drivers.add(new ParameterDriver(INC, array[2], scale[2], 0.0, FastMath.PI)); @@ -780,7 +827,7 @@ public KeplerianOrbit normalize(final Orbit orbit, final Orbit reference) { kO.getPerigeeArgumentDot(), kO.getRightAscensionOfAscendingNodeDot(), kO.getTrueAnomalyDot(), - PositionAngle.TRUE, + PositionAngleType.TRUE, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -791,7 +838,7 @@ public KeplerianOrbit normalize(final Orbit orbit, final Orbit reference) { MathUtils.normalizeAngle(kO.getPerigeeArgument(), kR.getPerigeeArgument()), MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), kR.getRightAscensionOfAscendingNode()), MathUtils.normalizeAngle(kO.getTrueAnomaly(), kR.getTrueAnomaly()), - PositionAngle.TRUE, + PositionAngleType.TRUE, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -821,7 +868,7 @@ public > FieldKeplerianOrbit normalize(fina kO.getPerigeeArgumentDot(), kO.getRightAscensionOfAscendingNodeDot(), kO.getTrueAnomalyDot(), - PositionAngle.TRUE, + PositionAngleType.TRUE, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -832,7 +879,7 @@ public > FieldKeplerianOrbit normalize(fina MathUtils.normalizeAngle(kO.getPerigeeArgument(), kR.getPerigeeArgument()), MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), kR.getRightAscensionOfAscendingNode()), MathUtils.normalizeAngle(kO.getTrueAnomaly(), kR.getTrueAnomaly()), - PositionAngle.TRUE, + PositionAngleType.TRUE, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -840,6 +887,12 @@ public > FieldKeplerianOrbit normalize(fina } + /** {@inheritDoc} */ + @Override + public boolean isPositionAngleBased() { + return true; + } + }; /** Name for position along X. */ @@ -928,7 +981,7 @@ public > FieldKeplerianOrbit normalize(fina *

            * Note that all implementations of this method must be consistent with the * implementation of the {@link org.orekit.orbits.Orbit#getJacobianWrtCartesian( - * org.orekit.orbits.PositionAngle, double[][]) Orbit.getJacobianWrtCartesian} + * PositionAngleType, double[][]) Orbit.getJacobianWrtCartesian} * method for the corresponding orbit type in terms of parameters order and meaning. *

            * @param orbit orbit to map @@ -938,13 +991,13 @@ public > FieldKeplerianOrbit normalize(fina * @param stateVectorDot flat array into which the state vector derivative should be mapped * (it can be null if derivatives are not desired, and it can have more than 6 elements, extra elements are untouched) */ - public abstract void mapOrbitToArray(Orbit orbit, PositionAngle type, double[] stateVector, double[] stateVectorDot); + public abstract void mapOrbitToArray(Orbit orbit, PositionAngleType type, double[] stateVector, double[] stateVectorDot); /** Convert state array to orbital parameters. *

            * Note that all implementations of this method must be consistent with the * implementation of the {@link org.orekit.orbits.Orbit#getJacobianWrtCartesian( - * org.orekit.orbits.PositionAngle, double[][]) Orbit.getJacobianWrtCartesian} + * PositionAngleType, double[][]) Orbit.getJacobianWrtCartesian} * method for the corresponding orbit type in terms of parameters order and meaning. *

            * @param array state as a flat array @@ -958,7 +1011,7 @@ public > FieldKeplerianOrbit normalize(fina * @param frame frame in which integration is performed * @return orbit corresponding to the flat array as a space dynamics object */ - public abstract Orbit mapArrayToOrbit(double[] array, double arrayDot[], PositionAngle type, + public abstract Orbit mapArrayToOrbit(double[] array, double[] arrayDot, PositionAngleType type, AbsoluteDate date, double mu, Frame frame); /** Convert an orbit to the instance type. @@ -976,7 +1029,7 @@ public abstract Orbit mapArrayToOrbit(double[] array, double arrayDot[], Positio *

            * Note that all implementations of this method must be consistent with the * implementation of the {@link org.orekit.orbits.Orbit#getJacobianWrtCartesian( - * org.orekit.orbits.PositionAngle, double[][]) Orbit.getJacobianWrtCartesian} + * PositionAngleType, double[][]) Orbit.getJacobianWrtCartesian} * method for the corresponding orbit type in terms of parameters order and meaning. *

            * @param CalculusFieldElement used @@ -987,15 +1040,15 @@ public abstract Orbit mapArrayToOrbit(double[] array, double arrayDot[], Positio * @param stateVectorDot flat array into which the state vector derivative should be mapped * (it can be null if derivatives are not desired, and it can have more than 6 elements, extra elements are untouched) */ - public abstract >void mapOrbitToArray(FieldOrbit orbit, PositionAngle type, - T[] stateVector, T[] stateVectorDot); + public abstract >void mapOrbitToArray(FieldOrbit orbit, PositionAngleType type, + T[] stateVector, T[] stateVectorDot); /** Convert state array to orbital parameters. *

            * Note that all implementations of this method must be consistent with the * implementation of the {@link org.orekit.orbits.Orbit#getJacobianWrtCartesian( - * org.orekit.orbits.PositionAngle, double[][]) Orbit.getJacobianWrtCartesian} + * PositionAngleType, double[][]) Orbit.getJacobianWrtCartesian} * method for the corresponding orbit type in terms of parameters order and meaning. *

            * @param CalculusFieldElement used @@ -1011,10 +1064,20 @@ public abstract >void mapOrbitToArray(FieldOrb */ public abstract > FieldOrbit mapArrayToOrbit(T[] array, T[] arrayDot, - PositionAngle type, + PositionAngleType type, FieldAbsoluteDate date, T mu, Frame frame); + /** Convert an orbit to the "Fielded" instance type. + * @param CalculusFieldElement used + * @param field CalculusField + * @param orbit base orbit + * @return converted FieldOrbit with type guaranteed to match (so it can be cast safely) + * @since 12.0 + */ + public abstract > FieldOrbit convertToFieldOrbit(Field field, + Orbit orbit); + /** Get parameters drivers initialized from a reference orbit. * @param dP user specified position error * @param orbit reference orbit @@ -1022,7 +1085,7 @@ public abstract > FieldOrbit mapArrayToOrbi * @return parameters drivers initialized from reference orbit */ public abstract ParameterDriversList getDrivers(double dP, Orbit orbit, - PositionAngle type); + PositionAngleType type); /** Normalize one orbit with respect to a reference one. *

            @@ -1040,7 +1103,8 @@ public abstract ParameterDriversList getDrivers(double dP, Orbit orbit, * @return normalized orbit (the type is guaranteed to match {@link OrbitType}) * @since 11.1 */ - public abstract > FieldOrbit normalize(FieldOrbit orbit, FieldOrbit reference); + public abstract > FieldOrbit normalize(FieldOrbit orbit, + FieldOrbit reference); /** Normalize one orbit with respect to a reference one. *

            @@ -1059,6 +1123,12 @@ public abstract ParameterDriversList getDrivers(double dP, Orbit orbit, */ public abstract Orbit normalize(Orbit orbit, Orbit reference); + /** Tells if the orbit type is based on position angles or not. + * @return true if based on {@link PositionAngleType} + * @since 12.0 + */ + public abstract boolean isPositionAngleBased(); + /** Compute scaling factor for parameters drivers. *

            * The scales are estimated from partial derivatives properties of orbits, @@ -1089,7 +1159,7 @@ protected double[] scale(final double dP, final Orbit orbit) { // convert the orbit to the desired type final double[][] jacobian = new double[6][6]; final Orbit converted = convertType(orbit); - converted.getJacobianWrtCartesian(PositionAngle.TRUE, jacobian); + converted.getJacobianWrtCartesian(PositionAngleType.TRUE, jacobian); for (int i = 0; i < 6; ++i) { final double[] row = jacobian[i]; diff --git a/src/main/java/org/orekit/orbits/PositionAngleBased.java b/src/main/java/org/orekit/orbits/PositionAngleBased.java new file mode 100644 index 0000000000..98fe3dd7bf --- /dev/null +++ b/src/main/java/org/orekit/orbits/PositionAngleBased.java @@ -0,0 +1,48 @@ +/* Copyright 2022-2023 Romain Serra + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.orbits; + +/** This interface represent orbit-like trajectory whose definition is based on a so-called position angle. + * + * @see PositionAngleType + * @see KeplerianOrbit + * @see CircularOrbit + * @see EquinoctialOrbit + * @see FieldKeplerianOrbit + * @see FieldCircularOrbit + * @see FieldEquinoctialOrbit + * @author Romain Serra + * @since 12.0 + */ +public interface PositionAngleBased { + + /** Get the cached {@link PositionAngleType}. + * @return cached type of position angle + */ + PositionAngleType getCachedPositionAngleType(); + + /** Tells whether the instance holds rates (first-order time derivatives) for dependent variables. + * @return true if and only if holding rates + */ + boolean hasRates(); + + /** Create a new instance such that {@link #hasRates()} is false. + * @return new object without rates + */ + PositionAngleBased removeRates(); + +} diff --git a/src/main/java/org/orekit/orbits/PositionAngle.java b/src/main/java/org/orekit/orbits/PositionAngleType.java similarity index 87% rename from src/main/java/org/orekit/orbits/PositionAngle.java rename to src/main/java/org/orekit/orbits/PositionAngleType.java index 5c1dae16c1..a8ba9629f5 100644 --- a/src/main/java/org/orekit/orbits/PositionAngle.java +++ b/src/main/java/org/orekit/orbits/PositionAngleType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,9 +21,12 @@ * @see KeplerianOrbit * @see CircularOrbit * @see EquinoctialOrbit + * @see FieldKeplerianOrbit + * @see FieldCircularOrbit + * @see FieldEquinoctialOrbit * @author Luc Maisonobe */ -public enum PositionAngle { +public enum PositionAngleType { /** Mean angle. */ MEAN, diff --git a/src/main/java/org/orekit/orbits/RichardsonExpansion.java b/src/main/java/org/orekit/orbits/RichardsonExpansion.java index 48113ea8d9..80089e19d7 100644 --- a/src/main/java/org/orekit/orbits/RichardsonExpansion.java +++ b/src/main/java/org/orekit/orbits/RichardsonExpansion.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/orbits/package-info.java b/src/main/java/org/orekit/orbits/package-info.java index 661fc847ae..e97aa642ea 100644 --- a/src/main/java/org/orekit/orbits/package-info.java +++ b/src/main/java/org/orekit/orbits/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/overview.html b/src/main/java/org/orekit/overview.html index 6c772ae712..9c814989c6 100644 --- a/src/main/java/org/orekit/overview.html +++ b/src/main/java/org/orekit/overview.html @@ -42,34 +42,51 @@

            2. Features

          • Geometry
              -
            • frames hierarchy supporting fixed and time-dependent (or telemetry-dependent) frames
            • -
            • predefined frames (EME2000/J2000, ICRF, GCRF, all ITRF from 1988 to 2014 and - intermediate frames, TOD, MOD, GTOD and TEME and PZ-90.11 frames, Veis, topocentric, tnw and qsw local orbital - frames, Moon, Sun, planets, solar system barycenter, Earth-Moon barycenter, ecliptic)
            • -
            • user extensible (used operationally in real time with a set of about 60 frames on several spacecraft)
            • -
            • transparent handling of IERS Earth Orientation Parameters (for both new CIO-based frames following - IERS 2010 conventions and old equinox-based frames)
            • -
            • transparent handling of JPL DE 4xx (405, 406 and more recent) and INPOP ephemerides
            • +
            • frames hierarchy supporting fixed and time-dependent (or + telemetry-dependent) frames
            • +
            • predefined frames (EME2000/J2000, ICRF, GCRF, all ITRF from 1988 to 2020 + and intermediate frames, TOD, MOD, GTOD and TEME frames, Veis, + topocentric, tnw and qsw local orbital frames, relative encounter frames, Moon, Sun, planets, solar + system barycenter, Earth-Moon barycenter, ecliptic)
            • +
            • user extensible (used operationally in real time with a set of about 60 + frames on several spacecraft)
            • +
            • transparent handling of IERS Earth Orientation Parameters (for both new + CIO-based frames following IERS 2010 conventions and old equinox-based + frames)
            • +
            • transparent handling of JPL DE 4xx (405, 406 and more recent) and INPOP + ephemerides
            • transforms including kinematic combination effects
            • composite transforms reduction and caching for efficiency
            • -
            • extensible central body shapes models (with predefined spherical and ellipsoidic shapes)
            • -
            • Cartesian and geodesic coordinates, kinematics
            • -
            • computation of Dilution Of Precision (DOP) with respect to GNSS constellations
            • +
            • extensible central body shapes models (with predefined spherical and + ellipsoidic shapes)
            • +
            • cartesian and geodesic coordinates, kinematics
            • +
            • computation of Dilution Of Precision (DOP) with respect to GNSS + constellations
            • projection of sensor Field Of View footprint on ground for any FoV shape
          • Spacecraft state
              -
            • Cartesian, elliptical Keplerian, circular and equinoctial parameters, with non-Keplerian - derivatives if available
            • -
            • Two-Line Elements
            • +
            • cartesian, elliptical Keplerian, circular and equinoctial parameters, + with non-Keplerian derivatives if available
            • +
            • Two-Line Elements (TLE)
            • +
            • Two-Line Elements generation using Fixed-Point algorithm or Least Squares Fitting
            • transparent conversion between all parameters
            • automatic binding with frames
            • attitude state and derivative
            • -
            • Jacobians
            • +
            • jacobians
            • mass management
            • -
            • user-defined associated state - (for example battery status, or higher order derivatives, or anything else)
            • +
            • user-defined associated state (for example battery status, or higher + order derivatives, or anything else)
            • +
            +
          • +
          • Covariance +
              +
            • covariance propagation
            • +
            • covariance extrapolation using a Keplerian model
            • +
            • covariance frame transformation (inertial, Earth fixed, and local orbital frames)
            • +
            • covariance type transformation (cartesian, keplerian, circular, and equinoctial)
            • +
            • covariance interpolation based on the blending model
          • Maneuvers @@ -78,39 +95,61 @@

            2. Features

          • impulse maneuvers for any propagator type
          • continuous maneuvers for numerical propagator type
          • configurable low thrust maneuver model based on event detectors
          • -
          • propulsion models intended to be used with maneuver class
          • +
          • used-defined propulsion models intended to be used with maneuver class (constant and piecewise polynomials already provided by the library)
          • user-friendly interface for maneuver triggers
        • Propagation
            -
          • analytical propagation models (Kepler, Eckstein-Heschler, GNSS, SDP4/SGP4 with 2006 corrections)
          • +
          • analytical propagation models +
              +
            • Kepler
            • +
            • Eckstein-Heschler
            • +
            • Brouwer-Lyddane with Warren Phipps' correction for the + critical inclination of 63.4° and the perturbative + acceleration due to atmospheric drag
            • +
            • SDP4/SGP4 with 2006 corrections
            • +
            • GNSS: GPS, QZSS, Galileo, GLONASS, Beidou, IRNSS and SBAS
            • +
            +
          • numerical propagators
              -
            • customizable force models -
                -
              • central attraction
              • -
              • gravity models including time-dependent like trends and pulsations (automatic reading of ICGEM (new Eigen models), SHM (old Eigen models), - EGM and GRGS gravity field files formats, even compressed)
              • -
              • atmospheric drag
              • -
              • third body attraction (with data for Sun, Moon and all solar systems planets)
              • -
              • radiation pressure with eclipses
              • -
              • solid tides, with or without solid pole tide
              • -
              • ocean tides, with or without ocean pole tide
              • -
              • Earth's albedo and infrared
              • -
              • empirical accelerations to account for the unmodeled forces
              • -
              • general relativity
              • -
              • multiple maneuvers
              • -
              -
            • -
            • state of the art ODE integrators (adaptive stepsize with error control, - continuous output, switching functions, G-stop, step normalization ...)
            • -
            • computation of Jacobians with respect to orbital parameters and selected force models parameters
            • -
            • serialization mechanism to store complete results on persistent storage for later use
            • -
            • CR3BP model
            • +
            • central attraction
            • +
            • gravity models including time-dependent like trends and pulsations + (automatic reading of ICGEM (new Eigen models), SHM (old Eigen + models), EGM and GRGS gravity field files formats, even compressed)
            • +
            • atmospheric drag
            • +
            • third body attraction (with data for Sun, Moon and all solar systems + planets)
            • +
            • radiation pressure with eclipses (multiple oblate spheroids occulting bodies, multiple coefficients for bow and wing models)
            • +
            • solid tides, with or without solid pole tide
            • +
            • ocean tides, with or without ocean pole tide
            • +
            • general relativity (including Lense-Thirring and De Sitter corrections)
            • +
            • Earth's albedo and infrared
            • +
            • multiple maneuvers
            • +
            • empirical accelerations to account for the unmodeled forces
            • +
            • state of the art ODE integrators (adaptive stepsize with error + control, continuous output, switching functions, G-stop, step + normalization ...)
            • +
            • serialization mechanism to store complete results on persistent + storage for later use
            • +
            • propagation in non-inertial frames (e.g. for Lagrange point halo + orbits)
            • +
            +
          • +
          • semi-analytical propagation model (DSST) with customizable force models +
              +
            • central attraction
            • +
            • gravity models
            • +
            • J2-squared effect (Zeis model)
            • +
            • atmospheric drag
            • +
            • third body attraction
            • +
            • radiation pressure with eclipse
          • -
          • semi-analytical propagation model (DSST) with customizable force models
          • +
          • computation of Jacobians with respect to orbital parameters and + selected force models parameters
          • +
          • trajectories around Lagragian points using CR3BP model
          • tabulated ephemerides
            • file based
            • @@ -118,73 +157,77 @@

              2. Features

            • integration based
          • -
          • Taylor-algebra (or any other real field) version of most of the above propagators, - with all force models, events detection, orbits types, coordinates types and frames - allowing allowing high order uncertainties and derivatives computation or very fast - Monte-Carlo analyzes -
          • -
          • unified interface above analytical/numerical/tabulated propagators for easy - switch from coarse analysis to fine simulation with one line change
          • +
          • Taylor-algebra (or any other real field) version of most of the above + propagators, with all force models, events detection, orbits types, + coordinates types and frames allowing high order uncertainties and + derivatives computation or very fast Monte-Carlo analyzes
          • +
          • unified interface above analytical/numerical/tabulated propagators for + easy switch from coarse analysis to fine simulation with one line change
          • all propagators can manage the time loop by themselves and handle callback - functions (called step handlers) from the calling application at each time step. + functions (called step handlers) from the calling application at each time step
            • step handlers can be called at discrete time at regular time steps, which are - independent of propagator time steps
            • + independent of propagator time steps
            • step handlers can be called with interpolators valid throughout one propagator - time step, which can have varying sizes
            • + time step, which can have varying sizes
            • step handlers can be switched off completely, when only final state is desired
            • special step handlers are provided for a posteriori ephemeris generation: all - intermediate results are stored during propagation and provided back to the application - which can navigate at will through them, effectively using the propagated orbit as if - it was analytical model, even if it really is a numerically propagated one, which - is ideal for search and iterative algorithms
            • + intermediate results are stored during propagation and provided back to the application + which can navigate at will through them, effectively using the propagated orbit as if + it was analytical model, even if it really is a numerically propagated one, which + is ideal for search and iterative algorithms
            • several step handlers can be used simultaneously, so it is possible to have a fine - grained fixed time step to log state in a huge file, and have at the same time a - coarse grained time step to display progress for user at a more human-friendly rate, - this feature can also be used for debugging purpose, by setting up a temporary - step handler alongside the operational ones
            • + grained fixed time step to log state in a huge file, and have at the same time a + coarse grained time step to display progress for user at a more human-friendly rate, + this feature can also be used for debugging purpose, by setting up a temporary + step handler alongside the operational ones
          • -
          • handling of discrete events during integration - (models changes, G-stop, simple notifications ...)
          • +
          • handling of discrete events during integration (models changes, G-stop, + simple notifications ...)
          • predefined discrete events
            • eclipse (both umbra and penumbra)
            • ascending and descending node crossing
            • -
            • anomaly, latitude argument or longitude argument crossings, - with either true, eccentric or mean angles
            • +
            • anomaly, latitude argument or longitude argument crossings, with + either true, eccentric or mean angles
            • apogee and perigee crossing
            • -
            • alignment with some body in the orbital plane (with customizable threshold angle)
            • -
            • angular separation thresholds crossing between spacecraft and a beacon (typically the Sun) - as seen from an observer (typically a ground station)
            • -
            • raising/setting with respect to a ground location - (with customizable triggering elevation and ground mask, optionally considering refraction)
            • +
            • alignment with some body in the orbital plane (with customizable + threshold angle)
            • +
            • angular separation thresholds crossing between spacecraft and + a beacon (typically the Sun) as seen from an observer (typically + a ground station)
            • +
            • raising/setting with respect to a ground location (with customizable + triggering elevation and ground mask, optionally considering + refraction)
            • date and on-the-fly resetting countdown
            • date interval with parameter-driven boundaries
            • latitude, longitude, altitude crossing
            • latitude, longitude extremum
            • elevation extremum
            • -
            • anomaly, latitude argument, or longitude argument crossings, either true, mean or eccentric
            • -
            • moving target detection (with optional radius) in spacecraft sensor Field Of View (any shape, with special case for circular)
            • +
            • moving target detection (with optional radius) in spacecraft sensor + Field Of View (any shape, with special case for circular)
            • spacecraft detection in ground based Field Of View (any shape)
            • sensor Field Of View (any shape) overlapping complex geographic zone
            • -
            • complex geographic zone traversal
            • -
            • inter-satellites direct view
            • +
            • complex geographic zones traversal
            • +
            • inter-satellites direct view (with customizable skimming altitude)
            • ground at night
            • impulse maneuvers occurrence
            • geomagnetic intensity
            • +
            • extremum approach for TCA (Time of Closest Approach) computing
          • -
          • possibility of slightly shifting events in time (for example to switch from - solar pointing mode to something else a few minutes before eclipse entry and - reverting to solar pointing mode a few minutes after eclipse exit)
          • -
          • events filtering based on their direction (for example to detect - only eclipse entries and not eclipse exits)
          • -
          • events filtering based on an external enabling function (for - example to detect events only during selected orbits and not others)
          • +
          • possibility of slightly shifting events in time (for example to switch + from solar pointing mode to something else a few minutes before eclipse + entry and reverting to solar pointing mode a few minutes after eclipse + exit)
          • +
          • events filtering based on their direction (for example to detect only + eclipse entries and not eclipse exits)
          • +
          • events filtering based on an external enabling function (for example to + detect events only during selected orbits and not others)
          • events combination with boolean operators
          • ability to run several propagators in parallel and manage their states - simultaneously throughout propagation
          • + simultaneously throughout propagation
        • Attitude @@ -200,9 +243,11 @@

          2. Features

        • tabulated attitudes, either respective to inertial frame or respective to Local Orbital Frames
        • specific law for GNSS satellites: GPS (block IIA, block IIF, block IIF), GLONASS, GALILEO, BEIDOU (GEO, IGSO, MEO)
        • +
        • torque-free for general (non-symmetrical) body
        -
      33. loading and writing of CCSDS Attitude Data Messages (both AEM, and APM types are supported, in both KVN and XML formats, standalone or in combined NDM)
      34. +
      35. loading and writing of CCSDS Attitude Data Messages (both AEM, APM and ACM types are supported, in both KVN and XML formats, standalone or in combined NDM)
      36. +
      37. exporting of attitude ephemeris in CCSDS AEM and ACM file format
      38. Orbit determination @@ -214,26 +259,40 @@

        2. Features

      39. choice between forming normal equations or not
      40. +
      41. sequential batch least squares +
          +
        • sequential Gauss-Newton optimizer
        • +
        • decomposition algorithms choice (QR, LU, SVD, Cholesky)
        • +
        • possibility to use an initial covariance matrix
        • +
        +
      42. Kalman filtering
        • customizable process noise matrices providers
        • -
        • time-dependent process nous provider
        • +
        • time dependent process noise provider
        • +
        • implementation of the Extended Kalman Filter
        • +
        • implementation of the Extended Semi-analytical Kalman Filter (ESKF)
        • +
        • implementation of the Unscented Kalman Filter
        • +
        • implementation of the Unscented Semi-analytical Kalman Filter
      43. parameters estimation
        • orbital parameters estimation (or only a subset if desired)
        • force model parameters estimation (drag coefficients, radiation pressure coefficients, - central attraction, maneuver thrust or flow rate)
        • + central attraction, maneuver thrust, flow rate or start/stop epoch)
        • measurements parameters estimation (biases, satellite clock offset, station clock offset, station position, pole motion and rate, prime meridian correction and rate, total zenith delay in tropospheric correction)
      44. -
      45. can be used with both numerical, DSST, or SGP4/SDP4 orbit propagators
      46. +
      47. orbit determination can be performed with numerical, DSST, SDP4/SGP4, Eckstein-Hechler, Brouwer-Lyddane, or Keplerian propagators
      48. +
      49. ephemeris-based orbit determination to estimate measurement parameters like station biases or clock offsets
      50. multi-satellites orbit determination
      51. +
      52. initial orbit determination methods (Gibbs, Gooding, Lambert and Laplace)
      53. ground stations displacements due to solid tides
      54. ground stations displacements due to ocean loading (based on Onsala Space Observatory files in BLQ format)
      55. +
      56. ground stations displacements due to plate tectonics
      57. several predefined measurements
        • range
        • @@ -247,6 +306,10 @@

          2. Features

        • inter-satellites GNSS phase
        • GNSS code
        • GNSS phase with integer ambiguity resolution and wind-up effect
        • +
        • Time Difference of Arrival (TDOA)
        • +
        • Frequency Difference of Arrival (FDOA)
        • +
        • Bi-static range and range rate
        • +
        • multiplexed
      58. possibility to add custom measurements
      59. @@ -256,7 +319,7 @@

        2. Features

        • tropospheric effects
        • ionospheric effects
        • -
        • clock relativistic effects
        • +
        • clock relativistic effects (including J2 correction)
        • station offsets
        • biases
        • delays
        • @@ -271,7 +334,6 @@

          2. Features

        • single frequency combination of measurements (Phase minus code and GRAPHIC)
        -
      60. possibility to parse CCSDS Tracking Data Message files
      61. measurements generation
        • with measurements feasibility triggered by regular event detectors @@ -288,21 +350,27 @@

          2. Features

          • computation of Dilution Of Precision
          • loading of ANTEX antenna models file
          • -
          • loading of RINEX observation files (version 2 and version 3)
          • +
          • loading and writing of RINEX observation files (version 2, 3, and 4)
          • +
          • loading of RINEX navigation files (version 2, 3, and 4)
          • support for Hatanaka compact RINEX format
          • -
          • loading of SINEX station file
          • +
          • loading of SINEX file (can load station positions, eccentricities, EOPs, and Differential Code Biases)
          • loading of RINEX clock files (version 2 and version 3)
          • parsing of IGS SSR messages for all constellations (version 1)
          • +
          • parsing of RTCM messages (both ephemeris and correction messages)
          • +
          • parsing of GPS RF link binary message
          • +
          • Hatch filters for GNSS measurements smoothing
          • implementation of Ntrip protocol
          • +
          • decoding of GPS navigation messages
        • Orbit file handling
            -
          • loading of SP3 orbit files (from versions a to d)
          • +
          • loading and writing of SP3 orbit files (from versions a to d)
          • loading and writing of CCSDS Orbit Data Messages (both OPM, OEM, OMM, OCM types are supported, in both KVN and XML formats, standalone or in combined NDM)
          • loading of SEM and YUMA files for GPS constellation
          • -
          • exporting of ephemeris in CCSDS OEM file format
          • +
          • exporting of ephemeris in CCSDS OEM and OCM file formats
          • loading of ILRS CPF orbit files
          • +
          • exporting of ephemeris in STK format
        • Earth models @@ -310,19 +378,36 @@

          2. Features

        • atmospheric models (DTM2000, Jacchia-Bowman 2008, NRL MSISE 2000, Harris-Priester and simple exponential models), and Marshall solar Activity Future Estimation, optionally with lift component
        • support for CSSI space weather data
        • -
        • tropospheric delay (modified Saastamoinen, Mendes-Pavlis, Vienna 1, Vienna 3, estimated, fixed)
        • +
        • support for SOLFSMY and DTC data for JB2008 atmospheric model
        • +
        • tropospheric delay (modified Saastamoinen, estimated, fixed)
        • +
        • tropospheric mapping functions (Vienna 1, Vienna 3, Global, Niell)
        • tropospheric refraction correction angle (Recommendation ITU-R P.834-7 and Saemundssen's formula quoted by Meeus)
        • -
        • tropospheric model for laser ranging (Marini-Murray)
        • +
        • tropospheric model for laser ranging (Marini-Murray, Mendes-Pavlis)
        • Klobuchar ionospheric model (including parsing α and β coefficients from University of Bern Astronomical Institute files)
        • -
        • Global Ionospheric Map model
        • +
        • Global Ionospheric Map (GIM) model
        • NeQuick ionospheric model
        • -
        • VTEC estimated ionospheric model
        • +
        • VTEC estimated ionospheric model with Single Layer Model (SLM) ionospheric mapping function
        • Global Pression and Temperature models (GPT and GPT2)
        • geomagnetic field (WMM, IGRF)
        • geoid model from any gravity field
        • displacement of ground points due to tides
        • tessellation of zones of interest as tiles
        • sampling of zones of interest as grids of points
        • +
        • construction of trajectories using loxodromes (commonly, a rhumb line)
        • +
        +
      62. +
      63. Collisions +
          +
        • loading and writing of CCSDS Conjunction Data Messages (CDM in both KVN and XML formats)
        • +
        • 2D probability of collision computing methods assuming short term encounter and spherical bodies : +
            +
          • Chan 1997
          • +
          • Alfriend 1999
          • +
          • Alfriend 1999 (maximum version)
          • +
          • Alfano 2005
          • +
          • Patera 2005 (custom Orekit implementation) (recommended)
          • +
          • Laas 2015 (recommended)
          • +
      64. Customizable data loading diff --git a/src/main/java/org/orekit/propagation/AbstractMatricesHarvester.java b/src/main/java/org/orekit/propagation/AbstractMatricesHarvester.java index 34b3240e44..73c6e88dff 100644 --- a/src/main/java/org/orekit/propagation/AbstractMatricesHarvester.java +++ b/src/main/java/org/orekit/propagation/AbstractMatricesHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,7 @@ import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.PositionAngleType; import org.orekit.utils.DoubleArrayDictionary; /** Base harvester between two-dimensional Jacobian matrices and one-dimensional {@link @@ -54,7 +55,7 @@ public abstract class AbstractMatricesHarvester implements MatricesHarvester { /** Simple constructor. *

        * The arguments for initial matrices must be compatible with the {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param stmName State Transition Matrix state name * @param initialStm initial State Transition Matrix ∂Y/∂Y₀, diff --git a/src/main/java/org/orekit/propagation/AbstractPropagator.java b/src/main/java/org/orekit/propagation/AbstractPropagator.java index da7564ed72..5152845deb 100644 --- a/src/main/java/org/orekit/propagation/AbstractPropagator.java +++ b/src/main/java/org/orekit/propagation/AbstractPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -217,7 +217,7 @@ protected SpacecraftState updateAdditionalStates(final SpacecraftState original) int yieldCount = 0; while (!pending.isEmpty()) { final AdditionalStateProvider provider = pending.remove(); - if (provider.yield(updated)) { + if (provider.yields(updated)) { // this generator has to wait for another one, // we put it again in the pending queue pending.add(provider); diff --git a/src/main/java/org/orekit/propagation/AbstractStateCovarianceInterpolator.java b/src/main/java/org/orekit/propagation/AbstractStateCovarianceInterpolator.java new file mode 100644 index 0000000000..f03b1ea198 --- /dev/null +++ b/src/main/java/org/orekit/propagation/AbstractStateCovarianceInterpolator.java @@ -0,0 +1,260 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation; + +import org.orekit.frames.Frame; +import org.orekit.frames.LOFType; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; +import org.orekit.time.TimeInterpolator; +import org.orekit.time.TimeStampedPair; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Abstract class for orbit and state covariance interpolator. + * + * @author Vincent Cucchietti + * @see Orbit + * @see StateCovariance + * @see TimeStampedPair + */ +public abstract class AbstractStateCovarianceInterpolator + extends AbstractTimeInterpolator> { + + /** Default position angle for covariance expressed in Cartesian elements. */ + public static final PositionAngleType DEFAULT_POSITION_ANGLE = PositionAngleType.MEAN; + + /** Default column dimension for position-velocity state covariance. */ + public static final int COLUMN_DIM = 6; + + /** Default row dimension for position-velocity state covariance. */ + public static final int ROW_DIM = 6; + + /** Output frame. */ + private final Frame outFrame; + + /** Output local orbital frame. */ + private final LOFType outLOF; + + /** Output orbit type. */ + private final OrbitType outOrbitType; + + /** Output position angle type. */ + private final PositionAngleType outPositionAngleType; + + /** Orbit interpolator. */ + private final TimeInterpolator orbitInterpolator; + + /** + * Constructor. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param orbitInterpolator orbit interpolator + * @param outLOF local orbital frame + * + * @see Frame + * @see OrbitType + * @see PositionAngleType + */ + public AbstractStateCovarianceInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final TimeInterpolator orbitInterpolator, + final LOFType outLOF) { + super(interpolationPoints, extrapolationThreshold); + this.orbitInterpolator = orbitInterpolator; + this.outLOF = outLOF; + this.outFrame = null; + this.outOrbitType = OrbitType.CARTESIAN; + this.outPositionAngleType = DEFAULT_POSITION_ANGLE; + } + + /** + * Constructor. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param orbitInterpolator orbit interpolator + * @param outFrame desired output covariance frame + * @param outPositionAngleType desired output position angle + * @param outOrbitType desired output orbit type + * + * @see Frame + * @see OrbitType + * @see PositionAngleType + */ + public AbstractStateCovarianceInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final TimeInterpolator orbitInterpolator, + final Frame outFrame, + final OrbitType outOrbitType, + final PositionAngleType outPositionAngleType) { + super(interpolationPoints, extrapolationThreshold); + this.orbitInterpolator = orbitInterpolator; + this.outLOF = null; + this.outFrame = outFrame; + this.outOrbitType = outOrbitType; + this.outPositionAngleType = outPositionAngleType; + } + + /** + * Interpolate orbit and associated covariance. + * + * @param interpolationData interpolation data + * + * @return interpolated orbit and associated covariance + */ + @Override + public TimeStampedPair interpolate(final InterpolationData interpolationData) { + + // Interpolate orbit at interpolation date + final Orbit interpolatedOrbit = interpolateOrbit(interpolationData.getInterpolationDate(), + interpolationData.getNeighborList()); + + // Rebuild state covariance + final StateCovariance covarianceInOrbitFrame = + computeInterpolatedCovarianceInOrbitFrame(interpolationData.getNeighborList(), interpolatedOrbit); + + // Output new blended StateCovariance instance in desired output + return expressCovarianceInDesiredOutput(interpolatedOrbit, covarianceInOrbitFrame); + } + + /** Get output frame. + * @return output frame. Can be null. + */ + public Frame getOutFrame() { + return outFrame; + } + + /** Get output local orbital frame. + * @return output local orbital frame. Can be null. + */ + public LOFType getOutLOF() { + return outLOF; + } + + /** Get output orbit type. + * @return output orbit type. + */ + public OrbitType getOutOrbitType() { + return outOrbitType; + } + + /** Get output position angle type. + * @return output position angle. + */ + public PositionAngleType getOutPositionAngleType() { + return outPositionAngleType; + } + + /** Get orbit interpolator. + * @return orbit interpolator. + */ + public TimeInterpolator getOrbitInterpolator() { + return orbitInterpolator; + } + + /** + * Interpolate orbit at given interpolation date. + * + * @param interpolationDate interpolation date + * @param neighborList neighbor list + * + * @return interpolated orbit + */ + protected Orbit interpolateOrbit(final AbsoluteDate interpolationDate, + final List> neighborList) { + + // Build orbit list from uncertain orbits + final List orbits = buildOrbitList(neighborList); + + return orbitInterpolator.interpolate(interpolationDate, orbits); + } + + /** + * Compute the interpolated covariance expressed in the interpolated orbit frame. + * + * @param uncertainStates list of orbits and associated covariances + * @param interpolatedOrbit interpolated orbit + * + * @return interpolated covariance expressed in the interpolated orbit frame + */ + protected abstract StateCovariance computeInterpolatedCovarianceInOrbitFrame( + List> uncertainStates, + Orbit interpolatedOrbit); + + /** + * Express covariance in output configuration defined at this instance construction. + * + * @param interpolatedOrbit interpolated orbit + * @param covarianceInOrbitFrame covariance expressed in interpolated orbit frame + * + * @return covariance in desired output configuration + */ + protected TimeStampedPair expressCovarianceInDesiredOutput(final Orbit interpolatedOrbit, + final StateCovariance covarianceInOrbitFrame) { + + final StateCovariance covarianceOutput; + + // Output frame is defined + if (outLOF == null) { + + // Output frame is pseudo inertial + if (outFrame.isPseudoInertial()) { + final StateCovariance covarianceInOutputFrame = + covarianceInOrbitFrame.changeCovarianceFrame(interpolatedOrbit, outFrame); + + covarianceOutput = + covarianceInOutputFrame.changeCovarianceType(interpolatedOrbit, outOrbitType, outPositionAngleType); + } + // Output frame is not pseudo inertial + else { + covarianceOutput = covarianceInOrbitFrame.changeCovarianceFrame(interpolatedOrbit, outFrame); + } + + } + // Output local orbital frame is defined + else { + covarianceOutput = covarianceInOrbitFrame.changeCovarianceFrame(interpolatedOrbit, outLOF); + } + + return new TimeStampedPair<>(interpolatedOrbit, covarianceOutput); + } + + /** + * Build an orbit list from cached samples. + * + * @param neighborList neighbor list + * + * @return orbit list + */ + private List buildOrbitList(final List> neighborList) { + + // Get samples stream + final Stream> uncertainStateStream = neighborList.stream(); + + // Map to orbit + final Stream orbitStream = uncertainStateStream.map(TimeStampedPair::getFirst); + + // Convert to list + return orbitStream.collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/orekit/propagation/AdditionalStateProvider.java b/src/main/java/org/orekit/propagation/AdditionalStateProvider.java index 74d99a553e..9e66e3ff0d 100644 --- a/src/main/java/org/orekit/propagation/AdditionalStateProvider.java +++ b/src/main/java/org/orekit/propagation/AdditionalStateProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -38,7 +38,7 @@ * the propagator. This reordering is performed each time the complete state is built, using a yield * mechanism. The propagator first pushes all providers in a stack and then empty the stack, one provider * at a time, taking care to select only providers that do not {@link - * #yield(SpacecraftState) yield} when asked. Consider for example a case where providers A, B and C + * #yields(SpacecraftState) yield} when asked. Consider for example a case where providers A, B and C * have been registered and provider B needs in fact the additional state generated by provider C. Then * when a complete state is built, the propagator puts the three providers in a new stack, and then starts the incremental * generation of additional states. It first checks provider A which does not yield so it is popped from @@ -110,7 +110,7 @@ default void init(final SpacecraftState initialState, final AbsoluteDate target) * as the state is incrementally built up * @since 11.1 */ - default boolean yield(SpacecraftState state) { + default boolean yields(SpacecraftState state) { return false; } diff --git a/src/main/java/org/orekit/propagation/BoundedPropagator.java b/src/main/java/org/orekit/propagation/BoundedPropagator.java index 402124b996..49eec5c5e8 100644 --- a/src/main/java/org/orekit/propagation/BoundedPropagator.java +++ b/src/main/java/org/orekit/propagation/BoundedPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/EphemerisGenerator.java b/src/main/java/org/orekit/propagation/EphemerisGenerator.java index 0ed5d8a839..f593620ce6 100644 --- a/src/main/java/org/orekit/propagation/EphemerisGenerator.java +++ b/src/main/java/org/orekit/propagation/EphemerisGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java b/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java index 467b0e6ec0..4a07ded896 100644 --- a/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -187,7 +187,7 @@ protected FieldSpacecraftState updateAdditionalStates(final FieldSpacecraftSt int yieldCount = 0; while (!pending.isEmpty()) { final FieldAdditionalStateProvider provider = pending.remove(); - if (provider.yield(updated)) { + if (provider.yields(updated)) { // this generator has to wait for another one, // we put it again in the pending queue pending.add(provider); diff --git a/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java b/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java index 3b33ac3d00..731fc47184 100644 --- a/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java +++ b/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -39,7 +39,7 @@ * the propagator. This reordering is performed each time the complete state is built, using a yield * mechanism. The propagator first pushes all providers in a stack and then empty the stack, one provider * at a time, taking care to select only providers that do not {@link - * #yield(FieldSpacecraftState) yield} when asked. Consider for example a case where providers A, B and C + * #yields(FieldSpacecraftState) yield} when asked. Consider for example a case where providers A, B and C * have been registered and provider B needs in fact the additional state generated by provider C. Then * when a complete state is built, the propagator puts the three providers in a new stack, and then starts the incremental * generation of additional states. It first checks provider A which does not yield so it is popped from @@ -71,6 +71,7 @@ * @see org.orekit.propagation.FieldPropagator * @see org.orekit.propagation.integration.FieldAdditionalDerivativesProvider * @author Luc Maisonobe + * @param type of the field elements */ public interface FieldAdditionalStateProvider> { @@ -110,7 +111,7 @@ default void init(final FieldSpacecraftState initialState, final FieldAbsolut * as the state is incrementally built up * @since 11.1 */ - default boolean yield(FieldSpacecraftState state) { + default boolean yields(FieldSpacecraftState state) { return false; } diff --git a/src/main/java/org/orekit/propagation/FieldBoundedPropagator.java b/src/main/java/org/orekit/propagation/FieldBoundedPropagator.java index eb44dc0f70..8897bcdd3e 100644 --- a/src/main/java/org/orekit/propagation/FieldBoundedPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldBoundedPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/FieldEphemerisGenerator.java b/src/main/java/org/orekit/propagation/FieldEphemerisGenerator.java index 6cb166d388..6336a77a5a 100644 --- a/src/main/java/org/orekit/propagation/FieldEphemerisGenerator.java +++ b/src/main/java/org/orekit/propagation/FieldEphemerisGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/FieldPropagator.java b/src/main/java/org/orekit/propagation/FieldPropagator.java index f824c60d40..f2727fbdc2 100644 --- a/src/main/java/org/orekit/propagation/FieldPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -158,10 +158,10 @@ default void setStepHandler(final FieldOrekitStepHandler handler) { * addAdditionalStateProvider} method. If the propagator is an {@link * org.orekit.propagation.integration.FieldAbstractIntegratedPropagator integrator-based * propagator}, the states for which a set of {@link - * org.orekit.propagation.integration.FieldAdditionalEquations additional equations} has - * been registered by calling the {@link - * org.orekit.propagation.integration.FieldAbstractIntegratedPropagator#addAdditionalEquations( - * org.orekit.propagation.integration.FieldAdditionalEquations) addAdditionalEquations} + * org.orekit.propagation.integration.FieldAdditionalDerivativesProvider additional derivatives + * provider} has been registered by calling the {@link + * org.orekit.propagation.integration.FieldAbstractIntegratedPropagator#addAdditionalDerivativesProvider( + * org.orekit.propagation.integration.FieldAdditionalDerivativesProvider) addAdditionalDerivativesProvider} * method are also counted as managed additional states. *

        *

        diff --git a/src/main/java/org/orekit/propagation/FieldSpacecraftState.java b/src/main/java/org/orekit/propagation/FieldSpacecraftState.java index c58bd74e8b..6e8aa1fd12 100644 --- a/src/main/java/org/orekit/propagation/FieldSpacecraftState.java +++ b/src/main/java/org/orekit/propagation/FieldSpacecraftState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,15 +17,8 @@ package org.orekit.propagation; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.exception.MathIllegalStateException; @@ -33,61 +26,62 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.orekit.attitudes.FieldAttitude; -import org.orekit.attitudes.LofOffset; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; -import org.orekit.frames.LOFType; import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.time.FieldTimeInterpolable; import org.orekit.time.FieldTimeShiftable; import org.orekit.time.FieldTimeStamped; import org.orekit.utils.DoubleArrayDictionary; -import org.orekit.utils.FieldAbsolutePVCoordinates; import org.orekit.utils.FieldArrayDictionary; +import org.orekit.utils.FieldAbsolutePVCoordinates; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; - +import org.orekit.utils.TimeStampedPVCoordinates; /** This class is the representation of a complete state holding orbit, attitude - * and mass information at a given date. + * and mass information at a given date, meant primarily for propagation. * - *

        It contains an {@link FieldOrbit orbital state} at a current - * {@link FieldAbsoluteDate} both handled by an {@link FieldOrbit}, plus the current - * mass and attitude. FieldOrbit and state are guaranteed to be consistent in terms + *

        It contains an {@link FieldOrbit}, or a {@link FieldAbsolutePVCoordinates} if there + * is no definite central body, plus the current mass and attitude at the intrinsic + * {@link FieldAbsoluteDate}. Quantities are guaranteed to be consistent in terms * of date and reference frame. The spacecraft state may also contain additional * states, which are simply named double arrays which can hold any user-defined * data. *

        *

        - * The state can be slightly shifted to close dates. This shift is based on - * a simple Keplerian model for orbit, a linear extrapolation for attitude - * taking the spin rate into account and no mass change. It is not - * intended as a replacement for proper orbit and attitude propagation but - * should be sufficient for either small time shifts or coarse accuracy. + * The state can be slightly shifted to close dates. This actual shift varies + * between {@link FieldOrbit} and {@link FieldAbsolutePVCoordinates}. + * For attitude it is a linear extrapolation taking the spin rate into account + * and no mass change. It is not intended as a replacement for proper + * orbit and attitude propagation but should be sufficient for either small + * time shifts or coarse accuracy. *

        *

        * The instance {@code FieldSpacecraftState} is guaranteed to be immutable. *

        * @see org.orekit.propagation.numerical.NumericalPropagator + * @see SpacecraftState * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @author Luc Maisonobe * @author Vincent Mouraux + * @param type of the field elements */ public class FieldSpacecraftState > - implements FieldTimeStamped, FieldTimeShiftable, T>, FieldTimeInterpolable, T> { + implements FieldTimeStamped, FieldTimeShiftable, T> { /** Default mass. */ private static final double DEFAULT_MASS = 1000.0; /** - * tolerance on date comparison in {@link #checkConsistency(FieldOrbit, FieldAttitude)}. 100 ns + * tolerance on date comparison in {@link #checkConsistency(FieldOrbit, FieldAttitude)}. 100 ns * corresponds to sub-mm accuracy at LEO orbital velocities. */ private static final double DATE_INCONSISTENCY_THRESHOLD = 100e-9; @@ -117,8 +111,8 @@ public class FieldSpacecraftState > * @param orbit the orbit */ public FieldSpacecraftState(final FieldOrbit orbit) { - this(orbit, - new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), orbit.getA().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary) null); } @@ -140,9 +134,8 @@ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude at * @param mass the mass (kg) */ public FieldSpacecraftState(final FieldOrbit orbit, final T mass) { - this(orbit, - new LofOffset(orbit.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), mass, (FieldArrayDictionary) null); } @@ -158,17 +151,6 @@ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude at this(orbit, attitude, mass, (FieldArrayDictionary) null); } - /** Build a spacecraft state from orbit and additional states. - *

        FieldAttitude and mass are set to unspecified non-null arbitrary values.

        - * @param orbit the orbit - * @param additional additional states - * @deprecated as of 11.1, replacezd by {@link #FieldSpacecraftState(FieldOrbit, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldOrbit orbit, final Map additional) { - this(orbit, new FieldArrayDictionary<>(orbit.getDate().getField(), additional)); - } - /** Build a spacecraft state from orbit and additional states. *

        FieldAttitude and mass are set to unspecified non-null arbitrary values.

        * @param orbit the orbit @@ -176,26 +158,11 @@ public FieldSpacecraftState(final FieldOrbit orbit, final Map ad * @since 11.1 */ public FieldSpacecraftState(final FieldOrbit orbit, final FieldArrayDictionary additional) { - this(orbit, - new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), orbit.getA().getField().getZero().add(DEFAULT_MASS), additional); } - /** Build a spacecraft state from orbit attitude and additional states. - *

        Mass is set to an unspecified non-null arbitrary value.

        - * @param orbit the orbit - * @param attitude attitude - * @param additional additional states - * @exception IllegalArgumentException if orbit and attitude dates - * or frames are not equal - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldOrbit, FieldAttitude, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude attitude, final Map additional) - throws IllegalArgumentException { - this(orbit, attitude, new FieldArrayDictionary<>(orbit.getDate().getField(), additional)); - } - /** Build a spacecraft state from orbit attitude and additional states. *

        Mass is set to an unspecified non-null arbitrary value.

        * @param orbit the orbit @@ -210,18 +177,6 @@ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude at this(orbit, attitude, orbit.getA().getField().getZero().add(DEFAULT_MASS), additional); } - /** Create a new instance from orbit, mass and additional states. - *

        FieldAttitude law is set to an unspecified default attitude.

        - * @param orbit the orbit - * @param mass the mass (kg) - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldOrbit, CalculusFieldElement, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldOrbit orbit, final T mass, final Map additional) { - this(orbit, mass, new FieldArrayDictionary<>(orbit.getDate().getField(), additional)); - } - /** Create a new instance from orbit, mass and additional states. *

        FieldAttitude law is set to an unspecified default attitude.

        * @param orbit the orbit @@ -230,24 +185,11 @@ public FieldSpacecraftState(final FieldOrbit orbit, final T mass, final Map orbit, final T mass, final FieldArrayDictionary additional) { - this(orbit, - new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), mass, additional); } - /** Build a spacecraft state from orbit, attitude, mass and additional states. - * @param orbit the orbit - * @param attitude attitude - * @param mass the mass (kg) - * @param additional additional states (may be null if no additional states are available) - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldOrbit, FieldAttitude, CalculusFieldElement, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude attitude, - final T mass, final Map additional) { - this(orbit, attitude, mass, new FieldArrayDictionary<>(orbit.getDate().getField(), additional)); - } - /** Build a spacecraft state from orbit, attitude, mass and additional states. * @param orbit the orbit * @param attitude attitude @@ -304,7 +246,8 @@ public FieldSpacecraftState(final Field field, final SpacecraftState state) { if (state.isOrbitDefined()) { final double[] stateD = new double[6]; final double[] stateDotD = state.getOrbit().hasDerivatives() ? new double[6] : null; - state.getOrbit().getType().mapOrbitToArray(state.getOrbit(), PositionAngle.TRUE, stateD, stateDotD); + final PositionAngleType positionAngleType = PositionAngleType.TRUE; + state.getOrbit().getType().mapOrbitToArray(state.getOrbit(), positionAngleType, stateD, stateDotD); final T[] stateF = MathArrays.buildArray(field, 6); for (int i = 0; i < stateD.length; ++i) { stateF[i] = field.getZero().add(stateD[i]); @@ -321,28 +264,19 @@ public FieldSpacecraftState(final Field field, final SpacecraftState state) { final FieldAbsoluteDate dateF = new FieldAbsoluteDate<>(field, state.getDate()); - this.orbit = state.getOrbit().getType().mapArrayToOrbit(stateF, stateDotF, - PositionAngle.TRUE, - dateF, field.getZero().add(state.getMu()), state.getFrame()); + this.orbit = state.getOrbit().getType().mapArrayToOrbit(stateF, stateDotF, positionAngleType, dateF, + field.getZero().add(state.getMu()), state.getFrame()); this.absPva = null; } else { - final T zero = field.getZero(); - final T x = zero.add(state.getPVCoordinates().getPosition().getX()); - final T y = zero.add(state.getPVCoordinates().getPosition().getY()); - final T z = zero.add(state.getPVCoordinates().getPosition().getZ()); - final T vx = zero.add(state.getPVCoordinates().getVelocity().getX()); - final T vy = zero.add(state.getPVCoordinates().getVelocity().getY()); - final T vz = zero.add(state.getPVCoordinates().getVelocity().getZ()); - final T ax = zero.add(state.getPVCoordinates().getAcceleration().getX()); - final T ay = zero.add(state.getPVCoordinates().getAcceleration().getY()); - final T az = zero.add(state.getPVCoordinates().getAcceleration().getZ()); - final FieldPVCoordinates pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x, y, z), - new FieldVector3D<>(vx, vy, vz), - new FieldVector3D<>(ax, ay, az)); + final TimeStampedPVCoordinates tspva = state.getPVCoordinates(); + final FieldVector3D position = new FieldVector3D<>(field, tspva.getPosition()); + final FieldVector3D velocity = new FieldVector3D<>(field, tspva.getVelocity()); + final FieldVector3D acceleration = new FieldVector3D<>(field, tspva.getAcceleration()); + final FieldPVCoordinates pva = new FieldPVCoordinates<>(position, velocity, acceleration); final FieldAbsoluteDate dateF = new FieldAbsoluteDate<>(field, state.getDate()); this.orbit = null; - this.absPva = new FieldAbsolutePVCoordinates<>(state.getFrame(), dateF, pva_f); + this.absPva = new FieldAbsolutePVCoordinates<>(state.getFrame(), dateF, pva); } this.attitude = new FieldAttitude<>(field, state.getAttitude()); @@ -375,8 +309,8 @@ public FieldSpacecraftState(final Field field, final SpacecraftState state) { */ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva) { this(absPva, - new LofOffset(absPva.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()). + getAttitude(absPva, absPva.getDate(), absPva.getFrame()), absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary) null); } @@ -398,9 +332,8 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi * @param mass the mass (kg) */ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final T mass) { - this(absPva, - new LofOffset(absPva.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()) + .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), mass, (FieldArrayDictionary) null); } @@ -416,17 +349,6 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi this(absPva, attitude, mass, (FieldArrayDictionary) null); } - /** Build a spacecraft state from orbit only. - *

        Attitude and mass are set to unspecified non-null arbitrary values.

        - * @param absPva position-velocity-acceleration - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Map additional) { - this(absPva, new FieldArrayDictionary<>(absPva.getDate().getField(), additional)); - } - /** Build a spacecraft state from orbit only. *

        Attitude and mass are set to unspecified non-null arbitrary values.

        * @param absPva position-velocity-acceleration @@ -434,28 +356,11 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Ma * @since 11.1 */ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final FieldArrayDictionary additional) { - this(absPva, - new LofOffset(absPva.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()) + .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional); } - /** Build a spacecraft state from orbit and attitude. - *

        Mass is set to an unspecified non-null arbitrary value.

        - * @param absPva position-velocity-acceleration - * @param attitude attitude - * @param additional additional states - * @exception IllegalArgumentException if orbit and attitude dates - * or frames are not equal - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, FieldAttitude, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final FieldAttitude attitude, - final Map additional) - throws IllegalArgumentException { - this(absPva, attitude, new FieldArrayDictionary<>(absPva.getDate().getField(), additional)); - } - /** Build a spacecraft state from orbit and attitude. *

        Mass is set to an unspecified non-null arbitrary value.

        * @param absPva position-velocity-acceleration @@ -471,18 +376,6 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional); } - /** Create a new instance from orbit and mass. - *

        Attitude law is set to an unspecified default attitude.

        - * @param absPva position-velocity-acceleration - * @param mass the mass (kg) - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, CalculusFieldElement, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final T mass, final Map additional) { - this(absPva, mass, new FieldArrayDictionary<>(absPva.getDate().getField(), additional)); - } - /** Create a new instance from orbit and mass. *

        Attitude law is set to an unspecified default attitude.

        * @param absPva position-velocity-acceleration @@ -491,25 +384,11 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final T * @since 11.1 */ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final T mass, final FieldArrayDictionary additional) { - this(absPva, - new LofOffset(absPva.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()) + .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), mass, additional); } - /** Build a spacecraft state from orbit, attitude and mass. - * @param absPva position-velocity-acceleration - * @param attitude attitude - * @param mass the mass (kg) - * @param additional additional states (may be null if no additional states are available) - * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, FieldAttitude, CalculusFieldElement, FieldArrayDictionary)} - */ - @Deprecated - public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final FieldAttitude attitude, - final T mass, final Map additional) { - this(absPva, attitude, mass, new FieldArrayDictionary<>(absPva.getDate().getField(), additional)); - } - /** Build a spacecraft state from orbit, attitude and mass. * @param absPva position-velocity-acceleration * @param attitude attitude @@ -541,14 +420,14 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi this.attitude = attitude; this.mass = mass; if (additional == null) { - this.additional = new FieldArrayDictionary(absPva.getDate().getField()); + this.additional = new FieldArrayDictionary<>(absPva.getDate().getField()); } else { - this.additional = new FieldArrayDictionary(additional); + this.additional = new FieldArrayDictionary<>(additional); } if (additionalDot == null) { - this.additionalDot = new FieldArrayDictionary(absPva.getDate().getField()); + this.additionalDot = new FieldArrayDictionary<>(absPva.getDate().getField()); } else { - this.additionalDot = new FieldArrayDictionary(additionalDot); + this.additionalDot = new FieldArrayDictionary<>(additionalDot); } } @@ -568,13 +447,13 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi * @return a new instance, with the additional state added * @see #hasAdditionalState(String) * @see #getAdditionalState(String) - * @see #getAdditionalStates() + * @see #getAdditionalStatesValues() */ @SafeVarargs public final FieldSpacecraftState addAdditionalState(final String name, final T... value) { final FieldArrayDictionary newDict = new FieldArrayDictionary<>(additional); newDict.put(name, value.clone()); - if (absPva == null) { + if (isOrbitDefined()) { return new FieldSpacecraftState<>(orbit, attitude, mass, newDict, additionalDot); } else { return new FieldSpacecraftState<>(absPva, attitude, mass, newDict, additionalDot); @@ -601,7 +480,7 @@ public final FieldSpacecraftState addAdditionalState(final String name, final public final FieldSpacecraftState addAdditionalStateDerivative(final String name, final T... value) { final FieldArrayDictionary newDict = new FieldArrayDictionary<>(additionalDot); newDict.put(name, value.clone()); - if (absPva == null) { + if (isOrbitDefined()) { return new FieldSpacecraftState<>(orbit, attitude, mass, additional, newDict); } else { return new FieldSpacecraftState<>(absPva, attitude, mass, additional, newDict); @@ -696,8 +575,9 @@ private static > void checkConsistency(final F * @return a new state, shifted with respect to the instance (which is immutable) * except for the mass which stay unchanged */ + @Override public FieldSpacecraftState shiftedBy(final double dt) { - if (absPva == null) { + if (isOrbitDefined()) { return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt), mass, shiftAdditional(dt), additionalDot); } else { @@ -737,8 +617,9 @@ public FieldSpacecraftState shiftedBy(final double dt) { * @return a new state, shifted with respect to the instance (which is immutable) * except for the mass which stay unchanged */ + @Override public FieldSpacecraftState shiftedBy(final T dt) { - if (absPva == null) { + if (isOrbitDefined()) { return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt), mass, shiftAdditional(dt), additionalDot); } else { @@ -760,7 +641,7 @@ private FieldArrayDictionary shiftAdditional(final double dt) { } // there are derivatives, we need to take them into account in the additional state - final FieldArrayDictionary shifted = new FieldArrayDictionary(additional); + final FieldArrayDictionary shifted = new FieldArrayDictionary<>(additional); for (final FieldArrayDictionary.Entry dotEntry : additionalDot.getData()) { final FieldArrayDictionary.Entry entry = shifted.getEntry(dotEntry.getKey()); if (entry != null) { @@ -785,7 +666,7 @@ private FieldArrayDictionary shiftAdditional(final T dt) { } // there are derivatives, we need to take them into account in the additional state - final FieldArrayDictionary shifted = new FieldArrayDictionary(additional); + final FieldArrayDictionary shifted = new FieldArrayDictionary<>(additional); for (final FieldArrayDictionary.Entry dotEntry : additionalDot.getData()) { final FieldArrayDictionary.Entry entry = shifted.getEntry(dotEntry.getKey()); if (entry != null) { @@ -797,119 +678,6 @@ private FieldArrayDictionary shiftAdditional(final T dt) { } - /** {@inheritDoc} - *

        - * The additional states that are interpolated are the ones already present - * in the instance. The sample instances must therefore have at least the same - * additional states has the instance. They may have more additional states, - * but the extra ones will be ignored. - *

        - *

        - * The instance and all the sample instances must be based on similar - * trajectory data, i.e. they must either all be based on orbits or all be based - * on absolute position-velocity-acceleration. Any inconsistency will trigger - * an {@link OrekitIllegalStateException}. - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - * @exception OrekitIllegalStateException if some instances are not based on - * similar trajectory data - */ - public FieldSpacecraftState interpolate(final FieldAbsoluteDate date, - final Stream> sample) { - - // prepare interpolators - final List> orbits; - final List> absPvas; - if (isOrbitDefined()) { - orbits = new ArrayList>(); - absPvas = null; - } else { - orbits = null; - absPvas = new ArrayList>(); - } - final List> attitudes = new ArrayList<>(); - final FieldHermiteInterpolator massInterpolator = new FieldHermiteInterpolator<>(); - final List.Entry> addionalEntries = additional.getData(); - final Map> additionalInterpolators = - new HashMap>(additional.size()); - for (final FieldArrayDictionary.Entry entry : addionalEntries) { - additionalInterpolators.put(entry.getKey(), new FieldHermiteInterpolator<>()); - } - final List.Entry> addionalDotEntries = additionalDot.getData(); - final Map> additionalDotInterpolators = - new HashMap>(additionalDot.size()); - for (final FieldArrayDictionary.Entry entry : addionalDotEntries) { - additionalDotInterpolators.put(entry.getKey(), new FieldHermiteInterpolator<>()); - } - - // extract sample data - sample.forEach(state -> { - final T deltaT = state.getDate().durationFrom(date); - if (isOrbitDefined()) { - orbits.add(state.getOrbit()); - } else { - absPvas.add(state.getAbsPVA()); - } - attitudes.add(state.getAttitude()); - final T[] mm = MathArrays.buildArray(date.getField(), 1); - mm[0] = state.getMass(); - massInterpolator.addSamplePoint(deltaT, - mm); - for (final Map.Entry> entry : additionalInterpolators.entrySet()) { - entry.getValue().addSamplePoint(deltaT, state.getAdditionalState(entry.getKey())); - } - for (final Map.Entry> entry : additionalDotInterpolators.entrySet()) { - entry.getValue().addSamplePoint(deltaT, state.getAdditionalStateDerivative(entry.getKey())); - } - }); - - // perform interpolations - final FieldOrbit interpolatedOrbit; - final FieldAbsolutePVCoordinates interpolatedAbsPva; - if (isOrbitDefined()) { - interpolatedOrbit = orbit.interpolate(date, orbits); - interpolatedAbsPva = null; - } else { - interpolatedOrbit = null; - interpolatedAbsPva = absPva.interpolate(date, absPvas); - } - final FieldAttitude interpolatedAttitude = attitude.interpolate(date, attitudes); - final T interpolatedMass = massInterpolator.value(date.getField().getZero())[0]; - final FieldArrayDictionary interpolatedAdditional; - if (additionalInterpolators.isEmpty()) { - interpolatedAdditional = null; - } else { - interpolatedAdditional = new FieldArrayDictionary<>(date.getField(), additionalInterpolators.size()); - for (final Map.Entry> entry : additionalInterpolators.entrySet()) { - interpolatedAdditional.put(entry.getKey(), entry.getValue().value(date.getField().getZero())); - } - } - final FieldArrayDictionary interpolatedAdditionalDot; - if (additionalDotInterpolators.isEmpty()) { - interpolatedAdditionalDot = null; - } else { - interpolatedAdditionalDot = new FieldArrayDictionary<>(date.getField(), additionalDotInterpolators.size()); - for (final Map.Entry> entry : additionalDotInterpolators.entrySet()) { - interpolatedAdditionalDot.put(entry.getKey(), entry.getValue().value(date.getField().getZero())); - } - } - - // create the complete interpolated state - if (isOrbitDefined()) { - return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass, - interpolatedAdditional, interpolatedAdditionalDot); - } else { - return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass, - interpolatedAdditional, interpolatedAdditionalDot); - } - - } - /** Get the absolute position-velocity-acceleration. *

        * A state contains either an {@link FieldAbsolutePVCoordinates absolute @@ -923,7 +691,7 @@ public FieldSpacecraftState interpolate(final FieldAbsoluteDate date, * @see #getOrbit() */ public FieldAbsolutePVCoordinates getAbsPVA() throws OrekitIllegalStateException { - if (absPva == null) { + if (isOrbitDefined()) { throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ABSOLUTE_PVCOORDINATES); } return absPva; @@ -949,18 +717,17 @@ public FieldOrbit getOrbit() throws OrekitIllegalStateException { return orbit; } - /** Get the date. - * @return date - */ + /** {@inheritDoc} */ + @Override public FieldAbsoluteDate getDate() { - return (absPva == null) ? orbit.getDate() : absPva.getDate(); + return (isOrbitDefined()) ? orbit.getDate() : absPva.getDate(); } /** Get the defining frame. * @return the frame in which state is defined */ public Frame getFrame() { - return (absPva == null) ? orbit.getFrame() : absPva.getFrame(); + return (isOrbitDefined()) ? orbit.getFrame() : absPva.getFrame(); } @@ -969,7 +736,7 @@ public Frame getFrame() { * @return true if the additional state is available * @see #addAdditionalState(String, CalculusFieldElement...) * @see #getAdditionalState(String) - * @see #getAdditionalStates() + * @see #getAdditionalStatesValues() */ public boolean hasAdditionalState(final String name) { return additional.getEntry(name) != null; @@ -1051,7 +818,7 @@ public void ensureCompatibleAdditionalStates(final FieldSpacecraftState state * @return value of the additional state * @see #addAdditionalState(String, CalculusFieldElement...) * @see #hasAdditionalState(String) - * @see #getAdditionalStates() + * @see #getAdditionalStatesValues() */ public T[] getAdditionalState(final String name) { final FieldArrayDictionary.Entry entry = additional.getEntry(name); @@ -1077,18 +844,6 @@ public T[] getAdditionalStateDerivative(final String name) { return entry.getValue(); } - /** Get an unmodifiable map of additional states. - * @return unmodifiable map of additional states - * @see #addAdditionalState(String, CalculusFieldElement...) - * @see #hasAdditionalState(String) - * @see #getAdditionalState(String) - * @deprecated as of 11.1, replaced by {@link #getAdditionalStatesValues()} - */ - @Deprecated - public Map getAdditionalStates() { - return getAdditionalStatesValues().toMap(); - } - /** Get an unmodifiable map of additional states. * @return unmodifiable map of additional states * @see #addAdditionalState(String, CalculusFieldElement...) @@ -1123,129 +878,138 @@ public FieldTransform toTransform() { new FieldTransform<>(pv.getDate(), attitude.getOrientation())); } + /** Compute the static transform from state defining frame to spacecraft frame. + * @return static transform from specified frame to current spacecraft frame + * @see #toTransform() + * @since 12.0 + */ + public FieldStaticTransform toStaticTransform() { + return FieldStaticTransform.of(getDate(), getPosition().negate(), attitude.getRotation()); + } + /** Get the central attraction coefficient. * @return mu central attraction coefficient (m^3/s^2), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather than an orbit + * state contains an absolute position-velocity-acceleration rather than an orbit */ public T getMu() { - return (absPva == null) ? orbit.getMu() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getMu() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the Keplerian period. *

        The Keplerian period is computed directly from semi major axis * and central acceleration constant.

        - * @return keplerian period in seconds, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * @return Keplerian period in seconds, or {code Double.NaN} if the + * state contains an absolute position-velocity-acceleration rather * than an orbit */ public T getKeplerianPeriod() { - return (absPva == null) ? orbit.getKeplerianPeriod() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getKeplerianPeriod() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the Keplerian mean motion. *

        The Keplerian mean motion is computed directly from semi major axis * and central acceleration constant.

        - * @return keplerian mean motion in radians per second, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * @return Keplerian mean motion in radians per second, or {code Double.NaN} if the + * state contains an absolute position-velocity-acceleration rather * than an orbit */ public T getKeplerianMeanMotion() { - return (absPva == null) ? orbit.getKeplerianMeanMotion() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getKeplerianMeanMotion() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the semi-major axis. * @return semi-major axis (m), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit */ public T getA() { - return (absPva == null) ? orbit.getA() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getA() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the first component of the eccentricity vector (as per equinoctial parameters). * @return e cos(ω + Ω), first component of eccentricity vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getE() */ public T getEquinoctialEx() { - return (absPva == null) ? orbit.getEquinoctialEx() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getEquinoctialEx() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the second component of the eccentricity vector (as per equinoctial parameters). * @return e sin(ω + Ω), second component of the eccentricity vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getE() */ public T getEquinoctialEy() { - return (absPva == null) ? orbit.getEquinoctialEy() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getEquinoctialEy() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the first component of the inclination vector (as per equinoctial parameters). * @return tan(i/2) cos(Ω), first component of the inclination vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getI() */ public T getHx() { - return (absPva == null) ? orbit.getHx() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getHx() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the second component of the inclination vector (as per equinoctial parameters). * @return tan(i/2) sin(Ω), second component of the inclination vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getI() */ public T getHy() { - return (absPva == null) ? orbit.getHy() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getHy() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the true latitude argument (as per equinoctial parameters). * @return v + ω + Ω true longitude argument (rad), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getLE() * @see #getLM() */ public T getLv() { - return (absPva == null) ? orbit.getLv() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getLv() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the eccentric latitude argument (as per equinoctial parameters). * @return E + ω + Ω eccentric longitude argument (rad), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getLv() * @see #getLM() */ public T getLE() { - return (absPva == null) ? orbit.getLE() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getLE() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the mean longitude argument (as per equinoctial parameters). * @return M + ω + Ω mean latitude argument (rad), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getLv() * @see #getLE() */ public T getLM() { - return (absPva == null) ? orbit.getLM() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getLM() : absPva.getDate().getField().getZero().add(Double.NaN); } // Additional orbital elements /** Get the eccentricity. * @return eccentricity, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getEquinoctialEx() * @see #getEquinoctialEy() */ public T getE() { - return (absPva == null) ? orbit.getE() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getE() : absPva.getDate().getField().getZero().add(Double.NaN); } /** Get the inclination. @@ -1254,7 +1018,15 @@ public T getE() { * @see #getHy() */ public T getI() { - return (absPva == null) ? orbit.getI() : absPva.getDate().getField().getZero().add(Double.NaN); + return (isOrbitDefined()) ? orbit.getI() : absPva.getDate().getField().getZero().add(Double.NaN); + } + + /** Get the position in orbit definition frame. + * @return position in orbit definition frame + * @since 12.0 + */ + public FieldVector3D getPosition() { + return (isOrbitDefined()) ? orbit.getPosition() : absPva.getPosition(); } /** Get the {@link TimeStampedFieldPVCoordinates} in orbit definition frame. @@ -1268,7 +1040,17 @@ public T getI() { * @return pvCoordinates in orbit definition frame */ public TimeStampedFieldPVCoordinates getPVCoordinates() { - return (absPva == null) ? orbit.getPVCoordinates() : absPva.getPVCoordinates(); + return (isOrbitDefined()) ? orbit.getPVCoordinates() : absPva.getPVCoordinates(); + } + + /** Get the position in given output frame. + * @param outputFrame frame in which position should be defined + * @return position in given output frame + * @since 12.0 + * @see #getPVCoordinates(Frame) + */ + public FieldVector3D getPosition(final Frame outputFrame) { + return (isOrbitDefined()) ? orbit.getPosition(outputFrame) : absPva.getPosition(outputFrame); } /** Get the {@link TimeStampedFieldPVCoordinates} in given output frame. @@ -1282,8 +1064,8 @@ public TimeStampedFieldPVCoordinates getPVCoordinates() { * @param outputFrame frame in which coordinates should be defined * @return pvCoordinates in orbit definition frame */ - public TimeStampedFieldPVCoordinatesgetPVCoordinates(final Frame outputFrame) { - return (absPva == null) ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame); + public TimeStampedFieldPVCoordinates getPVCoordinates(final Frame outputFrame) { + return (isOrbitDefined()) ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame); } /** Get the attitude. diff --git a/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java b/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java new file mode 100644 index 0000000000..874f85bb0f --- /dev/null +++ b/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java @@ -0,0 +1,609 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.Pair; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FieldAttitude; +import org.orekit.attitudes.FieldAttitudeInterpolator; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitInternalError; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.FieldOrbitHermiteInterpolator; +import org.orekit.time.AbstractFieldTimeInterpolator; +import org.orekit.time.AbstractTimeInterpolator; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeInterpolator; +import org.orekit.time.FieldTimeStamped; +import org.orekit.time.TimeStampedField; +import org.orekit.time.TimeStampedFieldHermiteInterpolator; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.FieldAbsolutePVCoordinates; +import org.orekit.utils.FieldAbsolutePVCoordinatesHermiteInterpolator; +import org.orekit.utils.FieldArrayDictionary; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Generic class for spacecraft state interpolator. + *

        + * The user can specify what interpolator to use for each attribute of the spacecraft state. However, at least one + * interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other interpolators can be + * left to null if the user do not want to interpolate these values. + * + * @param type of the field element + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see SpacecraftState + */ +public class FieldSpacecraftStateInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** + * Output frame. + *

        Must be inertial if interpolating spacecraft states defined by orbit

        + */ + private final Frame outputFrame; + + /** Orbit interpolator. */ + private final FieldTimeInterpolator, KK> orbitInterpolator; + + /** Absolute position-velocity-acceleration interpolator. */ + private final FieldTimeInterpolator, KK> absPVAInterpolator; + + /** Mass interpolator. */ + private final FieldTimeInterpolator, KK> massInterpolator; + + /** Attitude interpolator. */ + private final FieldTimeInterpolator, KK> attitudeInterpolator; + + /** Additional state interpolator. */ + private final FieldTimeInterpolator, KK> additionalStateInterpolator; + + /** + * Simplest constructor to create a default Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Same frame for coordinates and attitude
        • + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s
        • + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param outputFrame output frame + */ + public FieldSpacecraftStateInterpolator(final Frame outputFrame) { + this(DEFAULT_INTERPOLATION_POINTS, outputFrame); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Same frame for coordinates and attitude
        • + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s
        • + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param outputFrame output frame + */ + public FieldSpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame) { + this(interpolationPoints, outputFrame, outputFrame); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s
        • + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param outputFrame output frame + * @param attitudeReferenceFrame reference frame from which attitude is defined + */ + public FieldSpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame, + final Frame attitudeReferenceFrame) { + this(interpolationPoints, AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, + outputFrame, attitudeReferenceFrame); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame output frame + * @param attitudeReferenceFrame reference frame from which attitude is defined + */ + public FieldSpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputFrame, final Frame attitudeReferenceFrame) { + this(interpolationPoints, extrapolationThreshold, outputFrame, attitudeReferenceFrame, + CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame output frame + * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation + * @param attitudeReferenceFrame reference frame from which attitude is defined + * @param angularFilter filter for derivatives from the sample to use in attitude interpolation + */ + public FieldSpacecraftStateInterpolator(final int interpolationPoints, + final double extrapolationThreshold, + final Frame outputFrame, + final Frame attitudeReferenceFrame, + final CartesianDerivativesFilter pvaFilter, + final AngularDerivativesFilter angularFilter) { + + this(outputFrame, + new FieldOrbitHermiteInterpolator<>(interpolationPoints, extrapolationThreshold, outputFrame, pvaFilter), + new FieldAbsolutePVCoordinatesHermiteInterpolator<>(interpolationPoints, extrapolationThreshold, outputFrame, + pvaFilter), + new TimeStampedFieldHermiteInterpolator<>(interpolationPoints, extrapolationThreshold), + new FieldAttitudeInterpolator<>(attitudeReferenceFrame, + new TimeStampedFieldAngularCoordinatesHermiteInterpolator( + interpolationPoints, extrapolationThreshold, angularFilter)), + new TimeStampedFieldHermiteInterpolator<>(interpolationPoints, extrapolationThreshold)); + } + + /** + * Constructor. + *

        + * BEWARE: output frame must be inertial if interpolated spacecraft states are defined by orbit. Throws an + * error otherwise. + * + * @param outputFrame output frame + * @param orbitInterpolator orbit interpolator + * @param absPVAInterpolator absolute position-velocity-acceleration + * @param massInterpolator mass interpolator + * @param attitudeInterpolator attitude interpolator + * @param additionalStateInterpolator additional state interpolator + */ + public FieldSpacecraftStateInterpolator(final Frame outputFrame, + final FieldTimeInterpolator, KK> orbitInterpolator, + final FieldTimeInterpolator, KK> absPVAInterpolator, + final FieldTimeInterpolator, KK> massInterpolator, + final FieldTimeInterpolator, KK> attitudeInterpolator, + final FieldTimeInterpolator, KK> additionalStateInterpolator) { + super(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator); + this.outputFrame = outputFrame; + this.orbitInterpolator = orbitInterpolator; + this.absPVAInterpolator = absPVAInterpolator; + this.massInterpolator = massInterpolator; + this.attitudeInterpolator = attitudeInterpolator; + this.additionalStateInterpolator = additionalStateInterpolator; + } + + /** + * {@inheritDoc} + *

        + * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample + * instances must therefore have at least the same additional states as this neighbor instance. They may have more + * additional states, but the extra ones will be ignored. + *

        + * All the sample instances must be based on similar trajectory data, i.e. they must either all be based on + * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an + * {@link OrekitIllegalArgumentException}. + * + * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute + * position-velocity-acceleration coordinates + * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state + * definition type + */ + @Override + public FieldSpacecraftState interpolate(final FieldAbsoluteDate interpolationDate, + final Collection> sample) { + + final List> sampleList = new ArrayList<>(sample); + + // Convert to spacecraft state for consistency check + final List nonFieldSampleList = + sampleList.stream().map(FieldSpacecraftState::toSpacecraftState).collect(Collectors.toList()); + + // If sample is empty, an error will be thrown in super method + if (!sampleList.isEmpty()) { + + // Check given that given states definition are consistent + // (all defined by either orbits or absolute position-velocity-acceleration coordinates) + SpacecraftStateInterpolator.checkStatesDefinitionsConsistency(nonFieldSampleList); + + // Check interpolator and sample consistency + SpacecraftStateInterpolator.checkSampleAndInterpolatorConsistency(nonFieldSampleList, + orbitInterpolator != null, + absPVAInterpolator != null); + } + + return super.interpolate(interpolationDate, sample); + } + + /** {@inheritDoc} */ + @Override + public List, KK>> getSubInterpolators() { + + // Add all sub interpolators that are defined + final List, KK>> subInterpolators = new ArrayList<>(); + + addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators); + + return subInterpolators; + } + + /** + * @return fail to return number of interpolation points used by this interpolator. + * + * @throws OrekitException because multiple interpolator are defined so the number of interpolation points used may + * differ. + */ + @Override + public int getNbInterpolationPoints() { + throw new OrekitException(OrekitMessages.MULTIPLE_INTERPOLATOR_USED); + } + + /** + * {@inheritDoc} + */ + @Override + protected FieldSpacecraftState interpolate(final InterpolationData interpolationData) { + + // Get interpolation date + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + + // Get first state definition + final FieldSpacecraftState earliestState = interpolationData.getNeighborList().get(0); + final boolean areOrbitDefined = earliestState.isOrbitDefined(); + + // Prepare samples + final List> attitudes = new ArrayList<>(); + + final List> masses = new ArrayList<>(); + + final List.Entry> additionalEntries = + earliestState.getAdditionalStatesValues().getData(); + final Map, KK[]>>> additionalSample = + createAdditionalStateSample(additionalEntries); + + final List.Entry> additionalDotEntries = + earliestState.getAdditionalStatesDerivatives().getData(); + final Map, KK[]>>> additionalDotSample = + createAdditionalStateSample(additionalDotEntries); + + // Fill interpolators with samples + final List> samples = interpolationData.getCachedSamples().getAll(); + final List> orbitSample = new ArrayList<>(); + final List> absPVASample = new ArrayList<>(); + for (FieldSpacecraftState state : samples) { + final FieldAbsoluteDate currentDate = state.getDate(); + + // Add orbit sample if state is defined with an orbit + if (state.isOrbitDefined()) { + orbitSample.add(state.getOrbit()); + } + // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration + else { + absPVASample.add(state.getAbsPVA()); + } + + // Add mass sample + if (massInterpolator != null) { + masses.add(new TimeStampedField<>(state.getMass(), state.getDate())); + } + + // Add attitude sample if it is interpolated + if (attitudeInterpolator != null) { + attitudes.add(state.getAttitude()); + } + + if (additionalStateInterpolator != null) { + + // Add all additional state values if they are interpolated + for (final Map.Entry, KK[]>>> entry : additionalSample.entrySet()) { + entry.getValue().add(new Pair<>(currentDate, state.getAdditionalState(entry.getKey()))); + } + + // Add all additional state derivative values if they are interpolated + for (final Map.Entry, KK[]>>> entry : additionalDotSample.entrySet()) { + entry.getValue().add(new Pair<>(currentDate, state.getAdditionalStateDerivative(entry.getKey()))); + } + } + + } + + // Interpolate mass + final KK one = interpolationData.getOne(); + final KK interpolatedMass; + if (massInterpolator != null) { + interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue(); + } + else { + interpolatedMass = one.multiply(SpacecraftState.DEFAULT_MASS); + } + + // Interpolate additional states and derivatives + final FieldArrayDictionary interpolatedAdditional; + final FieldArrayDictionary interpolatedAdditionalDot; + if (additionalStateInterpolator != null) { + interpolatedAdditional = interpolateAdditionalState(interpolationDate, additionalSample); + interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample); + } + else { + interpolatedAdditional = null; + interpolatedAdditionalDot = null; + } + + // Interpolate orbit + if (areOrbitDefined && orbitInterpolator != null) { + final FieldOrbit interpolatedOrbit = + orbitInterpolator.interpolate(interpolationDate, orbitSample); + + final FieldAttitude interpolatedAttitude = + interpolateAttitude(interpolationDate, attitudes, interpolatedOrbit); + + return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass, + interpolatedAdditional, interpolatedAdditionalDot); + } + // Interpolate absolute position-velocity-acceleration + else if (!areOrbitDefined && absPVAInterpolator != null) { + + final FieldAbsolutePVCoordinates interpolatedAbsPva = + absPVAInterpolator.interpolate(interpolationDate, absPVASample); + + final FieldAttitude interpolatedAttitude = + interpolateAttitude(interpolationDate, attitudes, interpolatedAbsPva); + + return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass, + interpolatedAdditional, interpolatedAdditionalDot); + } + // Should never happen + else { + throw new OrekitInternalError(null); + } + + } + + /** Get output frame. + * @return output frame + */ + public Frame getOutputFrame() { + return outputFrame; + } + + /** Get orbit interpolator. + * @return optional orbit interpolator + * + * @see Optional + */ + public Optional, KK>> getOrbitInterpolator() { + return Optional.ofNullable(orbitInterpolator); + } + + /** Get absolute position-velocity-acceleration interpolator. + * @return optional absolute position-velocity-acceleration interpolator + * + * @see Optional + */ + public Optional, KK>> getAbsPVAInterpolator() { + return Optional.ofNullable(absPVAInterpolator); + } + + /** Get mass interpolator. + * @return optional mass interpolator + * + * @see Optional + */ + public Optional, KK>> getMassInterpolator() { + return Optional.ofNullable(massInterpolator); + } + + /** Get attitude interpolator. + * @return optional attitude interpolator + * + * @see Optional + */ + public Optional, KK>> getAttitudeInterpolator() { + return Optional.ofNullable(attitudeInterpolator); + } + + /** Get additional state interpolator. + * @return optional additional state interpolator + * + * @see Optional + */ + public Optional, KK>> getAdditionalStateInterpolator() { + return Optional.ofNullable(additionalStateInterpolator); + } + + /** + * Check that at least one interpolator is defined. + * + * @param orbitInterpolatorToCheck orbit interpolator + * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator + */ + private void checkAtLeastOneInterpolator(final FieldTimeInterpolator, KK> orbitInterpolatorToCheck, + final FieldTimeInterpolator, KK> absPVAInterpolatorToCheck) { + if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION); + } + } + + /** + * Create empty samples for given additional entries. + * + * @param additionalEntries tabulated additional entries + * + * @return empty samples for given additional entries + */ + private Map, KK[]>>> createAdditionalStateSample( + final List.Entry> additionalEntries) { + final Map, KK[]>>> additionalSamples = + new HashMap<>(additionalEntries.size()); + + for (final FieldArrayDictionary.Entry entry : additionalEntries) { + additionalSamples.put(entry.getKey(), new ArrayList<>()); + } + + return additionalSamples; + } + + /** + * Interpolate additional state values. + * + * @param interpolationDate interpolation date + * @param additionalSamples additional state samples + * + * @return interpolated additional state values + */ + private FieldArrayDictionary interpolateAdditionalState(final FieldAbsoluteDate interpolationDate, + final Map, KK[]>>> additionalSamples) { + final Field field = interpolationDate.getField(); + final FieldArrayDictionary interpolatedAdditional; + + if (additionalSamples.isEmpty()) { + interpolatedAdditional = null; + } + else { + interpolatedAdditional = new FieldArrayDictionary<>(field, additionalSamples.size()); + for (final Map.Entry, KK[]>>> entry : additionalSamples.entrySet()) { + + // Get current entry + final List, KK[]>> currentAdditionalSamples = entry.getValue(); + + // Extract number of values for this specific entry + final int nbOfValues = currentAdditionalSamples.get(0).getValue().length; + + // For each value of current additional state entry + final KK[] currentInterpolatedAdditional = MathArrays.buildArray(field, nbOfValues); + for (int i = 0; i < nbOfValues; i++) { + + // Create final index for lambda expression use + final int currentIndex = i; + + // Create sample for specific value of current additional state values + final List> currentValueSample = new ArrayList<>(); + + currentAdditionalSamples.forEach(currentSamples -> currentValueSample.add( + new TimeStampedField<>(currentSamples.getValue()[currentIndex], currentSamples.getFirst()))); + + // Interpolate + currentInterpolatedAdditional[i] = + additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue(); + } + + interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional); + } + } + return interpolatedAdditional; + } + + /** + * Interpolate attitude. + *

        + * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame. + * + * @param interpolationDate interpolation date + * @param attitudes attitudes sample + * @param pvProvider position-velocity-acceleration coordinates provider + * + * @return interpolated attitude if attitude interpolator is present, default attitude otherwise + */ + private FieldAttitude interpolateAttitude(final FieldAbsoluteDate interpolationDate, + final List> attitudes, + final FieldPVCoordinatesProvider pvProvider) { + if (attitudes.isEmpty()) { + final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame); + return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame); + } + else { + return attitudeInterpolator.interpolate(interpolationDate, attitudes); + } + } +} diff --git a/src/main/java/org/orekit/propagation/FieldStateCovariance.java b/src/main/java/org/orekit/propagation/FieldStateCovariance.java new file mode 100644 index 0000000000..5687d382ad --- /dev/null +++ b/src/main/java/org/orekit/propagation/FieldStateCovariance.java @@ -0,0 +1,781 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.linear.Array2DRowFieldMatrix; +import org.hipparchus.linear.BlockRealMatrix; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.util.MathArrays; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; +import org.orekit.frames.LOF; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeStamped; +import org.orekit.utils.CartesianDerivativesFilter; + +/** + * This class is the representation of a covariance matrix at a given date. + *

        + * Currently, the covariance only represents the orbital elements. + *

        + * It is possible to change the covariance frame by using the {@link #changeCovarianceFrame(FieldOrbit, Frame)} or + * {@link #changeCovarianceFrame(FieldOrbit, LOF)} method. These methods are based on Equations (18) and (20) of + * Covariance Transformations for Satellite Flight Dynamics Operations by David A. SVallado. + *

        + * Finally, covariance orbit type can be changed using the + * {@link #changeCovarianceType(FieldOrbit, OrbitType, PositionAngleType) changeCovarianceType(FieldOrbit, OrbitType, + * PositionAngle)} method. + *

        + * + * @author Bryan Cazabonne + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class FieldStateCovariance> implements FieldTimeStamped { + + /** State dimension. */ + private static final int STATE_DIMENSION = 6; + + /** Default position angle for covariance expressed in cartesian elements. */ + private static final PositionAngleType DEFAULT_POSITION_ANGLE = PositionAngleType.TRUE; + + /** Orbital covariance [6x6]. */ + private final FieldMatrix orbitalCovariance; + + /** Covariance frame (can be null if LOF is defined). */ + private final Frame frame; + + /** Covariance LOF type (can be null if frame is defined). */ + private final LOF lof; + + /** Covariance epoch. */ + private final FieldAbsoluteDate epoch; + + /** Covariance orbit type. */ + private final OrbitType orbitType; + + /** Covariance position angle type (not used if orbitType is CARTESIAN). */ + private final PositionAngleType angleType; + + /** + * Constructor. + * + * @param orbitalCovariance 6x6 orbital parameters covariance + * @param epoch epoch of the covariance + * @param lof covariance LOF type + */ + public FieldStateCovariance(final FieldMatrix orbitalCovariance, final FieldAbsoluteDate epoch, + final LOF lof) { + this(orbitalCovariance, epoch, null, lof, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); + } + + /** + * Constructor. + * + * @param orbitalCovariance 6x6 orbital parameters covariance + * @param epoch epoch of the covariance + * @param covarianceFrame covariance frame (inertial or Earth fixed) + * @param orbitType orbit type of the covariance (CARTESIAN if covarianceFrame is not pseudo-inertial) + * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN) + */ + public FieldStateCovariance(final FieldMatrix orbitalCovariance, final FieldAbsoluteDate epoch, + final Frame covarianceFrame, + final OrbitType orbitType, final PositionAngleType angleType) { + this(orbitalCovariance, epoch, covarianceFrame, null, orbitType, angleType); + } + + /** + * Private constructor. + * + * @param orbitalCovariance 6x6 orbital parameters covariance + * @param epoch epoch of the covariance + * @param covarianceFrame covariance frame (inertial or Earth fixed) + * @param lof covariance LOF type + * @param orbitType orbit type of the covariance + * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN) + */ + private FieldStateCovariance(final FieldMatrix orbitalCovariance, final FieldAbsoluteDate epoch, + final Frame covarianceFrame, final LOF lof, + final OrbitType orbitType, final PositionAngleType angleType) { + + StateCovariance.checkFrameAndOrbitTypeConsistency(covarianceFrame, orbitType); + + this.orbitalCovariance = orbitalCovariance; + this.epoch = epoch; + this.frame = covarianceFrame; + this.lof = lof; + this.orbitType = orbitType; + this.angleType = angleType; + + } + + /** {@inheritDoc}. */ + @Override + public FieldAbsoluteDate getDate() { + return epoch; + } + + /** + * Get the covariance matrix. + * + * @return the covariance matrix + */ + public FieldMatrix getMatrix() { + return orbitalCovariance; + } + + /** + * Get the covariance orbit type. + * + * @return the covariance orbit type + */ + public OrbitType getOrbitType() { + return orbitType; + } + + /** + * Get the covariance angle type. + * + * @return the covariance angle type + */ + public PositionAngleType getPositionAngleType() { + return angleType; + } + + /** + * Get the covariance frame. + * + * @return the covariance frame (can be null) + * + * @see #getLOF() + */ + public Frame getFrame() { + return frame; + } + + /** + * Get the covariance LOF type. + * + * @return the covariance LOF type (can be null) + * + * @see #getFrame() + */ + public LOF getLOF() { + return lof; + } + + /** + * Get the covariance matrix in another orbit type. + *

        + * The covariance orbit type cannot be changed if the covariance + * matrix is expressed in a {@link LOF local orbital frame} or a + * non-pseudo inertial frame. + *

        + * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation. + * Hence, the current covariance matrix will not exactly match the new linearized case and the + * distribution will not follow a generalized Gaussian distribution anymore. + *

        + * This is based on equation (1) to (6) from "Vallado, D. A. (2004). Covariance transformations for satellite flight + * dynamics operations." + * + * @param orbit orbit to which the covariance matrix should correspond + * @param outOrbitType target orbit type of the state covariance matrix + * @param outAngleType target position angle type of the state covariance matrix + * + * @return a new covariance state, expressed in the target orbit type with the target position angle + * + * @see #changeCovarianceFrame(FieldOrbit, Frame) + */ + public FieldStateCovariance changeCovarianceType(final FieldOrbit orbit, final OrbitType outOrbitType, + final PositionAngleType outAngleType) { + + // Handle case where the covariance is already expressed in the output type + if (outOrbitType == orbitType && (outAngleType == angleType || outOrbitType == OrbitType.CARTESIAN)) { + if (lof == null) { + return new FieldStateCovariance<>(orbitalCovariance, epoch, frame, orbitType, angleType); + } + else { + return new FieldStateCovariance<>(orbitalCovariance, epoch, lof); + } + } + + // Check if the covariance is expressed in a celestial body frame + if (frame != null) { + + // Check if the covariance is defined in an inertial frame + if (frame.isPseudoInertial()) { + return changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType, outOrbitType, outAngleType, + orbitalCovariance); + } + + // The covariance is not defined in an inertial frame. The orbit type cannot be changed + throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME); + + } + + // The covariance is not expressed in a celestial body frame. The orbit type cannot be changed + throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF); + + } + + /** + * Get the covariance in a given local orbital frame. + *

        + * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change + * in covariance orbit type is required. + *

        + * This is based on equation (18) to (20) "from Vallado, D. A. (2004). Covariance transformations for satellite + * flight dynamics operations." + * + * @param orbit orbit to which the covariance matrix should correspond + * @param lofOut output local orbital frame + * + * @return a new covariance state, expressed in the output local orbital frame + */ + public FieldStateCovariance changeCovarianceFrame(final FieldOrbit orbit, final LOF lofOut) { + + // Verify current covariance frame + if (lof != null) { + + // Change the covariance local orbital frame + return changeFrameAndCreate(orbit, epoch, lof, lofOut, orbitalCovariance); + + } else { + + // Covariance is expressed in celestial body frame + return changeFrameAndCreate(orbit, epoch, frame, lofOut, orbitalCovariance, orbitType, angleType); + + } + + } + + /** + * Get the covariance in the output frame. + *

        + * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change + * in covariance orbit type is required. + *

        + * This is based on equation (18) to (20) "from Vallado, D. A. (2004). Covariance transformations for satellite + * flight dynamics operations." + * + * @param orbit orbit to which the covariance matrix should correspond + * @param frameOut output frame + * + * @return a new covariance state, expressed in the output frame + */ + public FieldStateCovariance changeCovarianceFrame(final FieldOrbit orbit, final Frame frameOut) { + + // Verify current covariance frame + if (lof != null) { + + // Covariance is expressed in local orbital frame + return changeFrameAndCreate(orbit, epoch, lof, frameOut, orbitalCovariance); + + } else { + + // Change covariance frame + return changeFrameAndCreate(orbit, epoch, frame, frameOut, orbitalCovariance, orbitType, angleType); + + } + + } + + /** + * Get a time-shifted covariance matrix. + *

        + * The shifting model is a linearized, Keplerian one. In other words, it is based on a state transition matrix that + * is computed assuming Keplerian motion. + *

        + * Shifting is not intended as a replacement for proper covariance propagation, but should be sufficient + * for small time shifts or coarse accuracy. + * + * @param field to which the elements belong + * @param orbit orbit to which the covariance matrix should correspond + * @param dt time shift in seconds + * + * @return a new covariance state, shifted with respect to the instance + */ + public FieldStateCovariance shiftedBy(final Field field, final FieldOrbit orbit, final T dt) { + + // Shifted orbit + final FieldOrbit shifted = orbit.shiftedBy(dt); + + // State covariance expressed in celestial body frame + if (frame != null) { + + // State covariance expressed in a pseudo-inertial frame + if (frame.isPseudoInertial()) { + + // Compute STM + final FieldMatrix stm = getStm(field, orbit, dt); + + // Convert covariance in STM type (i.e., Equinoctial elements) + final FieldStateCovariance inStmType = changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType, + OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, + orbitalCovariance); + + // Shift covariance by applying the STM + final FieldMatrix shiftedCov = stm.multiply(inStmType.getMatrix().multiplyTransposed(stm)); + + // Restore the initial covariance type + return changeTypeAndCreate(shifted, shifted.getDate(), frame, + OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, + orbitType, angleType, shiftedCov); + } + + // State covariance expressed in a non pseudo-inertial frame + else { + + // Convert state covariance to orbit pseudo-inertial frame + final FieldStateCovariance inOrbitFrame = changeCovarianceFrame(orbit, orbit.getFrame()); + + // Shift the state covariance + final FieldStateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(field, orbit, dt); + + // Restore the initial covariance frame + return shiftedCovariance.changeCovarianceFrame(shifted, frame); + } + } + + // State covariance expressed in a commonly used local orbital frame (LOF) + else { + + // Convert state covariance to orbit pseudo-inertial frame + final FieldStateCovariance inOrbitFrame = changeCovarianceFrame(orbit, orbit.getFrame()); + + // Shift the state covariance + final FieldStateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(field, orbit, dt); + + // Restore the initial covariance frame + return shiftedCovariance.changeCovarianceFrame(shifted, lof); + } + + } + + /** Get new state covariance instance. + * @return new state covariance instance. + */ + public StateCovariance toStateCovariance() { + + // Extract data + final T[][] data = orbitalCovariance.getData(); + final int rowDim = data.length; + final int columnDim = data.length; + + // Convert to non-field version + final double[][] realData = new double[rowDim][columnDim]; + for (int i = 0; i < rowDim; i++) { + for (int j = 0; j < columnDim; j++) { + realData[i][j] = data[i][j].getReal(); + } + } + final BlockRealMatrix realMatrix = new BlockRealMatrix(realData); + final AbsoluteDate realDate = epoch.toAbsoluteDate(); + + // Return new state covariance according to current state covariance definition + if (frame == null) { + return new StateCovariance(realMatrix, realDate, lof); + } + else { + return new StateCovariance(realMatrix, realDate, frame, orbitType, angleType); + } + } + + /** + * Create a covariance matrix in another orbit type. + *

        + * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation. + * Hence, the input covariance matrix will not exactly match the new linearized case and the + * distribution will not follow a generalized Gaussian distribution anymore. + *

        + * This is based on equation (1) to (6) from "Vallado, D. A. (2004). Covariance transformations for satellite flight + * dynamics operations." + * + * @param orbit orbit to which the covariance matrix should correspond + * @param date covariance epoch + * @param covFrame covariance frame + * @param inOrbitType initial orbit type of the state covariance matrix + * @param inAngleType initial position angle type of the state covariance matrix + * @param outOrbitType target orbit type of the state covariance matrix + * @param outAngleType target position angle type of the state covariance matrix + * @param inputCov input covariance + * + * @return the covariance expressed in the target orbit type with the target position angle + */ + private FieldStateCovariance changeTypeAndCreate(final FieldOrbit orbit, + final FieldAbsoluteDate date, + final Frame covFrame, + final OrbitType inOrbitType, + final PositionAngleType inAngleType, + final OrbitType outOrbitType, + final PositionAngleType outAngleType, + final FieldMatrix inputCov) { + + // Notations: + // I: Input orbit type + // O: Output orbit type + // C: Cartesian parameters + + // Extract field + final Field field = date.getField(); + + // Compute dOutputdCartesian + final T[][] aOC = MathArrays.buildArray(field, STATE_DIMENSION, STATE_DIMENSION); + final FieldOrbit orbitInOutputType = outOrbitType.convertType(orbit); + orbitInOutputType.getJacobianWrtCartesian(outAngleType, aOC); + final FieldMatrix dOdC = new Array2DRowFieldMatrix<>(aOC, false); + + // Compute dCartesiandInput + final T[][] aCI = MathArrays.buildArray(field, STATE_DIMENSION, STATE_DIMENSION); + final FieldOrbit orbitInInputType = inOrbitType.convertType(orbit); + orbitInInputType.getJacobianWrtParameters(inAngleType, aCI); + final FieldMatrix dCdI = new Array2DRowFieldMatrix<>(aCI, false); + + // Compute dOutputdInput + final FieldMatrix dOdI = dOdC.multiply(dCdI); + + // Conversion of the state covariance matrix in target orbit type + final FieldMatrix outputCov = dOdI.multiply(inputCov.multiplyTransposed(dOdI)); + + // Return the converted covariance + return new FieldStateCovariance<>(outputCov, date, covFrame, outOrbitType, outAngleType); + + } + + /** + * Create a covariance matrix from a {@link LOF local orbital frame} to another + * {@link LOF local orbital frame}. + *

        + * Changing the covariance frame is a linear process, this method does not introduce approximation. + *

        + * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics + * Operations" by David A. Vallado. + * + * @param orbit orbit to which the covariance matrix should correspond + * @param date covariance epoch + * @param lofIn the local orbital frame in which the input covariance matrix is expressed + * @param lofOut the target local orbital frame + * @param inputCartesianCov input covariance {@code CARTESIAN}) + * + * @return the covariance matrix expressed in the target commonly used local orbital frame in cartesian elements + */ + private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, + final FieldAbsoluteDate date, + final LOF lofIn, + final LOF lofOut, + final FieldMatrix inputCartesianCov) { + + // Builds the matrix to perform covariance transformation + final FieldMatrix jacobianFromLofInToLofOut = + getJacobian(date.getField(), + LOF.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates())); + + // Get the Cartesian covariance matrix converted to frameOut + final FieldMatrix cartesianCovarianceOut = + jacobianFromLofInToLofOut.multiply(inputCartesianCov.multiplyTransposed(jacobianFromLofInToLofOut)); + + // Output converted covariance + return new FieldStateCovariance<>(cartesianCovarianceOut, date, lofOut); + + } + + /** + * Convert the covariance matrix from a {@link Frame frame} to a {@link LOF local orbital frame}. + *

        + * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change + * in covariance orbit type is required. + *

        + * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics + * Operations" by David A. Vallado. + *

        + * As the frame transformation must be performed with the covariance expressed in Cartesian elements, both the orbit + * and position angle types of the input covariance must be provided. + *

        + * The output covariance matrix will provided in Cartesian orbit type and not converted back to + * its original expression (if input different from Cartesian elements). + * + * @param orbit orbit to which the covariance matrix should correspond + * @param date covariance epoch + * @param frameIn the frame in which the input covariance matrix is expressed. In case the frame is not + * pseudo-inertial, the input covariance matrix is expected to be expressed in Cartesian elements. + * @param lofOut the target local orbital frame + * @param inputCov input covariance + * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial) + * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (not used + * if covOrbitType equals {@code CARTESIAN}) + * @return the covariance matrix expressed in the target local orbital frame in Cartesian elements + * @throws OrekitException if input frame is not pseudo-inertial and the input covariance is + * not expressed in Cartesian elements. + */ + private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, + final FieldAbsoluteDate date, + final Frame frameIn, + final LOF lofOut, + final FieldMatrix inputCov, + final OrbitType covOrbitType, + final PositionAngleType covAngleType) { + + // Input frame is inertial + if (frameIn.isPseudoInertial()) { + + // Convert input matrix to Cartesian parameters in input frame + final FieldMatrix cartesianCovarianceIn = + changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType, + OrbitType.CARTESIAN, PositionAngleType.MEAN, + inputCov).getMatrix(); + + // Builds the matrix to perform covariance transformation + final FieldMatrix jacobianFromFrameInToLofOut = + getJacobian(date.getField(), lofOut.transformFromInertial(date, orbit.getPVCoordinates(frameIn))); + + // Get the Cartesian covariance matrix converted to frameOut + final FieldMatrix cartesianCovarianceOut = + jacobianFromFrameInToLofOut.multiply( + cartesianCovarianceIn.multiplyTransposed(jacobianFromFrameInToLofOut)); + + // Return converted covariance matrix expressed in cartesian elements + return new FieldStateCovariance<>(cartesianCovarianceOut, date, lofOut); + + } + + // Input frame is not inertial so the covariance matrix is expected to be in cartesian elements + else { + + // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type + final Frame orbitInertialFrame = orbit.getFrame(); + + // Compute rotation matrix from frameIn to orbit inertial frame + final FieldMatrix cartesianCovarianceInOrbitFrame = + changeFrameAndCreate(orbit, date, frameIn, orbitInertialFrame, inputCov, + OrbitType.CARTESIAN, PositionAngleType.MEAN).getMatrix(); + + // Convert from orbit inertial frame to lofOut + return changeFrameAndCreate(orbit, date, orbitInertialFrame, lofOut, cartesianCovarianceInOrbitFrame, + OrbitType.CARTESIAN, PositionAngleType.MEAN); + + } + + } + + /** + * Convert the covariance matrix from a {@link LOF local orbital frame} to a {@link Frame frame}. + *

        + * Changing the covariance frame is a linear process, this method does not introduce approximation. + *

        + * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics + * Operations" by David A. Vallado. + *

        + * The input covariance matrix is necessarily provided in Cartesian orbit type. + *

        + * The output covariance matrix will be expressed in Cartesian elements. + * + * @param orbit orbit to which the covariance matrix should correspond + * @param date covariance epoch + * @param lofIn the local orbital frame in which the input covariance matrix is expressed + * @param frameOut the target frame + * @param inputCartesianCov input covariance ({@code CARTESIAN}) + * + * @return the covariance matrix expressed in the target frame in cartesian elements + */ + private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, + final FieldAbsoluteDate date, + final LOF lofIn, + final Frame frameOut, + final FieldMatrix inputCartesianCov) { + + // Output frame is pseudo-inertial + if (frameOut.isPseudoInertial()) { + + // Builds the matrix to perform covariance transformation + final FieldMatrix jacobianFromLofInToFrameOut = + getJacobian(date.getField(), + lofIn.transformFromInertial(date, orbit.getPVCoordinates(frameOut)).getInverse()); + + // Transform covariance + final FieldMatrix transformedCovariance = + jacobianFromLofInToFrameOut.multiply( + inputCartesianCov.multiplyTransposed(jacobianFromLofInToFrameOut)); + + // Get the Cartesian covariance matrix converted to frameOut + return new FieldStateCovariance<>(transformedCovariance, date, frameOut, OrbitType.CARTESIAN, + DEFAULT_POSITION_ANGLE); + + } + + // Output frame is not pseudo-inertial + else { + + // Builds the matrix to perform covariance transformation + final FieldMatrix jacobianFromLofInToOrbitFrame = + getJacobian(date.getField(), + lofIn.transformFromInertial(date, orbit.getPVCoordinates()).getInverse()); + + // Get the Cartesian covariance matrix converted to orbit inertial frame + final FieldMatrix cartesianCovarianceInOrbitFrame = jacobianFromLofInToOrbitFrame.multiply( + inputCartesianCov.multiplyTransposed(jacobianFromLofInToOrbitFrame)); + + // Get the Cartesian covariance matrix converted to frameOut + return changeFrameAndCreate(orbit, date, orbit.getFrame(), frameOut, cartesianCovarianceInOrbitFrame, + OrbitType.CARTESIAN, PositionAngleType.MEAN); + } + + } + + /** + * Get the covariance matrix in another frame. + *

        + * The transformation is based on Equation (18) of "Covariance Transformations for Satellite Flight Dynamics + * Operations" by David A. Vallado. + *

        + * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change + * in covariance orbit type is required. + *

        + * As the frame transformation must be performed with the covariance expressed in Cartesian elements, both the orbit + * and position angle types of the input covariance must be provided. + *

        + * In case the input frame is not pseudo-inertial, the input covariance matrix is necessarily + * expressed in Cartesian elements. + *

        + * In case the output frame is not pseudo-inertial, the output covariance matrix will be + * expressed in Cartesian elements. + * + * @param orbit orbit to which the covariance matrix should correspond + * @param date covariance epoch + * @param frameIn the frame in which the input covariance matrix is expressed + * @param frameOut the target frame + * @param inputCov input covariance + * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial) + * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (not + * used if covOrbitType equals {@code CARTESIAN}) + * @return the covariance matrix expressed in the target frame + * + * @throws OrekitException if input frame is not pseudo-inertial and the input covariance is + * not expressed in cartesian elements. + */ + private FieldStateCovariance changeFrameAndCreate(final FieldOrbit orbit, + final FieldAbsoluteDate date, + final Frame frameIn, + final Frame frameOut, + final FieldMatrix inputCov, + final OrbitType covOrbitType, + final PositionAngleType covAngleType) { + + // Get the transform from the covariance frame to the output frame + final FieldTransform inToOut = frameIn.getTransformTo(frameOut, orbit.getDate()); + + // Matrix to perform the covariance transformation + final FieldMatrix j = getJacobian(date.getField(), inToOut); + + // Input frame pseudo-inertial + if (frameIn.isPseudoInertial()) { + + // Convert input matrix to Cartesian parameters in input frame + final FieldMatrix cartesianCovarianceIn = + changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType, + OrbitType.CARTESIAN, PositionAngleType.MEAN, + inputCov).getMatrix(); + + // Get the Cartesian covariance matrix converted to frameOut + final FieldMatrix cartesianCovarianceOut = j.multiply(cartesianCovarianceIn.multiplyTransposed(j)); + + // Output frame is pseudo-inertial + if (frameOut.isPseudoInertial()) { + + // Convert output Cartesian matrix to initial orbit type and position angle + return changeTypeAndCreate(orbit, date, frameOut, OrbitType.CARTESIAN, PositionAngleType.MEAN, + covOrbitType, covAngleType, cartesianCovarianceOut); + + } + + // Output frame is not pseudo-inertial + else { + + // Output Cartesian matrix + return new FieldStateCovariance<>(cartesianCovarianceOut, date, frameOut, OrbitType.CARTESIAN, + DEFAULT_POSITION_ANGLE); + + } + } + + // Input frame is not pseudo-inertial + else { + + // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type + + // Convert covariance matrix to frameOut + final FieldMatrix covInFrameOut = j.multiply(inputCov.multiplyTransposed(j)); + + // Output the Cartesian covariance matrix converted to frameOut + return new FieldStateCovariance<>(covInFrameOut, date, frameOut, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); + + } + + } + + /** + * Builds the matrix to perform covariance frame transformation. + * + * @param field to which the elements belong + * @param transform input transformation + * + * @return the matrix to perform the covariance frame transformation + */ + private FieldMatrix getJacobian(final Field field, final FieldTransform transform) { + // Get the Jacobian of the transform + final T[][] jacobian = MathArrays.buildArray(field, STATE_DIMENSION, STATE_DIMENSION); + transform.getJacobian(CartesianDerivativesFilter.USE_PV, jacobian); + // Return + return new Array2DRowFieldMatrix<>(jacobian, false); + + } + + /** + * Get the state transition matrix considering Keplerian contribution only. + * + * @param field to which the elements belong + * @param initialOrbit orbit to which the initial covariance matrix should correspond + * @param dt time difference between the two orbits + * + * @return the state transition matrix used to shift the covariance matrix + */ + private FieldMatrix getStm(final Field field, final FieldOrbit initialOrbit, final T dt) { + + // initialize the STM + final FieldMatrix stm = MatrixUtils.createFieldIdentityMatrix(field, STATE_DIMENSION); + + // State transition matrix using Keplerian contribution only + final T mu = initialOrbit.getMu(); + final T sma = initialOrbit.getA(); + final T contribution = mu.divide(sma.pow(5)).sqrt().multiply(dt).multiply(-1.5); + stm.setEntry(5, 0, contribution); + + // Return + return stm; + + } + +} diff --git a/src/main/java/org/orekit/propagation/MatricesHarvester.java b/src/main/java/org/orekit/propagation/MatricesHarvester.java index c47fff6abd..f5feb35f2b 100644 --- a/src/main/java/org/orekit/propagation/MatricesHarvester.java +++ b/src/main/java/org/orekit/propagation/MatricesHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,8 @@ import java.util.List; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; /** Interface for extracting State Transition Matrices and Jacobians matrices from {@link SpacecraftState spacecraft state}. *

        @@ -75,4 +77,19 @@ public interface MatricesHarvester { */ List getJacobiansColumnsNames(); + /** + * Get the orbit type used for the matrix computation. + * @return the orbit type used for the matrix computation + */ + OrbitType getOrbitType(); + + /** + * Get the position angle used for the matrix computation. + *

        + * Irrelevant if {@link #getOrbitType()} returns {@link OrbitType#CARTESIAN}. + *

        + * @return the position angle used for the matrix computation + */ + PositionAngleType getPositionAngleType(); + } diff --git a/src/main/java/org/orekit/propagation/PropagationType.java b/src/main/java/org/orekit/propagation/PropagationType.java index c229a9ec8b..b008348299 100644 --- a/src/main/java/org/orekit/propagation/PropagationType.java +++ b/src/main/java/org/orekit/propagation/PropagationType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/Propagator.java b/src/main/java/org/orekit/propagation/Propagator.java index d2acc58dd3..57fb969a99 100644 --- a/src/main/java/org/orekit/propagation/Propagator.java +++ b/src/main/java/org/orekit/propagation/Propagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,9 +22,10 @@ import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.linear.RealMatrix; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.frames.Frame; import org.orekit.frames.Frames; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.sampling.OrekitFixedStepHandler; import org.orekit.propagation.sampling.OrekitStepHandler; @@ -70,7 +71,7 @@ public interface Propagator extends PVCoordinatesProvider { * @return attitude law. */ static AttitudeProvider getDefaultLaw(final Frames frames) { - return new InertialProvider(Rotation.IDENTITY, frames.getEME2000()); + return new FrameAlignedProvider(Rotation.IDENTITY, frames.getEME2000()); } /** Get the multiplexer holding all step handlers. @@ -249,7 +250,7 @@ default void setStepHandler(final OrekitStepHandler handler) { *

        *

        * The arguments for initial matrices must be compatible with the {@link org.orekit.orbits.OrbitType - * orbit type} and {@link org.orekit.orbits.PositionAngle position angle} that will be used by the propagator. + * orbit type} and {@link PositionAngleType position angle} that will be used by the propagator. *

        *

        * The default implementation throws an exception as the method is not supported by all propagators. diff --git a/src/main/java/org/orekit/propagation/PropagatorsParallelizer.java b/src/main/java/org/orekit/propagation/PropagatorsParallelizer.java index e56bc3db4c..042e16379c 100644 --- a/src/main/java/org/orekit/propagation/PropagatorsParallelizer.java +++ b/src/main/java/org/orekit/propagation/PropagatorsParallelizer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,9 +29,12 @@ import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; +import org.orekit.propagation.sampling.MultiSatFixedStepHandler; import org.orekit.propagation.sampling.MultiSatStepHandler; +import org.orekit.propagation.sampling.MultisatStepNormalizer; import org.orekit.propagation.sampling.OrekitStepHandler; import org.orekit.propagation.sampling.OrekitStepInterpolator; +import org.orekit.propagation.sampling.StepHandlerMultiplexer; import org.orekit.time.AbsoluteDate; /** This class provides a way to propagate simultaneously several orbits. @@ -120,6 +123,20 @@ public PropagatorsParallelizer(final List propagators, this.globalHandler = globalHandler; } + /** Simple constructor. + * @param propagators list of propagators to use + * @param h fixed time step (sign is not used) + * @param globalHandler global handler for managing all spacecrafts + * simultaneously + * @since 12.0 + */ + public PropagatorsParallelizer(final List propagators, + final double h, + final MultiSatFixedStepHandler globalHandler) { + this.propagators = propagators; + this.globalHandler = new MultisatStepNormalizer(h, globalHandler); + } + /** Get an unmodifiable list of the underlying mono-satellite propagators. * @return unmodifiable list of the underlying mono-satellite propagators */ @@ -389,6 +406,11 @@ private static class PropagatorMonitoring { // the main thread will let underlying propagators go forward // by consuming the step handling parameters they will put at each step queue = new SynchronousQueue<>(); + + // Remove former instances of "MultiplePropagatorsHandler" from step handlers multiplexer + clearMultiplePropagatorsHandler(propagator); + + // Add MultiplePropagatorsHandler step handler propagator.getMultiplexer().add(new MultiplePropagatorsHandler(queue)); // start the propagator @@ -453,6 +475,32 @@ private void manageException(final Exception exception) { } } + /** Clear existing instances of MultiplePropagatorsHandler in a monitored propagator. + *

        + * Removes former instances of "MultiplePropagatorsHandler" from step handlers multiplexer. + *

        + * This is done to avoid propagation getting stuck after several calls to PropagatorsParallelizer.propagate(...) + *

        + * See issue 1105. + * @param propagator monitored propagator whose MultiplePropagatorsHandlers must be cleared + */ + private void clearMultiplePropagatorsHandler(final Propagator propagator) { + + // First, list instances of MultiplePropagatorsHandler in the propagator multiplexer + final StepHandlerMultiplexer multiplexer = propagator.getMultiplexer(); + final List existingMultiplePropagatorsHandler = new ArrayList<>(); + for (final OrekitStepHandler handler : multiplexer.getHandlers()) { + if (handler instanceof MultiplePropagatorsHandler) { + existingMultiplePropagatorsHandler.add(handler); + } + } + // Then, clear all MultiplePropagatorsHandler instances from multiplexer. + // This is done in two steps because method "StepHandlerMultiplexer.remove(...)" already loops on the OrekitStepHandlers, + // leading to a ConcurrentModificationException if attempting to do everything in a single loop + for (final OrekitStepHandler handler : existingMultiplePropagatorsHandler) { + multiplexer.remove(handler); + } + } } } diff --git a/src/main/java/org/orekit/propagation/SpacecraftState.java b/src/main/java/org/orekit/propagation/SpacecraftState.java index 8ab8c77b60..14e975f9c6 100644 --- a/src/main/java/org/orekit/propagation/SpacecraftState.java +++ b/src/main/java/org/orekit/propagation/SpacecraftState.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,30 +17,24 @@ package org.orekit.propagation; import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.exception.MathIllegalStateException; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.attitudes.Attitude; -import org.orekit.attitudes.LofOffset; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; -import org.orekit.frames.LOFType; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.orbits.Orbit; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeInterpolable; import org.orekit.time.TimeShiftable; import org.orekit.time.TimeStamped; import org.orekit.utils.AbsolutePVCoordinates; @@ -49,21 +43,22 @@ import org.orekit.utils.TimeStampedPVCoordinates; /** This class is the representation of a complete state holding orbit, attitude - * and mass information at a given date. + * and mass information at a given date, meant primarily for propagation. * - *

        It contains an {@link Orbit orbital state} at a current - * {@link AbsoluteDate} both handled by an {@link Orbit}, plus the current - * mass and attitude. Orbit and state are guaranteed to be consistent in terms + *

        It contains an {@link Orbit}, or an {@link AbsolutePVCoordinates} if there + * is no definite central body, plus the current mass and attitude at the intrinsic + * {@link AbsoluteDate}. Quantities are guaranteed to be consistent in terms * of date and reference frame. The spacecraft state may also contain additional * states, which are simply named double arrays which can hold any user-defined * data. *

        *

        - * The state can be slightly shifted to close dates. This shift is based on - * a simple Keplerian model for orbit, a linear extrapolation for attitude - * taking the spin rate into account and no mass change. It is not - * intended as a replacement for proper orbit and attitude propagation but - * should be sufficient for either small time shifts or coarse accuracy. + * The state can be slightly shifted to close dates. This actual shift varies + * between {@link Orbit} and {@link AbsolutePVCoordinates}. + * For attitude it is a linear extrapolation taking the spin rate into account + * and no mass change. It is not intended as a replacement for proper + * orbit and attitude propagation but should be sufficient for either small + * time shifts or coarse accuracy. *

        *

        * The instance SpacecraftState is guaranteed to be immutable. @@ -74,14 +69,14 @@ * @author Luc Maisonobe */ public class SpacecraftState - implements TimeStamped, TimeShiftable, TimeInterpolable, Serializable { + implements TimeStamped, TimeShiftable, Serializable { + + /** Default mass. */ + public static final double DEFAULT_MASS = 1000.0; /** Serializable UID. */ private static final long serialVersionUID = 20211119L; - /** Default mass. */ - private static final double DEFAULT_MASS = 1000.0; - /** * tolerance on date comparison in {@link #checkConsistency(Orbit, Attitude)}. 100 ns * corresponds to sub-mm accuracy at LEO orbital velocities. @@ -113,9 +108,8 @@ public class SpacecraftState * @param orbit the orbit */ public SpacecraftState(final Orbit orbit) { - this(orbit, - new LofOffset(orbit.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), DEFAULT_MASS, (DoubleArrayDictionary) null); } @@ -137,9 +131,8 @@ public SpacecraftState(final Orbit orbit, final Attitude attitude) * @param mass the mass (kg) */ public SpacecraftState(final Orbit orbit, final double mass) { - this(orbit, - new LofOffset(orbit.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), mass, (DoubleArrayDictionary) null); } @@ -155,17 +148,6 @@ public SpacecraftState(final Orbit orbit, final Attitude attitude, final double this(orbit, attitude, mass, (DoubleArrayDictionary) null); } - /** Build a spacecraft state from orbit and additional states. - *

        Attitude and mass are set to unspecified non-null arbitrary values.

        - * @param orbit the orbit - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(Orbit, DoubleArrayDictionary)} - */ - @Deprecated - public SpacecraftState(final Orbit orbit, final Map additional) { - this(orbit, new DoubleArrayDictionary(additional)); - } - /** Build a spacecraft state from orbit and additional states. *

        Attitude and mass are set to unspecified non-null arbitrary values.

        * @param orbit the orbit @@ -173,27 +155,11 @@ public SpacecraftState(final Orbit orbit, final Map additional * @since 11.1 */ public SpacecraftState(final Orbit orbit, final DoubleArrayDictionary additional) { - this(orbit, - new LofOffset(orbit.getFrame(), - LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()), + this(orbit, getDefaultAttitudeProvider(orbit.getFrame()) + .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), DEFAULT_MASS, additional); } - /** Build a spacecraft state from orbit, attitude and additional states. - *

        Mass is set to an unspecified non-null arbitrary value.

        - * @param orbit the orbit - * @param attitude attitude - * @param additional additional states - * @exception IllegalArgumentException if orbit and attitude dates - * or frames are not equal - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(Orbit, Attitude, DoubleArrayDictionary)} - */ - @Deprecated - public SpacecraftState(final Orbit orbit, final Attitude attitude, final Map additional) - throws IllegalArgumentException { - this(orbit, attitude, new DoubleArrayDictionary(additional)); - } - /** Build a spacecraft state from orbit, attitude and additional states. *

        Mass is set to an unspecified non-null arbitrary value.

        * @param orbit the orbit @@ -208,18 +174,6 @@ public SpacecraftState(final Orbit orbit, final Attitude attitude, final DoubleA this(orbit, attitude, DEFAULT_MASS, additional); } - /** Create a new instance from orbit, mass and additional states. - *

        Attitude law is set to an unspecified default attitude.

        - * @param orbit the orbit - * @param mass the mass (kg) - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(Orbit, double, DoubleArrayDictionary)} - */ - @Deprecated - public SpacecraftState(final Orbit orbit, final double mass, final Map additional) { - this(orbit, mass, new DoubleArrayDictionary(additional)); - } - /** Create a new instance from orbit, mass and additional states. *

        Attitude law is set to an unspecified default attitude.

        * @param orbit the orbit @@ -228,27 +182,11 @@ public SpacecraftState(final Orbit orbit, final double mass, final Map additional) - throws IllegalArgumentException { - this(orbit, attitude, mass, new DoubleArrayDictionary(additional)); - } - /** Build a spacecraft state from orbit, attitude, mass and additional states. * @param orbit the orbit * @param attitude attitude @@ -294,15 +232,13 @@ public SpacecraftState(final Orbit orbit, final Attitude attitude, final double } } - - /** Build a spacecraft state from position-velocity-acceleration only. *

        Attitude and mass are set to unspecified non-null arbitrary values.

        * @param absPva position-velocity-acceleration */ public SpacecraftState(final AbsolutePVCoordinates absPva) { - this(absPva, - new LofOffset(absPva.getFrame(), LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + this(absPva, getDefaultAttitudeProvider(absPva.getFrame()) + .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), DEFAULT_MASS, (DoubleArrayDictionary) null); } @@ -324,8 +260,8 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitu * @param mass the mass (kg) */ public SpacecraftState(final AbsolutePVCoordinates absPva, final double mass) { - this(absPva, - new LofOffset(absPva.getFrame(), LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + this(absPva, getDefaultAttitudeProvider(absPva.getFrame()) + .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), mass, (DoubleArrayDictionary) null); } @@ -341,17 +277,6 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitu this(absPva, attitude, mass, (DoubleArrayDictionary) null); } - /** Build a spacecraft state from position-velocity-acceleration and additional states. - *

        Attitude and mass are set to unspecified non-null arbitrary values.

        - * @param absPva position-velocity-acceleration - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(AbsolutePVCoordinates, DoubleArrayDictionary)} - */ - @Deprecated - public SpacecraftState(final AbsolutePVCoordinates absPva, final Map additional) { - this(absPva, new DoubleArrayDictionary(additional)); - } - /** Build a spacecraft state from position-velocity-acceleration and additional states. *

        Attitude and mass are set to unspecified non-null arbitrary values.

        * @param absPva position-velocity-acceleration @@ -359,26 +284,11 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final MapMass is set to an unspecified non-null arbitrary value.

        - * @param absPva position-velocity-acceleration - * @param attitude attitude - * @param additional additional states - * @exception IllegalArgumentException if orbit and attitude dates - * or frames are not equal - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(AbsolutePVCoordinates, Attitude, DoubleArrayDictionary)} - */ - @Deprecated - public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitude, final Map additional) - throws IllegalArgumentException { - this(absPva, attitude, new DoubleArrayDictionary(additional)); - } - /** Build a spacecraft state from position-velocity-acceleration, attitude and additional states. *

        Mass is set to an unspecified non-null arbitrary value.

        * @param absPva position-velocity-acceleration @@ -393,18 +303,6 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitu this(absPva, attitude, DEFAULT_MASS, additional); } - /** Create a new instance from position-velocity-acceleration, mass and additional states. - *

        Attitude law is set to an unspecified default attitude.

        - * @param absPva position-velocity-acceleration - * @param mass the mass (kg) - * @param additional additional states - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(AbsolutePVCoordinates, double, DoubleArrayDictionary)} - */ - @Deprecated - public SpacecraftState(final AbsolutePVCoordinates absPva, final double mass, final Map additional) { - this(absPva, mass, new DoubleArrayDictionary(additional)); - } - /** Create a new instance from position-velocity-acceleration, mass and additional states. *

        Attitude law is set to an unspecified default attitude.

        * @param absPva position-velocity-acceleration @@ -413,26 +311,11 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final double mass, fi * @since 11.1 */ public SpacecraftState(final AbsolutePVCoordinates absPva, final double mass, final DoubleArrayDictionary additional) { - this(absPva, - new LofOffset(absPva.getFrame(), LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()), + this(absPva, getDefaultAttitudeProvider(absPva.getFrame()) + .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), mass, additional); } - /** Build a spacecraft state from position-velocity-acceleration, attitude, mass and additional states. - * @param absPva position-velocity-acceleration - * @param attitude attitude - * @param mass the mass (kg) - * @param additional additional states (may be null if no additional states are available) - * @exception IllegalArgumentException if orbit and attitude dates - * or frames are not equal - * @deprecated as of 11.1, replaced by {@link #SpacecraftState(AbsolutePVCoordinates, Attitude, double, DoubleArrayDictionary)} - */ - public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitude, - final double mass, final Map additional) - throws IllegalArgumentException { - this(absPva, attitude, mass, new DoubleArrayDictionary(additional)); - } - /** Build a spacecraft state from position-velocity-acceleration, attitude, mass and additional states. * @param absPva position-velocity-acceleration * @param attitude attitude @@ -453,7 +336,7 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitu * @param attitude attitude * @param mass the mass (kg) * @param additional additional states (may be null if no additional states are available) - * @param additionalDot additional states derivatives(may be null if no additional states derivativesare available) + * @param additionalDot additional states derivatives(may be null if no additional states derivatives are available) * @exception IllegalArgumentException if orbit and attitude dates * or frames are not equal * @since 11.1 @@ -495,12 +378,12 @@ public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitu * @return a new instance, with the additional state added * @see #hasAdditionalState(String) * @see #getAdditionalState(String) - * @see #getAdditionalStates() + * @see #getAdditionalStatesValues() */ public SpacecraftState addAdditionalState(final String name, final double... value) { final DoubleArrayDictionary newDict = new DoubleArrayDictionary(additional); newDict.put(name, value.clone()); - if (absPva == null) { + if (isOrbitDefined()) { return new SpacecraftState(orbit, attitude, mass, newDict, additionalDot); } else { return new SpacecraftState(absPva, attitude, mass, newDict, additionalDot); @@ -530,7 +413,7 @@ public SpacecraftState addAdditionalState(final String name, final double... val public SpacecraftState addAdditionalStateDerivative(final String name, final double... value) { final DoubleArrayDictionary newDict = new DoubleArrayDictionary(additionalDot); newDict.put(name, value.clone()); - if (absPva == null) { + if (isOrbitDefined()) { return new SpacecraftState(orbit, attitude, mass, additional, newDict); } else { return new SpacecraftState(absPva, attitude, mass, additional, newDict); @@ -557,6 +440,17 @@ private static void checkConsistency(final Orbit orbit, final Attitude attitude) } } + /** Defines provider for default Attitude when not passed to constructor. + * Currently chosen arbitrarily as aligned with input frame. + * It is also used in {@link FieldSpacecraftState}. + * @param frame reference frame + * @return default attitude provider + * @since 12.0 + */ + static AttitudeProvider getDefaultAttitudeProvider(final Frame frame) { + return new FrameAlignedProvider(frame); + } + /** Check if the state contains an orbit part. *

        * A state contains either an {@link AbsolutePVCoordinates absolute @@ -624,8 +518,9 @@ private static void checkConsistency(final AbsolutePVCoordinates absPva, final A * @return a new state, shifted with respect to the instance (which is immutable) * except for the mass and additional states which stay unchanged */ + @Override public SpacecraftState shiftedBy(final double dt) { - if (absPva == null) { + if (isOrbitDefined()) { return new SpacecraftState(orbit.shiftedBy(dt), attitude.shiftedBy(dt), mass, shiftAdditional(dt), additionalDot); } else { @@ -659,120 +554,6 @@ private DoubleArrayDictionary shiftAdditional(final double dt) { } - /** {@inheritDoc} - *

        - * The additional states that are interpolated are the ones already present - * in the instance. The sample instances must therefore have at least the same - * additional states has the instance. They may have more additional states, - * but the extra ones will be ignored. - *

        - *

        - * The instance and all the sample instances must be based on similar - * trajectory data, i.e. they must either all be based on orbits or all be based - * on absolute position-velocity-acceleration. Any inconsistency will trigger - * an {@link OrekitIllegalStateException}. - *

        - *

        - * As this implementation of interpolation is polynomial, it should be used only - * with small samples (about 10-20 points) in order to avoid Runge's phenomenon - * and numerical problems (including NaN appearing). - *

        - * @exception OrekitIllegalStateException if some instances are not based on - * similar trajectory data - */ - public SpacecraftState interpolate(final AbsoluteDate date, - final Stream sample) { - - // prepare interpolators - final List orbits; - final List absPvas; - if (isOrbitDefined()) { - orbits = new ArrayList(); - absPvas = null; - } else { - orbits = null; - absPvas = new ArrayList(); - } - final List attitudes = new ArrayList<>(); - final HermiteInterpolator massInterpolator = new HermiteInterpolator(); - final List addionalEntries = additional.getData(); - final Map additionalInterpolators = - new HashMap(addionalEntries.size()); - for (final DoubleArrayDictionary.Entry entry : addionalEntries) { - additionalInterpolators.put(entry.getKey(), new HermiteInterpolator()); - } - final List additionalDotEntries = additionalDot.getData(); - final Map additionalDotInterpolators = - new HashMap(additionalDotEntries.size()); - for (final DoubleArrayDictionary.Entry entry : additionalDotEntries) { - additionalDotInterpolators.put(entry.getKey(), new HermiteInterpolator()); - } - - // extract sample data - sample.forEach(state -> { - final double deltaT = state.getDate().durationFrom(date); - if (isOrbitDefined()) { - orbits.add(state.getOrbit()); - } else { - absPvas.add(state.getAbsPVA()); - } - attitudes.add(state.getAttitude()); - massInterpolator.addSamplePoint(deltaT, - new double[] { - state.getMass() - }); - for (final Map.Entry entry : additionalInterpolators.entrySet()) { - entry.getValue().addSamplePoint(deltaT, state.getAdditionalState(entry.getKey())); - } - for (final Map.Entry entry : additionalDotInterpolators.entrySet()) { - entry.getValue().addSamplePoint(deltaT, state.getAdditionalStateDerivative(entry.getKey())); - } - - }); - - // perform interpolations - final Orbit interpolatedOrbit; - final AbsolutePVCoordinates interpolatedAbsPva; - if (isOrbitDefined()) { - interpolatedOrbit = orbit.interpolate(date, orbits); - interpolatedAbsPva = null; - } else { - interpolatedOrbit = null; - interpolatedAbsPva = absPva.interpolate(date, absPvas); - } - final Attitude interpolatedAttitude = attitude.interpolate(date, attitudes); - final double interpolatedMass = massInterpolator.value(0)[0]; - final DoubleArrayDictionary interpolatedAdditional; - if (additionalInterpolators.isEmpty()) { - interpolatedAdditional = null; - } else { - interpolatedAdditional = new DoubleArrayDictionary(additionalInterpolators.size()); - for (final Map.Entry entry : additionalInterpolators.entrySet()) { - interpolatedAdditional.put(entry.getKey(), entry.getValue().value(0)); - } - } - final DoubleArrayDictionary interpolatedAdditionalDot; - if (additionalDotInterpolators.isEmpty()) { - interpolatedAdditionalDot = null; - } else { - interpolatedAdditionalDot = new DoubleArrayDictionary(additionalDotInterpolators.size()); - for (final Map.Entry entry : additionalDotInterpolators.entrySet()) { - interpolatedAdditionalDot.put(entry.getKey(), entry.getValue().value(0)); - } - } - - // create the complete interpolated state - if (isOrbitDefined()) { - return new SpacecraftState(interpolatedOrbit, interpolatedAttitude, interpolatedMass, - interpolatedAdditional, interpolatedAdditionalDot); - } else { - return new SpacecraftState(interpolatedAbsPva, interpolatedAttitude, interpolatedMass, - interpolatedAdditional, interpolatedAdditionalDot); - } - - } - /** Get the absolute position-velocity-acceleration. *

        * A state contains either an {@link AbsolutePVCoordinates absolute @@ -786,7 +567,7 @@ public SpacecraftState interpolate(final AbsoluteDate date, * @see #getOrbit() */ public AbsolutePVCoordinates getAbsPVA() throws OrekitIllegalStateException { - if (absPva == null) { + if (isOrbitDefined()) { throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ABSOLUTE_PVCOORDINATES); } return absPva; @@ -812,9 +593,8 @@ public Orbit getOrbit() throws OrekitIllegalStateException { return orbit; } - /** Get the date. - * @return date - */ + /** {@inheritDoc} */ + @Override public AbsoluteDate getDate() { return (absPva == null) ? orbit.getDate() : absPva.getDate(); } @@ -823,7 +603,7 @@ public AbsoluteDate getDate() { * @return the frame in which state is defined */ public Frame getFrame() { - return (absPva == null) ? orbit.getFrame() : absPva.getFrame(); + return (isOrbitDefined()) ? orbit.getFrame() : absPva.getFrame(); } /** Check if an additional state is available. @@ -831,7 +611,7 @@ public Frame getFrame() { * @return true if the additional state is available * @see #addAdditionalState(String, double[]) * @see #getAdditionalState(String) - * @see #getAdditionalStates() + * @see #getAdditionalStatesValues() */ public boolean hasAdditionalState(final String name) { return additional.getEntry(name) != null; @@ -914,7 +694,7 @@ public void ensureCompatibleAdditionalStates(final SpacecraftState state) * @return value of the additional state * @see #addAdditionalState(String, double[]) * @see #hasAdditionalState(String) - * @see #getAdditionalStates() + * @see #getAdditionalStatesValues() */ public double[] getAdditionalState(final String name) { final DoubleArrayDictionary.Entry entry = additional.getEntry(name); @@ -940,18 +720,6 @@ public double[] getAdditionalStateDerivative(final String name) { return entry.getValue(); } - /** Get an unmodifiable map of additional states. - * @return unmodifiable map of additional states - * @see #addAdditionalState(String, double[]) - * @see #hasAdditionalState(String) - * @see #getAdditionalState(String) - * @deprecated as of 11.1, replaced by {@link #getAdditionalStatesValues()} - */ - @Deprecated - public Map getAdditionalStates() { - return getAdditionalStatesValues().toMap(); - } - /** Get an unmodifiable map of additional states. * @return unmodifiable map of additional states * @see #addAdditionalState(String, double[]) @@ -987,129 +755,138 @@ public Transform toTransform() { new Transform(pv.getDate(), attitude.getOrientation())); } + /** Compute the static transform from state defining frame to spacecraft frame. + * @return static transform from specified frame to current spacecraft frame + * @see #toTransform() + * @since 12.0 + */ + public StaticTransform toStaticTransform() { + return StaticTransform.of(getDate(), getPosition().negate(), attitude.getRotation()); + } + /** Get the central attraction coefficient. * @return mu central attraction coefficient (m^3/s^2), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather than an orbit + * state contains an absolute position-velocity-acceleration rather than an orbit */ public double getMu() { - return (absPva == null) ? orbit.getMu() : Double.NaN; + return (isOrbitDefined()) ? orbit.getMu() : Double.NaN; } /** Get the Keplerian period. *

        The Keplerian period is computed directly from semi major axis * and central acceleration constant.

        - * @return keplerian period in seconds, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * @return Keplerian period in seconds, or {code Double.NaN} if the + * state contains an absolute position-velocity-acceleration rather * than an orbit */ public double getKeplerianPeriod() { - return (absPva == null) ? orbit.getKeplerianPeriod() : Double.NaN; + return (isOrbitDefined()) ? orbit.getKeplerianPeriod() : Double.NaN; } /** Get the Keplerian mean motion. *

        The Keplerian mean motion is computed directly from semi major axis * and central acceleration constant.

        - * @return keplerian mean motion in radians per second, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * @return Keplerian mean motion in radians per second, or {code Double.NaN} if the + * state contains an absolute position-velocity-acceleration rather * than an orbit */ public double getKeplerianMeanMotion() { - return (absPva == null) ? orbit.getKeplerianMeanMotion() : Double.NaN; + return (isOrbitDefined()) ? orbit.getKeplerianMeanMotion() : Double.NaN; } /** Get the semi-major axis. * @return semi-major axis (m), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit */ public double getA() { - return (absPva == null) ? orbit.getA() : Double.NaN; + return (isOrbitDefined()) ? orbit.getA() : Double.NaN; } /** Get the first component of the eccentricity vector (as per equinoctial parameters). * @return e cos(ω + Ω), first component of eccentricity vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getE() */ public double getEquinoctialEx() { - return (absPva == null) ? orbit.getEquinoctialEx() : Double.NaN; + return (isOrbitDefined()) ? orbit.getEquinoctialEx() : Double.NaN; } /** Get the second component of the eccentricity vector (as per equinoctial parameters). * @return e sin(ω + Ω), second component of the eccentricity vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getE() */ public double getEquinoctialEy() { - return (absPva == null) ? orbit.getEquinoctialEy() : Double.NaN; + return (isOrbitDefined()) ? orbit.getEquinoctialEy() : Double.NaN; } /** Get the first component of the inclination vector (as per equinoctial parameters). * @return tan(i/2) cos(Ω), first component of the inclination vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getI() */ public double getHx() { - return (absPva == null) ? orbit.getHx() : Double.NaN; + return (isOrbitDefined()) ? orbit.getHx() : Double.NaN; } /** Get the second component of the inclination vector (as per equinoctial parameters). * @return tan(i/2) sin(Ω), second component of the inclination vector, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getI() */ public double getHy() { - return (absPva == null) ? orbit.getHy() : Double.NaN; + return (isOrbitDefined()) ? orbit.getHy() : Double.NaN; } /** Get the true latitude argument (as per equinoctial parameters). * @return v + ω + Ω true longitude argument (rad), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getLE() * @see #getLM() */ public double getLv() { - return (absPva == null) ? orbit.getLv() : Double.NaN; + return (isOrbitDefined()) ? orbit.getLv() : Double.NaN; } /** Get the eccentric latitude argument (as per equinoctial parameters). * @return E + ω + Ω eccentric longitude argument (rad), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getLv() * @see #getLM() */ public double getLE() { - return (absPva == null) ? orbit.getLE() : Double.NaN; + return (isOrbitDefined()) ? orbit.getLE() : Double.NaN; } /** Get the mean longitude argument (as per equinoctial parameters). * @return M + ω + Ω mean latitude argument (rad), or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getLv() * @see #getLE() */ public double getLM() { - return (absPva == null) ? orbit.getLM() : Double.NaN; + return (isOrbitDefined()) ? orbit.getLM() : Double.NaN; } // Additional orbital elements /** Get the eccentricity. * @return eccentricity, or {code Double.NaN} if the - * state is contains an absolute position-velocity-acceleration rather + * state contains an absolute position-velocity-acceleration rather * than an orbit * @see #getEquinoctialEx() * @see #getEquinoctialEy() */ public double getE() { - return (absPva == null) ? orbit.getE() : Double.NaN; + return (isOrbitDefined()) ? orbit.getE() : Double.NaN; } /** Get the inclination. @@ -1118,7 +895,16 @@ public double getE() { * @see #getHy() */ public double getI() { - return (absPva == null) ? orbit.getI() : Double.NaN; + return (isOrbitDefined()) ? orbit.getI() : Double.NaN; + } + + /** Get the position in orbit definition frame. + * @return position in orbit definition frame + * @since 12.0 + * @see #getPVCoordinates() + */ + public Vector3D getPosition() { + return (isOrbitDefined()) ? orbit.getPosition() : absPva.getPosition(); } /** Get the {@link TimeStampedPVCoordinates} in orbit definition frame. @@ -1132,7 +918,17 @@ public double getI() { * @return pvCoordinates in orbit definition frame */ public TimeStampedPVCoordinates getPVCoordinates() { - return (absPva == null) ? orbit.getPVCoordinates() : absPva.getPVCoordinates(); + return (isOrbitDefined()) ? orbit.getPVCoordinates() : absPva.getPVCoordinates(); + } + + /** Get the position in given output frame. + * @param outputFrame frame in which position should be defined + * @return position in given output frame + * @since 12.0 + * @see #getPVCoordinates(Frame) + */ + public Vector3D getPosition(final Frame outputFrame) { + return (isOrbitDefined()) ? orbit.getPosition(outputFrame) : absPva.getPosition(outputFrame); } /** Get the {@link TimeStampedPVCoordinates} in given output frame. @@ -1147,7 +943,7 @@ public TimeStampedPVCoordinates getPVCoordinates() { * @return pvCoordinates in orbit definition frame */ public TimeStampedPVCoordinates getPVCoordinates(final Frame outputFrame) { - return (absPva == null) ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame); + return (isOrbitDefined()) ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame); } /** Get the attitude. diff --git a/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java b/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java new file mode 100644 index 0000000000..b17a4555f6 --- /dev/null +++ b/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java @@ -0,0 +1,631 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation; + +import org.hipparchus.util.Pair; +import org.orekit.attitudes.Attitude; +import org.orekit.attitudes.AttitudeInterpolator; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitInternalError; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitHermiteInterpolator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; +import org.orekit.time.TimeInterpolator; +import org.orekit.time.TimeStamped; +import org.orekit.time.TimeStampedDouble; +import org.orekit.time.TimeStampedDoubleHermiteInterpolator; +import org.orekit.utils.AbsolutePVCoordinates; +import org.orekit.utils.AbsolutePVCoordinatesHermiteInterpolator; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.DoubleArrayDictionary; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Generic class for spacecraft state interpolator. + *

        + * The user can specify what interpolator to use for each attribute of the spacecraft state. However, at least one + * interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other interpolators can be + * left to null if the user do not want to interpolate these values. + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see SpacecraftState + */ +public class SpacecraftStateInterpolator extends AbstractTimeInterpolator { + + /** + * Output frame. + *

        Must be inertial if interpolating spacecraft states defined by orbit

        + */ + private final Frame outputFrame; + + /** Orbit interpolator. */ + private final TimeInterpolator orbitInterpolator; + + /** Absolute position-velocity-acceleration interpolator. */ + private final TimeInterpolator absPVAInterpolator; + + /** Mass interpolator. */ + private final TimeInterpolator massInterpolator; + + /** Attitude interpolator. */ + private final TimeInterpolator attitudeInterpolator; + + /** Additional state interpolator. */ + private final TimeInterpolator additionalStateInterpolator; + + /** + * Simplest constructor to create a default Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Same frame for coordinates and attitude
        • + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s
        • + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param outputFrame output frame + */ + public SpacecraftStateInterpolator(final Frame outputFrame) { + this(DEFAULT_INTERPOLATION_POINTS, outputFrame); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Same frame for coordinates and attitude
        • + *
        • Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s
        • + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param outputFrame output frame + */ + public SpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, outputFrame); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s
        • + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param outputFrame output frame + * @param attitudeReferenceFrame reference frame from which attitude is defined + */ + public SpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame, + final Frame attitudeReferenceFrame) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, attitudeReferenceFrame, + CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * The interpolators will have the following configuration : + *

          + *
        • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame output frame + * @param attitudeReferenceFrame reference frame from which attitude is defined + */ + public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputFrame, final Frame attitudeReferenceFrame) { + this(interpolationPoints, extrapolationThreshold, outputFrame, attitudeReferenceFrame, + CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR); + } + + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame output frame + * @param attitudeReferenceFrame reference frame from which attitude is defined + * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation + * @param angularFilter filter for derivatives from the sample to use in attitude interpolation + */ + public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputFrame, final Frame attitudeReferenceFrame, + final CartesianDerivativesFilter pvaFilter, + final AngularDerivativesFilter angularFilter) { + this(outputFrame, new OrbitHermiteInterpolator(interpolationPoints, extrapolationThreshold, outputFrame, pvaFilter), + new AbsolutePVCoordinatesHermiteInterpolator(interpolationPoints, extrapolationThreshold, outputFrame, + pvaFilter), + new TimeStampedDoubleHermiteInterpolator(interpolationPoints, extrapolationThreshold), + new AttitudeInterpolator(attitudeReferenceFrame, + new TimeStampedAngularCoordinatesHermiteInterpolator(interpolationPoints, + extrapolationThreshold, + angularFilter)), + new TimeStampedDoubleHermiteInterpolator(interpolationPoints, extrapolationThreshold)); + } + + /** + * Constructor. + *

        + * At least one interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other + * interpolators can be left to null if the user do not want to interpolate these values. + *

        + * BEWARE: output frame must be inertial if interpolated spacecraft states are defined by orbit. Throws an + * error otherwise. + *

        + * BEWARE: it is up to the user to check the consistency of input interpolators. + * + * @param outputFrame output frame (inertial if the user is planning to use the orbit interpolator) + * @param orbitInterpolator orbit interpolator (can be null if absPVAInterpolator is defined) + * @param absPVAInterpolator absolute position-velocity-acceleration (can be null if orbitInterpolator is defined) + * @param massInterpolator mass interpolator (can be null) + * @param attitudeInterpolator attitude interpolator (can be null) + * @param additionalStateInterpolator additional state interpolator (can be null) + */ + public SpacecraftStateInterpolator(final Frame outputFrame, final TimeInterpolator orbitInterpolator, + final TimeInterpolator absPVAInterpolator, + final TimeInterpolator massInterpolator, + final TimeInterpolator attitudeInterpolator, + final TimeInterpolator additionalStateInterpolator) { + super(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator); + this.outputFrame = outputFrame; + this.orbitInterpolator = orbitInterpolator; + this.absPVAInterpolator = absPVAInterpolator; + this.massInterpolator = massInterpolator; + this.attitudeInterpolator = attitudeInterpolator; + this.additionalStateInterpolator = additionalStateInterpolator; + } + + /** + * Check that an interpolator exist for given sample state definition. + * + * @param sample sample (non empty) + * @param orbitInterpolatorIsPresent flag defining if an orbit interpolator has been defined for this instance + * @param absPVInterpolatorIsPresent flag defining if an absolute position-velocity-acceleration interpolator has been + * defined for this instance + * + * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state + * definition type + */ + public static void checkSampleAndInterpolatorConsistency(final List sample, + final boolean orbitInterpolatorIsPresent, + final boolean absPVInterpolatorIsPresent) { + // Get first state definition + final SpacecraftState earliestState = sample.get(0); + + if (earliestState.isOrbitDefined() && !orbitInterpolatorIsPresent || + !earliestState.isOrbitDefined() && !absPVInterpolatorIsPresent) { + throw new OrekitIllegalArgumentException(OrekitMessages.WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION); + } + } + + /** + * Check that all state are either orbit defined or based on absolute position-velocity-acceleration. + * + * @param states spacecraft state sample + */ + public static void checkStatesDefinitionsConsistency(final List states) { + // Check all states handle the same additional states and are defined the same way (orbit or absolute PVA) + final SpacecraftState s0 = states.get(0); + final boolean s0IsOrbitDefined = s0.isOrbitDefined(); + for (final SpacecraftState state : states) { + s0.ensureCompatibleAdditionalStates(state); + if (s0IsOrbitDefined != state.isOrbitDefined()) { + throw new OrekitIllegalArgumentException(OrekitMessages.DIFFERENT_STATE_DEFINITION); + } + } + } + + /** + * {@inheritDoc} + *

        + * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample + * instances must therefore have at least the same additional states as this neighbor instance. They may have more + * additional states, but the extra ones will be ignored. + *

        + * All the sample instances must be based on similar trajectory data, i.e. they must either all be based on + * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an + * {@link OrekitIllegalArgumentException}. + * + * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute + * position-velocity-acceleration coordinates + * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state + * definition type + */ + @Override + public SpacecraftState interpolate(final AbsoluteDate interpolationDate, final Collection sample) { + + final List sampleList = new ArrayList<>(sample); + + // If sample is empty, an error will be thrown in super method + if (!sample.isEmpty()) { + + // Check given that given states definition are consistent + // (all defined by either orbits or absolute position-velocity-acceleration coordinates) + checkStatesDefinitionsConsistency(sampleList); + + // Check interpolator and sample consistency + checkSampleAndInterpolatorConsistency(sampleList, orbitInterpolator != null, absPVAInterpolator != null); + } + + return super.interpolate(interpolationDate, sample); + } + + /** {@inheritDoc} */ + @Override + public List> getSubInterpolators() { + + // Add all sub interpolators that are defined + final List> subInterpolators = new ArrayList<>(); + + addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators); + addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators); + + return subInterpolators; + + } + + /** + * @return fail to return number of interpolation points used by this interpolator. + * + * @throws OrekitException because multiple interpolator are defined so the number of interpolation points used may + * differ. + */ + @Override + public int getNbInterpolationPoints() { + throw new OrekitException(OrekitMessages.MULTIPLE_INTERPOLATOR_USED); + } + + /** + * {@inheritDoc} + */ + @Override + protected SpacecraftState interpolate(final InterpolationData interpolationData) { + + // Get first state definition + final SpacecraftState earliestState = interpolationData.getNeighborList().get(0); + final boolean areOrbitDefined = earliestState.isOrbitDefined(); + + // Prepare samples + final List attitudes = new ArrayList<>(); + + final List masses = new ArrayList<>(); + + final List additionalEntries = earliestState.getAdditionalStatesValues().getData(); + final Map>> additionalSample = + createAdditionalStateSample(additionalEntries); + + final List additionalDotEntries = + earliestState.getAdditionalStatesDerivatives().getData(); + final Map>> additionalDotSample = + createAdditionalStateSample(additionalDotEntries); + + // Fill interpolators with samples + final List samples = interpolationData.getCachedSamples().getAll(); + final List orbitSample = new ArrayList<>(); + final List absPVASample = new ArrayList<>(); + for (SpacecraftState state : samples) { + final AbsoluteDate currentDate = state.getDate(); + + // Add orbit sample if state is defined with an orbit + if (state.isOrbitDefined()) { + orbitSample.add(state.getOrbit()); + } + // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration + else { + absPVASample.add(state.getAbsPVA()); + } + + // Add mass sample + if (massInterpolator != null) { + masses.add(new TimeStampedDouble(state.getMass(), state.getDate())); + } + + // Add attitude sample if it is interpolated + if (attitudeInterpolator != null) { + attitudes.add(state.getAttitude()); + } + + if (additionalStateInterpolator != null) { + + // Add all additional state values if they are interpolated + for (final Map.Entry>> entry : additionalSample.entrySet()) { + entry.getValue().add(new Pair<>(currentDate, state.getAdditionalState(entry.getKey()))); + } + + // Add all additional state derivative values if they are interpolated + for (final Map.Entry>> entry : additionalDotSample.entrySet()) { + entry.getValue().add(new Pair<>(currentDate, state.getAdditionalStateDerivative(entry.getKey()))); + } + } + } + + // Interpolate mass + final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final double interpolatedMass; + if (massInterpolator != null) { + interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue(); + } else { + interpolatedMass = SpacecraftState.DEFAULT_MASS; + } + + // Interpolate additional states and derivatives + final DoubleArrayDictionary interpolatedAdditional; + final DoubleArrayDictionary interpolatedAdditionalDot; + if (additionalStateInterpolator != null) { + interpolatedAdditional = interpolateAdditionalState(interpolationDate, additionalSample); + interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample); + } else { + interpolatedAdditional = null; + interpolatedAdditionalDot = null; + } + + // Interpolate orbit + if (areOrbitDefined && orbitInterpolator != null) { + final Orbit interpolatedOrbit = orbitInterpolator.interpolate(interpolationDate, orbitSample); + + final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedOrbit); + + return new SpacecraftState(interpolatedOrbit, interpolatedAttitude, interpolatedMass, interpolatedAdditional, + interpolatedAdditionalDot); + } + // Interpolate absolute position-velocity-acceleration + else if (!areOrbitDefined && absPVAInterpolator != null) { + + final AbsolutePVCoordinates interpolatedAbsPva = absPVAInterpolator.interpolate(interpolationDate, absPVASample); + + final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedAbsPva); + + return new SpacecraftState(interpolatedAbsPva, interpolatedAttitude, interpolatedMass, interpolatedAdditional, + interpolatedAdditionalDot); + } + // Should never happen + else { + throw new OrekitInternalError(null); + } + + } + + /** + * Get output frame. + * + * @return output frame + */ + public Frame getOutputFrame() { + return outputFrame; + } + + /** + * Get orbit interpolator. + * + * @return optional orbit interpolator + * + * @see Optional + */ + public Optional> getOrbitInterpolator() { + return Optional.ofNullable(orbitInterpolator); + } + + /** + * Get absolute position-velocity-acceleration interpolator. + * + * @return optional absolute position-velocity-acceleration interpolator + * + * @see Optional + */ + public Optional> getAbsPVAInterpolator() { + return Optional.ofNullable(absPVAInterpolator); + } + + /** + * Get mass interpolator. + * + * @return optional mass interpolator + * + * @see Optional + */ + public Optional> getMassInterpolator() { + return Optional.ofNullable(massInterpolator); + } + + /** + * Get attitude interpolator. + * + * @return optional attitude interpolator + * + * @see Optional + */ + public Optional> getAttitudeInterpolator() { + return Optional.ofNullable(attitudeInterpolator); + } + + /** + * Get additional state interpolator. + * + * @return optional additional state interpolator + * + * @see Optional + */ + public Optional> getAdditionalStateInterpolator() { + return Optional.ofNullable(additionalStateInterpolator); + } + + /** + * Check that at least one interpolator is defined. + * + * @param orbitInterpolatorToCheck orbit interpolator + * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator + */ + private void checkAtLeastOneInterpolator(final TimeInterpolator orbitInterpolatorToCheck, + final TimeInterpolator absPVAInterpolatorToCheck) { + if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) { + throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION); + } + } + + /** + * Create empty samples for given additional entries. + * + * @param additionalEntries tabulated additional entries + * + * @return empty samples for given additional entries + */ + private Map>> createAdditionalStateSample( + final List additionalEntries) { + final Map>> additionalSamples = new HashMap<>(additionalEntries.size()); + + for (final DoubleArrayDictionary.Entry entry : additionalEntries) { + additionalSamples.put(entry.getKey(), new ArrayList<>()); + } + + return additionalSamples; + } + + /** + * Interpolate additional state values. + * + * @param interpolationDate interpolation date + * @param additionalSamples additional state samples + * + * @return interpolated additional state values + */ + private DoubleArrayDictionary interpolateAdditionalState(final AbsoluteDate interpolationDate, + final Map>> additionalSamples) { + final DoubleArrayDictionary interpolatedAdditional; + + if (additionalSamples.isEmpty()) { + interpolatedAdditional = null; + } else { + interpolatedAdditional = new DoubleArrayDictionary(additionalSamples.size()); + for (final Map.Entry>> entry : additionalSamples.entrySet()) { + + // Get current entry + final List> currentAdditionalSamples = entry.getValue(); + + // Extract number of values for this specific entry + final int nbOfValues = currentAdditionalSamples.get(0).getValue().length; + + // For each value of current additional state entry + final double[] currentInterpolatedAdditional = new double[nbOfValues]; + for (int i = 0; i < nbOfValues; i++) { + + // Create final index for lambda expression use + final int currentIndex = i; + + // Create sample for specific value of current additional state values + final List currentValueSample = new ArrayList<>(); + + currentAdditionalSamples.forEach(currentSamples -> currentValueSample.add( + new TimeStampedDouble(currentSamples.getValue()[currentIndex], currentSamples.getFirst()))); + + // Interpolate + currentInterpolatedAdditional[i] = + additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue(); + } + + interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional); + } + } + return interpolatedAdditional; + } + + /** + * Interpolate attitude. + *

        + * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame. + * + * @param interpolationDate interpolation date + * @param attitudes attitudes sample + * @param pvProvider position-velocity-acceleration coordinates provider + * + * @return interpolated attitude if attitude interpolator is present, default attitude otherwise + */ + private Attitude interpolateAttitude(final AbsoluteDate interpolationDate, final List attitudes, + final PVCoordinatesProvider pvProvider) { + if (attitudes.isEmpty()) { + final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame); + return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame); + } else { + return attitudeInterpolator.interpolate(interpolationDate, attitudes); + } + } +} diff --git a/src/main/java/org/orekit/propagation/StateCovariance.java b/src/main/java/org/orekit/propagation/StateCovariance.java index 3988681e3d..fbc7460d23 100644 --- a/src/main/java/org/orekit/propagation/StateCovariance.java +++ b/src/main/java/org/orekit/propagation/StateCovariance.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,15 +19,14 @@ import org.hipparchus.linear.Array2DRowRealMatrix; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; -import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; -import org.orekit.frames.LOFType; +import org.orekit.frames.LOF; import org.orekit.frames.Transform; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; import org.orekit.utils.CartesianDerivativesFilter; @@ -37,12 +36,12 @@ * Currently, the covariance only represents the orbital elements. *

        * It is possible to change the covariance frame by using the - * {@link #changeCovarianceFrame(Orbit, Frame)} or {@link #changeCovarianceFrame(Orbit, LOFType)} method. + * {@link #changeCovarianceFrame(Orbit, Frame)} or {@link #changeCovarianceFrame(Orbit, LOF)} method. * These methods are based on Equations (18) and (20) of Covariance Transformations for Satellite * Flight Dynamics Operations by David A. SVallado. *

        * Finally, covariance orbit type can be changed using the - * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngle)} method. + * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngleType)} method. * * @author Bryan Cazabonne * @author Vincent Cucchietti @@ -51,19 +50,19 @@ public class StateCovariance implements TimeStamped { /** State dimension. */ - private static final int STATE_DIMENSION = 6; + public static final int STATE_DIMENSION = 6; /** Default position angle for covariance expressed in Cartesian elements. */ - private static final PositionAngle DEFAULT_POSITION_ANGLE = PositionAngle.TRUE; + private static final PositionAngleType DEFAULT_POSITION_ANGLE = PositionAngleType.TRUE; /** Orbital covariance [6x6]. */ private final RealMatrix orbitalCovariance; - /** Covariance frame (can be null if lofType is defined). */ + /** Covariance frame (can be null if LOF is defined). */ private final Frame frame; /** Covariance LOF type (can be null if frame is defined). */ - private final LOFType lofType; + private final LOF lof; /** Covariance epoch. */ private final AbsoluteDate epoch; @@ -72,16 +71,16 @@ public class StateCovariance implements TimeStamped { private final OrbitType orbitType; /** Covariance position angle type (not used if orbitType is CARTESIAN). */ - private final PositionAngle angleType; + private final PositionAngleType angleType; /** * Constructor. * @param orbitalCovariance 6x6 orbital parameters covariance * @param epoch epoch of the covariance - * @param lofType covariance LOF type + * @param lof covariance LOF type */ - public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch, final LOFType lofType) { - this(orbitalCovariance, epoch, null, lofType, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); + public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch, final LOF lof) { + this(orbitalCovariance, epoch, null, lof, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); } /** @@ -94,7 +93,7 @@ public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate ep */ public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch, final Frame covarianceFrame, - final OrbitType orbitType, final PositionAngle angleType) { + final OrbitType orbitType, final PositionAngleType angleType) { this(orbitalCovariance, epoch, covarianceFrame, null, orbitType, angleType); } @@ -103,42 +102,44 @@ public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate ep * @param orbitalCovariance 6x6 orbital parameters covariance * @param epoch epoch of the covariance * @param covarianceFrame covariance frame (inertial or Earth fixed) - * @param lofType covariance LOF type + * @param lof covariance LOF type * @param orbitType orbit type of the covariance * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN) */ private StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch, - final Frame covarianceFrame, final LOFType lofType, - final OrbitType orbitType, final PositionAngle angleType) { + final Frame covarianceFrame, final LOF lof, + final OrbitType orbitType, final PositionAngleType angleType) { - checkInputConsistency(covarianceFrame, orbitType); + checkFrameAndOrbitTypeConsistency(covarianceFrame, orbitType); this.orbitalCovariance = orbitalCovariance; this.epoch = epoch; - this.frame = covarianceFrame; - this.lofType = lofType; + this.frame = covarianceFrame; + this.lof = lof; this.orbitType = orbitType; this.angleType = angleType; } - /** Check constructor's inputs consistency. + /** + * Check constructor's inputs consistency. * * @param covarianceFrame covariance frame (inertial or Earth fixed) * @param inputType orbit type of the covariance + * + * @throws OrekitException if input frame is not pseudo-inertial AND the orbit type is not Cartesian */ - private void checkInputConsistency(final Frame covarianceFrame, final OrbitType inputType) { + public static void checkFrameAndOrbitTypeConsistency(final Frame covarianceFrame, final OrbitType inputType) { // State covariance expressed in a celestial body frame if (covarianceFrame != null) { - // Input frame is pseudo-inertial + // Input frame is not pseudo-inertial if (!covarianceFrame.isPseudoInertial() && inputType != OrbitType.CARTESIAN) { throw new OrekitException(OrekitMessages.WRONG_ORBIT_PARAMETERS_TYPE, inputType.name(), OrbitType.CARTESIAN.name()); } - } } @@ -168,14 +169,14 @@ public OrbitType getOrbitType() { * Get the covariance angle type. * @return the covariance angle type */ - public PositionAngle getPositionAngle() { + public PositionAngleType getPositionAngleType() { return angleType; } /** * Get the covariance frame. * @return the covariance frame (can be null) - * @see #getLOFType() + * @see #getLOF() */ public Frame getFrame() { return frame; @@ -186,15 +187,15 @@ public Frame getFrame() { * @return the covariance LOF type (can be null) * @see #getFrame() */ - public LOFType getLOFType() { - return lofType; + public LOF getLOF() { + return lof; } /** * Get the covariance matrix in another orbit type. *

        * The covariance orbit type cannot be changed if the covariance - * matrix is expressed in a {@link LOFType local orbital frame} or a + * matrix is expressed in a {@link LOF local orbital frame} or a * non-pseudo inertial frame. *

        * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation. @@ -211,18 +212,28 @@ public LOFType getLOFType() { * @see #changeCovarianceFrame(Orbit, Frame) */ public StateCovariance changeCovarianceType(final Orbit orbit, final OrbitType outOrbitType, - final PositionAngle outAngleType) { + final PositionAngleType outAngleType) { + + // Handle case where the covariance is already expressed in the output type + if (outOrbitType == orbitType && (outAngleType == angleType || outOrbitType == OrbitType.CARTESIAN)) { + if (lof == null) { + return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType); + } + else { + return new StateCovariance(orbitalCovariance, epoch, lof); + } + } - // Check if the covariance expressed in a celestial body frame + // Check if the covariance is expressed in a celestial body frame if (frame != null) { - // Check if the covarianc is defined in inertial frame + // Check if the covariance is defined in an inertial frame if (frame.isPseudoInertial()) { return changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType, outOrbitType, outAngleType, orbitalCovariance); } - // The covariance is not defined in inertial frame. The orbit type cannot be changes + // The covariance is not defined in an inertial frame. The orbit type cannot be changed throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME); } @@ -245,13 +256,13 @@ public StateCovariance changeCovarianceType(final Orbit orbit, final OrbitType o * @param lofOut output local orbital frame * @return a new covariance state, expressed in the output local orbital frame */ - public StateCovariance changeCovarianceFrame(final Orbit orbit, final LOFType lofOut) { + public StateCovariance changeCovarianceFrame(final Orbit orbit, final LOF lofOut) { // Verify current covariance frame - if (lofType != null) { + if (lof != null) { // Change the covariance local orbital frame - return changeFrameAndCreate(orbit, epoch, lofType, lofOut, orbitalCovariance); + return changeFrameAndCreate(orbit, epoch, lof, lofOut, orbitalCovariance); } else { @@ -278,10 +289,10 @@ public StateCovariance changeCovarianceFrame(final Orbit orbit, final LOFType lo public StateCovariance changeCovarianceFrame(final Orbit orbit, final Frame frameOut) { // Verify current covariance frame - if (lofType != null) { + if (lof != null) { // Covariance is expressed in local orbital frame - return changeFrameAndCreate(orbit, epoch, lofType, frameOut, orbitalCovariance); + return changeFrameAndCreate(orbit, epoch, lof, frameOut, orbitalCovariance); } else { @@ -321,7 +332,7 @@ public StateCovariance shiftedBy(final Orbit orbit, final double dt) { // Convert covariance in STM type (i.e., Equinoctial elements) final StateCovariance inStmType = changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType, - OrbitType.EQUINOCTIAL, PositionAngle.MEAN, + OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, orbitalCovariance); // Shift covariance by applying the STM @@ -329,7 +340,7 @@ public StateCovariance shiftedBy(final Orbit orbit, final double dt) { // Restore the initial covariance type return changeTypeAndCreate(shifted, shifted.getDate(), frame, - OrbitType.EQUINOCTIAL, PositionAngle.MEAN, + OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, orbitType, angleType, shiftedCov); } @@ -347,7 +358,7 @@ public StateCovariance shiftedBy(final Orbit orbit, final double dt) { } } - // State covariance expressed in a commonly used local orbital frame (LOFType) + // State covariance expressed in a commonly used local orbital frame (LOF) else { // Convert state covariance to orbit pseudo-inertial frame @@ -357,7 +368,7 @@ public StateCovariance shiftedBy(final Orbit orbit, final double dt) { final StateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(orbit, dt); // Restore the initial covariance frame - return shiftedCovariance.changeCovarianceFrame(shifted, lofType); + return shiftedCovariance.changeCovarianceFrame(shifted, lof); } } @@ -384,8 +395,8 @@ public StateCovariance shiftedBy(final Orbit orbit, final double dt) { */ private static StateCovariance changeTypeAndCreate(final Orbit orbit, final AbsoluteDate date, final Frame covFrame, - final OrbitType inOrbitType, final PositionAngle inAngleType, - final OrbitType outOrbitType, final PositionAngle outAngleType, + final OrbitType inOrbitType, final PositionAngleType inAngleType, + final OrbitType outOrbitType, final PositionAngleType outAngleType, final RealMatrix inputCov) { // Notations: @@ -417,8 +428,8 @@ private static StateCovariance changeTypeAndCreate(final Orbit orbit, final Abso } /** - * Create a covariance matrix from a {@link LOFType local orbital frame} to another - * {@link LOFType local orbital frame}. + * Create a covariance matrix from a {@link LOF local orbital frame} to another + * {@link LOF local orbital frame}. *

        * Changing the covariance frame is a linear process, this method does not introduce approximation. *

        @@ -433,12 +444,12 @@ private static StateCovariance changeTypeAndCreate(final Orbit orbit, final Abso * @return the covariance matrix expressed in the target commonly used local orbital frame in Cartesian elements */ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date, - final LOFType lofIn, final LOFType lofOut, + final LOF lofIn, final LOF lofOut, final RealMatrix inputCartesianCov) { // Builds the matrix to perform covariance transformation final RealMatrix jacobianFromLofInToLofOut = - getJacobian(LOFType.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates())); + getJacobian(LOF.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates())); // Get the Cartesian covariance matrix converted to frameOut final RealMatrix cartesianCovarianceOut = @@ -450,7 +461,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs } /** - * Convert the covariance matrix from a {@link Frame frame} to a {@link LOFType local orbital frame}. + * Convert the covariance matrix from a {@link Frame frame} to a {@link LOF local orbital frame}. *

        * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change * in covariance orbit type is required. @@ -478,10 +489,10 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs * not expressed in Cartesian elements. */ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date, - final Frame frameIn, final LOFType lofOut, + final Frame frameIn, final LOF lofOut, final RealMatrix inputCov, final OrbitType covOrbitType, - final PositionAngle covAngleType) { + final PositionAngleType covAngleType) { // Input frame is inertial if (frameIn.isPseudoInertial()) { @@ -489,7 +500,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs // Convert input matrix to Cartesian parameters in input frame final RealMatrix cartesianCovarianceIn = changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType, - OrbitType.CARTESIAN, PositionAngle.MEAN, + OrbitType.CARTESIAN, PositionAngleType.MEAN, inputCov).getMatrix(); // Builds the matrix to perform covariance transformation @@ -514,18 +525,18 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs // Compute rotation matrix from frameIn to orbit inertial frame final RealMatrix cartesianCovarianceInOrbitFrame = changeFrameAndCreate(orbit, date, frameIn, orbitInertialFrame, inputCov, - OrbitType.CARTESIAN, PositionAngle.MEAN).getMatrix(); + OrbitType.CARTESIAN, PositionAngleType.MEAN).getMatrix(); // Convert from orbit inertial frame to lofOut return changeFrameAndCreate(orbit, date, orbitInertialFrame, lofOut, cartesianCovarianceInOrbitFrame, - OrbitType.CARTESIAN, PositionAngle.MEAN); + OrbitType.CARTESIAN, PositionAngleType.MEAN); } } /** - * Convert the covariance matrix from a {@link LOFType local orbital frame} to a {@link Frame frame}. + * Convert the covariance matrix from a {@link LOF local orbital frame} to a {@link Frame frame}. *

        * Changing the covariance frame is a linear process, this method does not introduce approximation. *

        @@ -544,7 +555,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs * @return the covariance matrix expressed in the target frame in Cartesian elements */ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date, - final LOFType lofIn, final Frame frameOut, + final LOF lofIn, final Frame frameOut, final RealMatrix inputCartesianCov) { // Output frame is pseudo-inertial @@ -577,7 +588,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs // Get the Cartesian covariance matrix converted to frameOut return changeFrameAndCreate(orbit, date, orbit.getFrame(), frameOut, cartesianCovarianceInOrbitFrame, - OrbitType.CARTESIAN, PositionAngle.MEAN); + OrbitType.CARTESIAN, PositionAngleType.MEAN); } } @@ -616,7 +627,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs final Frame frameIn, final Frame frameOut, final RealMatrix inputCov, final OrbitType covOrbitType, - final PositionAngle covAngleType) { + final PositionAngleType covAngleType) { // Get the transform from the covariance frame to the output frame final Transform inToOut = frameIn.getTransformTo(frameOut, orbit.getDate()); @@ -630,7 +641,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs // Convert input matrix to Cartesian parameters in input frame final RealMatrix cartesianCovarianceIn = changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType, - OrbitType.CARTESIAN, PositionAngle.MEAN, + OrbitType.CARTESIAN, PositionAngleType.MEAN, inputCov).getMatrix(); // Get the Cartesian covariance matrix converted to frameOut @@ -640,7 +651,7 @@ private static StateCovariance changeFrameAndCreate(final Orbit orbit, final Abs if (frameOut.isPseudoInertial()) { // Convert output Cartesian matrix to initial orbit type and position angle - return changeTypeAndCreate(orbit, date, frameOut, OrbitType.CARTESIAN, PositionAngle.MEAN, + return changeTypeAndCreate(orbit, date, frameOut, OrbitType.CARTESIAN, PositionAngleType.MEAN, covOrbitType, covAngleType, cartesianCovarianceOut); } @@ -691,15 +702,13 @@ private static RealMatrix getJacobian(final Transform transform) { * @param dt time difference between the two orbits * @return the state transition matrix used to shift the covariance matrix */ - private RealMatrix getStm(final Orbit initialOrbit, final double dt) { + public static RealMatrix getStm(final Orbit initialOrbit, final double dt) { // initialize the STM final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION); // State transition matrix using Keplerian contribution only - final double mu = initialOrbit.getMu(); - final double sma = initialOrbit.getA(); - final double contribution = -1.5 * dt * FastMath.sqrt(mu / FastMath.pow(sma, 5)); + final double contribution = initialOrbit.getMeanAnomalyDotWrtA() * dt; stm.setEntry(5, 0, contribution); // Return diff --git a/src/main/java/org/orekit/propagation/StateCovarianceBlender.java b/src/main/java/org/orekit/propagation/StateCovarianceBlender.java new file mode 100644 index 0000000000..c493396f21 --- /dev/null +++ b/src/main/java/org/orekit/propagation/StateCovarianceBlender.java @@ -0,0 +1,185 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation; + +import org.hipparchus.analysis.polynomials.SmoothStepFactory; +import org.hipparchus.linear.RealMatrix; +import org.orekit.frames.Frame; +import org.orekit.frames.LOFType; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeInterpolator; +import org.orekit.time.TimeStampedPair; + +import java.util.List; + +/** + * State covariance blender. + *

        + * Its purpose is to interpolate state covariance between tabulated state covariances by using the concept of blending, + * exposed in : "Efficient Covariance Interpolation using Blending of Approximate State Error Transitions" by Sergei + * Tanygin. + *

        + * It propagates tabulated values to the interpolation date assuming a standard keplerian model and then blend each + * propagated covariances using a smoothstep function. + *

        + * It gives accurate results as explained here. In the + * very poorly tracked test case evolving in a highly dynamical environment mentioned in the linked thread, the user can + * expect at worst errors of less than 0.25% in position sigmas and less than 0.4% in velocity sigmas with steps of 40mn + * between tabulated values. + * + * @author Vincent Cucchietti + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory + * @see org.hipparchus.analysis.polynomials.SmoothStepFactory.SmoothStepFunction + */ +public class StateCovarianceBlender extends AbstractStateCovarianceInterpolator { + + /** Blending function. */ + private final SmoothStepFactory.SmoothStepFunction blendingFunction; + + /** + * Constructor. + *

        + * BEWARE: If the output local orbital frame is not considered pseudo-inertial, all the covariance components + * related to the velocity will be poorly interpolated. Only the position covariance should be considered in this + * case. + * + * @param blendingFunction blending function + * @param orbitInterpolator orbit interpolator + * @param outLOF local orbital frame + * + * @see Frame + * @see OrbitType + * @see PositionAngleType + */ + public StateCovarianceBlender(final SmoothStepFactory.SmoothStepFunction blendingFunction, + final TimeInterpolator orbitInterpolator, + final LOFType outLOF) { + super(DEFAULT_INTERPOLATION_POINTS, 0., orbitInterpolator, outLOF); + this.blendingFunction = blendingFunction; + } + + /** + * Constructor. + * + * @param blendingFunction blending function + * @param orbitInterpolator orbit interpolator + * @param outFrame desired output covariance frame + * @param outPositionAngleType desired output position angle + * @param outOrbitType desired output orbit type + * + * @see Frame + * @see OrbitType + * @see PositionAngleType + */ + public StateCovarianceBlender(final SmoothStepFactory.SmoothStepFunction blendingFunction, + final TimeInterpolator orbitInterpolator, + final Frame outFrame, + final OrbitType outOrbitType, + final PositionAngleType outPositionAngleType) { + super(DEFAULT_INTERPOLATION_POINTS, 0., orbitInterpolator, outFrame, outOrbitType, outPositionAngleType); + this.blendingFunction = blendingFunction; + } + + /** {@inheritDoc} */ + @Override + protected StateCovariance computeInterpolatedCovarianceInOrbitFrame( + final List> uncertainStates, + final Orbit interpolatedOrbit) { + + // Necessarily only two sample for blending + final TimeStampedPair previousUncertainState = uncertainStates.get(0); + final TimeStampedPair nextUncertainState = uncertainStates.get(1); + + // Get the interpolation date + final AbsoluteDate interpolationDate = interpolatedOrbit.getDate(); + + // Propagate previous tabulated covariance to interpolation date + final StateCovariance forwardedCovariance = + propagateCovarianceAnalytically(interpolationDate, interpolatedOrbit, previousUncertainState); + + // Propagate next tabulated covariance to interpolation date + final StateCovariance backwardedCovariance = + propagateCovarianceAnalytically(interpolationDate, interpolatedOrbit, nextUncertainState); + + // Compute normalized time parameter + final double timeParameter = + getTimeParameter(interpolationDate, previousUncertainState.getDate(), nextUncertainState.getDate()); + + // Blend the covariance matrices + final double blendingValue = blendingFunction.value(timeParameter); + final RealMatrix forwardedCovarianceMatrix = forwardedCovariance.getMatrix(); + final RealMatrix backwardedCovarianceMatrix = backwardedCovariance.getMatrix(); + + final RealMatrix blendedCovarianceMatrix = + forwardedCovarianceMatrix.blendArithmeticallyWith(backwardedCovarianceMatrix, blendingValue); + + return new StateCovariance(blendedCovarianceMatrix, interpolationDate, interpolatedOrbit.getFrame(), + OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); + } + + /** + * Propagate given state covariance to the interpolation date using keplerian motion. + * + * @param interpolationTime interpolation date at which we desire to interpolate the state covariance + * @param orbitAtInterpolatingTime orbit at interpolation date + * @param tabulatedUncertainState tabulated uncertain state + * + * @return state covariance at given interpolation date. + * + * @see StateCovariance + */ + private StateCovariance propagateCovarianceAnalytically(final AbsoluteDate interpolationTime, + final Orbit orbitAtInterpolatingTime, + final TimeStampedPair tabulatedUncertainState) { + + // Get orbit and covariance + final Orbit tabulatedOrbit = tabulatedUncertainState.getFirst(); + final StateCovariance tabulatedCovariance = tabulatedUncertainState.getSecond(); + final Frame interpolatedOrbitFrame = orbitAtInterpolatingTime.getFrame(); + + // Express tabulated covariance in interpolated orbit frame for consistency + final StateCovariance tabulatedCovarianceInOrbitFrame = + tabulatedCovariance.changeCovarianceFrame(tabulatedOrbit, interpolatedOrbitFrame); + + // First convert the covariance matrix to equinoctial elements to avoid singularities inherent to keplerian elements + final RealMatrix covarianceMatrixInEquinoctial = + tabulatedCovarianceInOrbitFrame.changeCovarianceType(tabulatedOrbit, OrbitType.EQUINOCTIAL, + DEFAULT_POSITION_ANGLE).getMatrix(); + + // Compute state error transition matrix in equinoctial elements (identical to the one in keplerian elements) + final RealMatrix stateErrorTransitionMatrixInEquinoctial = + StateCovariance.getStm(tabulatedOrbit, interpolationTime.durationFrom(tabulatedOrbit.getDate())); + + // Propagate the covariance matrix using the previously computed state error transition matrix + final RealMatrix propagatedCovarianceMatrixInEquinoctial = + stateErrorTransitionMatrixInEquinoctial.multiply( + covarianceMatrixInEquinoctial.multiplyTransposed(stateErrorTransitionMatrixInEquinoctial)); + + // Recreate a StateCovariance instance + final StateCovariance propagatedCovarianceInEquinoctial = + new StateCovariance(propagatedCovarianceMatrixInEquinoctial, interpolationTime, + interpolatedOrbitFrame, OrbitType.EQUINOCTIAL, DEFAULT_POSITION_ANGLE); + + // Output propagated state covariance after converting back to cartesian elements + return propagatedCovarianceInEquinoctial.changeCovarianceType(orbitAtInterpolatingTime, + OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE); + } +} diff --git a/src/main/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolator.java b/src/main/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolator.java new file mode 100644 index 0000000000..c3768b9a44 --- /dev/null +++ b/src/main/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolator.java @@ -0,0 +1,522 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.linear.BlockRealMatrix; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.Frame; +import org.orekit.frames.LOFType; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeInterpolator; +import org.orekit.time.TimeStampedPair; +import org.orekit.utils.CartesianDerivativesFilter; + +import java.util.ArrayList; +import java.util.List; + +/** + * State covariance Keplerian quintic interpolator. + *

        + * Its purpose is to interpolate state covariance between tabulated state covariances using polynomial interpolation. To do + * so, it uses a {@link HermiteInterpolator} and compute the first and second order derivatives at tabulated states assuming + * a standard Keplerian motion depending on given derivatives filter. + *

        + * It gives very accurate results as explained here. In the + * very poorly tracked test case evolving in a highly dynamical environment mentioned in the linked thread, the user can + * expect at worst errors of less than 0.2% in position sigmas and less than 0.35% in velocity sigmas with steps of 40mn + * between tabulated values. + *

        + * However, note that this method does not guarantee the positive definiteness of the computed state covariance as opposed to + * {@link StateCovarianceBlender}. + * + * @author Vincent Cucchietti + * @see HermiteInterpolator + * @see StateCovarianceBlender + */ +public class StateCovarianceKeplerianHermiteInterpolator extends AbstractStateCovarianceInterpolator { + + /** + * Filter defining if only the state covariance value are used or if first or/and second Keplerian derivatives should be + * used. + */ + private final CartesianDerivativesFilter filter; + + /** + * Constructor using an output local orbital frame and : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: If the output local orbital frame is not considered pseudo-inertial, all the covariance components + * related to the velocity will be poorly interpolated. Only the position covariance should be considered in this + * case. + * + * @param orbitInterpolator orbit interpolator + * @param outLOF output local orbital frame + */ + public StateCovarianceKeplerianHermiteInterpolator(final TimeInterpolator orbitInterpolator, + final LOFType outLOF) { + this(DEFAULT_INTERPOLATION_POINTS, orbitInterpolator, outLOF); + } + + /** + * Constructor using an output local orbital frame and : + *

          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: If the output local orbital frame is not considered pseudo-inertial, all the covariance components + * related to the velocity will be poorly interpolated. Only the position covariance should be considered in this + * case. + * + * @param interpolationPoints number of interpolation points + * @param orbitInterpolator orbit interpolator + * @param outLOF output local orbital frame + */ + public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, + final TimeInterpolator orbitInterpolator, + final LOFType outLOF) { + this(interpolationPoints, orbitInterpolator, CartesianDerivativesFilter.USE_PVA, + outLOF); + } + + /** + * Constructor using an output local orbital frame and : + *

          + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: If the output local orbital frame is not considered pseudo-inertial, all the covariance components + * related to the velocity will be poorly interpolated. Only the position covariance should be considered in this + * case. + * + * @param interpolationPoints number of interpolation points + * @param orbitInterpolator orbit interpolator + * @param outLOF output local orbital frame + * @param filter filter for derivatives from the sample to use in position-velocity-acceleration interpolation + */ + public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, + final TimeInterpolator orbitInterpolator, + final CartesianDerivativesFilter filter, + final LOFType outLOF) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, orbitInterpolator, filter, outLOF); + } + + /** + * Constructor using an output local orbital frame. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + *

        + * BEWARE: If the output local orbital frame is not considered pseudo-inertial, all the covariance components + * related to the velocity will be poorly interpolated. Only the position covariance should be considered in this + * case. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param orbitInterpolator orbit interpolator + * @param outLOF output local orbital frame + * @param filter filter defining if only the state covariance value are used or if first or/and second Keplerian + * derivatives should be used during the interpolation. + */ + public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final TimeInterpolator orbitInterpolator, + final CartesianDerivativesFilter filter, final LOFType outLOF) { + super(interpolationPoints, extrapolationThreshold, orbitInterpolator, outLOF); + this.filter = filter; + } + + /** + * Constructor using an output frame and : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param orbitInterpolator orbit interpolator + * @param outFrame output frame + * @param outOrbitType output orbit type + * @param outPositionAngleType output position angle + */ + public StateCovarianceKeplerianHermiteInterpolator(final TimeInterpolator orbitInterpolator, final Frame outFrame, + final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) { + this(DEFAULT_INTERPOLATION_POINTS, orbitInterpolator, outFrame, outOrbitType, outPositionAngleType); + } + + /** + * Constructor using an output frame and : + *
          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param orbitInterpolator orbit interpolator + * @param outFrame output frame + * @param outOrbitType output orbit type + * @param outPositionAngleType output position angle + */ + public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, + final TimeInterpolator orbitInterpolator, final Frame outFrame, + final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, orbitInterpolator, CartesianDerivativesFilter.USE_PVA, + outFrame, outOrbitType, outPositionAngleType); + } + + /** + * Constructor using an output frame and : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param orbitInterpolator orbit interpolator + * @param filter filter defining if only the state covariance value are used or if first or/and second Keplerian + * derivatives should be used during the interpolation. + * @param outFrame output frame + * @param outOrbitType output orbit type + * @param outPositionAngleType output position angle + */ + public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, + final TimeInterpolator orbitInterpolator, + final CartesianDerivativesFilter filter, final Frame outFrame, + final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, orbitInterpolator, filter, outFrame, outOrbitType, + outPositionAngleType); + } + + /** + * Constructor using an output frame. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param orbitInterpolator orbit interpolator + * @param filter filter defining if only the state covariance value are used or if first or/and second Keplerian + * derivatives should be used during the interpolation. + * @param outFrame output frame + * @param outOrbitType output orbit type + * @param outPositionAngleType output position angle + */ + public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final TimeInterpolator orbitInterpolator, + final CartesianDerivativesFilter filter, final Frame outFrame, + final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) { + super(interpolationPoints, extrapolationThreshold, orbitInterpolator, outFrame, outOrbitType, outPositionAngleType); + this.filter = filter; + } + + /** Get Filter defining if only the state covariance value are used or if first or/and second Keplerian derivatives + * should be used. + * @return Filter defining if only the state covariance value are used or if first or/and second Keplerian derivatives + * should be used. + */ + public CartesianDerivativesFilter getFilter() { + return filter; + } + + /** {@inheritDoc} */ + @Override + protected StateCovariance computeInterpolatedCovarianceInOrbitFrame( + final List> uncertainStates, + final Orbit interpolatedOrbit) { + + // Compute and combine covariances value and time derivatives + final List covarianceValueAndDerivativesList = new ArrayList<>(); + for (TimeStampedPair uncertainState : uncertainStates) { + final double[][][] currentCovarianceValueAndDerivatives = + computeAndCombineCovarianceValueAndDerivatives(uncertainState, interpolatedOrbit); + + covarianceValueAndDerivativesList.add(currentCovarianceValueAndDerivatives); + } + + // Interpolate covariance matrix in equinoctial elements + final RealMatrix interpolatedCovarianceMatrixInEqui = + computeInterpolatedStateCovariance(interpolatedOrbit.getDate(), + uncertainStates, + covarianceValueAndDerivativesList); + + return new StateCovariance(interpolatedCovarianceMatrixInEqui, + interpolatedOrbit.getDate(), interpolatedOrbit.getFrame(), + OrbitType.EQUINOCTIAL, DEFAULT_POSITION_ANGLE); + } + + /** + * Compute and combine state covariance matrix and its two Keplerian time derivatives. + * + * @param uncertainState orbit and its associated covariance + * @param interpolatedOrbit interpolated orbit + * + * @return state covariance matrix and its two time derivatives + */ + private double[][][] computeAndCombineCovarianceValueAndDerivatives( + final TimeStampedPair uncertainState, final Orbit interpolatedOrbit) { + + // Get orbit and associated covariance + final Orbit orbit = uncertainState.getFirst(); + final StateCovariance covariance = uncertainState.getSecond(); + + // Express covariance in interpolated orbit frame for consistency among the sample + final StateCovariance covarianceInOrbitFrame = covariance.changeCovarianceFrame(orbit, interpolatedOrbit.getFrame()); + + // Convert to equinoctial elements to avoid singularities + final StateCovariance covarianceInOrbitFrameInEqui = + covarianceInOrbitFrame.changeCovarianceType(orbit, OrbitType.EQUINOCTIAL, DEFAULT_POSITION_ANGLE); + + // Get matrix + final RealMatrix covarianceInOrbitFrameInEquiMatrix = covarianceInOrbitFrameInEqui.getMatrix(); + + // Compute covariance first and second time derivative according to instance filter + final int dim = StateCovariance.STATE_DIMENSION; + + final RealMatrix covarianceMatrixFirstDerInKep; + final RealMatrix covarianceMatrixSecondDerInKep; + + switch (filter) { + case USE_P: + covarianceMatrixFirstDerInKep = MatrixUtils.createRealMatrix(dim, dim); + covarianceMatrixSecondDerInKep = MatrixUtils.createRealMatrix(dim, dim); + break; + case USE_PV: + covarianceMatrixFirstDerInKep = computeCovarianceFirstDerivative(orbit, covarianceInOrbitFrameInEquiMatrix); + covarianceMatrixSecondDerInKep = MatrixUtils.createRealMatrix(dim, dim); + break; + case USE_PVA: + covarianceMatrixFirstDerInKep = computeCovarianceFirstDerivative(orbit, covarianceInOrbitFrameInEquiMatrix); + covarianceMatrixSecondDerInKep = + computeCovarianceSecondDerivative(orbit, covarianceInOrbitFrameInEquiMatrix); + break; + default: + // Should never happen + throw new OrekitInternalError(null); + } + + // Combine and output the state covariance and its first two time derivative in a single array + return combineCovarianceValueAndDerivatives(covarianceInOrbitFrameInEquiMatrix, + covarianceMatrixFirstDerInKep, + covarianceMatrixSecondDerInKep); + } + + /** + * Compute interpolated state covariance in equinoctial elements using a Hermite interpolator. + * + * @param interpolationDate interpolation date + * @param uncertainStates list of orbits and their associated covariances + * @param covarianceValueAndDerivativesList list of covariances and their associated first and second time derivatives + * + * @return interpolated state covariance in equinoctial elements + * + * @see HermiteInterpolator + */ + private RealMatrix computeInterpolatedStateCovariance(final AbsoluteDate interpolationDate, + final List> uncertainStates, + final List covarianceValueAndDerivativesList) { + + final RealMatrix interpolatedCovarianceMatrix = new BlockRealMatrix(new double[ROW_DIM][COLUMN_DIM]); + + // Interpolate each element in the covariance matrix + for (int i = 0; i < ROW_DIM; i++) { + for (int j = 0; j < COLUMN_DIM; j++) { + + // Create an interpolator for each element + final HermiteInterpolator tempInterpolator = new HermiteInterpolator(); + + // Fill interpolator with all samples value and associated derivatives + for (int k = 0; k < uncertainStates.size(); k++) { + final TimeStampedPair currentUncertainStates = uncertainStates.get(k); + + final double[][][] currentCovarianceValueAndDerivatives = covarianceValueAndDerivativesList.get(k); + + final double deltaT = currentUncertainStates.getDate().durationFrom(interpolationDate); + + addSampleToInterpolator(tempInterpolator, deltaT, currentCovarianceValueAndDerivatives[i][j]); + } + + // Interpolate + interpolatedCovarianceMatrix.setEntry(i, j, tempInterpolator.value(0)[0]); + } + } + + return interpolatedCovarianceMatrix; + } + + /** + * Add sample to given interpolator. + * + * @param interpolator interpolator to add sample to + * @param deltaT abscissa for interpolation + * @param valueAndDerivatives value and associated derivatives to add + */ + private void addSampleToInterpolator(final HermiteInterpolator interpolator, final double deltaT, + final double[] valueAndDerivatives) { + switch (filter) { + case USE_P: + interpolator.addSamplePoint(deltaT, new double[] { valueAndDerivatives[0] }); + break; + case USE_PV: + interpolator.addSamplePoint(deltaT, + new double[] { valueAndDerivatives[0] }, + new double[] { valueAndDerivatives[1] }); + break; + case USE_PVA: + interpolator.addSamplePoint(deltaT, + new double[] { valueAndDerivatives[0] }, + new double[] { valueAndDerivatives[1] }, + new double[] { valueAndDerivatives[2] }); + break; + default: + // Should never happen + throw new OrekitInternalError(null); + } + } + + /** + * Compute state covariance first Keplerian time derivative. + * + * @param orbit orbit + * @param covarianceMatrixInEqui state covariance matrix in equinoctial elements + * + * @return state covariance first time derivative + */ + private RealMatrix computeCovarianceFirstDerivative(final Orbit orbit, + final RealMatrix covarianceMatrixInEqui) { + + final RealMatrix covarianceFirstDerivative = new BlockRealMatrix(ROW_DIM, COLUMN_DIM); + + // Compute common term used in papers + final double m = orbit.getMeanAnomalyDotWrtA(); + + // Compute first time derivative of each element in the covariance matrix + for (int i = 0; i < ROW_DIM; i++) { + for (int j = 0; j < COLUMN_DIM; j++) { + if (i != 5 && j != 5) { + covarianceFirstDerivative.setEntry(i, j, 0); + } + else if (i == 5 && j != 5) { + + final double value = covarianceMatrixInEqui.getEntry(0, j) * m; + + covarianceFirstDerivative.setEntry(i, j, value); + covarianceFirstDerivative.setEntry(j, i, value); + } + else { + final double value = 2 * covarianceMatrixInEqui.getEntry(0, 5) * m; + + covarianceFirstDerivative.setEntry(i, j, value); + } + } + } + + return covarianceFirstDerivative; + + } + + /** + * Compute state covariance second Keplerian time derivative. + * + * @param orbit orbit + * @param covarianceMatrixInEqui state covariance matrix in equinoctial elements + * + * @return state covariance second time derivative + */ + private RealMatrix computeCovarianceSecondDerivative(final Orbit orbit, + final RealMatrix covarianceMatrixInEqui) { + + final RealMatrix covarianceSecondDerivative = new BlockRealMatrix(ROW_DIM, COLUMN_DIM); + + // Compute common term used in papers + final double m = orbit.getMeanAnomalyDotWrtA(); + + // Compute second time derivative of each element in the covariance matrix + for (int i = 0; i < ROW_DIM; i++) { + for (int j = 0; j < COLUMN_DIM; j++) { + if (i == 5 && j == 5) { + + final double value = 2 * covarianceMatrixInEqui.getEntry(0, 0) * m * m; + + covarianceSecondDerivative.setEntry(i, j, value); + } + else { + covarianceSecondDerivative.setEntry(i, j, 0); + } + } + } + + return covarianceSecondDerivative; + + } + + /** + * Combine state covariance matrix and its two Keplerian time derivatives. + * + * @param covarianceMatrixInEqui covariance matrix in equinoctial elements + * @param covarianceMatrixFirstDerInEqui covariance matrix first time derivative in equinoctial elements + * @param covarianceMatrixSecondDerInEqui covariance matrix second time derivative in equinoctial elements + * + * @return state covariance matrix and its two time derivatives + */ + private double[][][] combineCovarianceValueAndDerivatives(final RealMatrix covarianceMatrixInEqui, + final RealMatrix covarianceMatrixFirstDerInEqui, + final RealMatrix covarianceMatrixSecondDerInEqui) { + + final double[][][] covarianceValueAndDerivativesArray = new double[ROW_DIM][COLUMN_DIM][3]; + + // Combine covariance and its first two time derivatives in a single 3D array + for (int i = 0; i < ROW_DIM; i++) { + for (int j = 0; j < COLUMN_DIM; j++) { + covarianceValueAndDerivativesArray[i][j][0] = covarianceMatrixInEqui.getEntry(i, j); + covarianceValueAndDerivativesArray[i][j][1] = covarianceMatrixFirstDerInEqui.getEntry(i, j); + covarianceValueAndDerivativesArray[i][j][2] = covarianceMatrixSecondDerInEqui.getEntry(i, j); + } + } + + return covarianceValueAndDerivativesArray; + } +} diff --git a/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java b/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java index b0d0b5a52c..966411b5e9 100644 --- a/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java +++ b/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,8 +19,9 @@ import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.orekit.frames.Frame; +import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.time.AbsoluteDate; /** @@ -63,13 +64,13 @@ public class StateCovarianceMatrixProvider implements AdditionalStateProvider { private final OrbitType stmOrbitType; /** Position angle used for State Transition Matrix. */ - private final PositionAngle stmAngleType; + private final PositionAngleType stmAngleType; /** Orbit type for the covariance matrix. */ private final OrbitType covOrbitType; /** Position angle used for the covariance matrix. */ - private final PositionAngle covAngleType; + private final PositionAngleType covAngleType; /** Initial state covariance. */ private StateCovariance covInit; @@ -84,24 +85,19 @@ public class StateCovarianceMatrixProvider implements AdditionalStateProvider { * @param stmName name of the state for State Transition Matrix * @param harvester matrix harvester as returned by * {@code propagator.setupMatricesComputation(stmName, null, null)} - * @param stmOrbitType orbit type used for the State Transition Matrix computation - * @param stmAngleType position angle used for State Transition Matrix computation (not used if stmOrbitType equals - * {@code CARTESIAN}) * @param covInit initial state covariance */ public StateCovarianceMatrixProvider(final String additionalName, final String stmName, - final MatricesHarvester harvester, - final OrbitType stmOrbitType, final PositionAngle stmAngleType, - final StateCovariance covInit) { + final MatricesHarvester harvester, final StateCovariance covInit) { // Initialize fields this.additionalName = additionalName; this.stmName = stmName; this.harvester = harvester; this.covInit = covInit; this.covOrbitType = covInit.getOrbitType(); - this.covAngleType = covInit.getPositionAngle(); - this.stmOrbitType = stmOrbitType; - this.stmAngleType = stmAngleType; + this.covAngleType = covInit.getPositionAngleType(); + this.stmOrbitType = harvester.getOrbitType(); + this.stmAngleType = harvester.getPositionAngleType(); } /** {@inheritDoc} */ @@ -115,7 +111,12 @@ public String getName() { public void init(final SpacecraftState initialState, final AbsoluteDate target) { // Convert the initial state covariance in the same orbit type as the STM covInit = covInit.changeCovarianceType(initialState.getOrbit(), stmOrbitType, stmAngleType); - covMatrixInit = covInit.getMatrix(); + + // Express covariance matrix in the same frame as the STM + final Orbit initialOrbit = initialState.getOrbit(); + final StateCovariance covInitInSTMFrame = covInit.changeCovarianceFrame(initialOrbit, initialOrbit.getFrame()); + + covMatrixInit = covInitInSTMFrame.getMatrix(); } /** @@ -125,7 +126,7 @@ public void init(final SpacecraftState initialState, final AbsoluteDate target) *

        */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !state.hasAdditionalState(stmName); } @@ -158,20 +159,30 @@ public OrbitType getCovarianceOrbitType() { } /** - * Get the state covariance. + * Get the state covariance in the same frame/local orbital frame, orbit type and position angle as the initial + * covariance. * * @param state spacecraft state to which the covariance matrix should correspond * @return the state covariance * @see #getStateCovariance(SpacecraftState, Frame) - * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngle) + * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngleType) */ public StateCovariance getStateCovariance(final SpacecraftState state) { // Get the current propagated covariance final RealMatrix covarianceMatrix = toRealMatrix(state.getAdditionalState(additionalName)); - // Return the state covariance - return new StateCovariance(covarianceMatrix, state.getDate(), state.getFrame(), covOrbitType, covAngleType); + // Create associated state covariance + final StateCovariance covariance = + new StateCovariance(covarianceMatrix, state.getDate(), state.getFrame(), covOrbitType, covAngleType); + + // Return the state covariance in same frame/lof as initial covariance + if (covInit.getLOF() == null) { + return covariance; + } + else { + return covariance.changeCovarianceFrame(state.getOrbit(), covInit.getLOF()); + } } @@ -184,7 +195,7 @@ public StateCovariance getStateCovariance(final SpacecraftState state) { * @param frame output frame for which the output covariance matrix must be expressed (must be inertial) * @return the state covariance expressed in frame * @see #getStateCovariance(SpacecraftState) - * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngle) + * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngleType) */ public StateCovariance getStateCovariance(final SpacecraftState state, final Frame frame) { // Return the converted covariance @@ -202,7 +213,7 @@ public StateCovariance getStateCovariance(final SpacecraftState state, final Fra * @see #getStateCovariance(SpacecraftState, Frame) */ public StateCovariance getStateCovariance(final SpacecraftState state, final OrbitType orbitType, - final PositionAngle angleType) { + final PositionAngleType angleType) { // Return the converted covariance return getStateCovariance(state).changeCovarianceType(state.getOrbit(), orbitType, angleType); } diff --git a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalGradientConverter.java index 73d18ff3f6..225b5428d6 100644 --- a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -32,6 +32,7 @@ import org.orekit.utils.FieldAngularCoordinates; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; import org.orekit.utils.TimeStampedFieldAngularCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; @@ -41,7 +42,7 @@ * @author Bryan Cazabonne * @since 11.1 */ -public abstract class AbstractAnalyticalGradientConverter extends AbstractGradientConverter { +public abstract class AbstractAnalyticalGradientConverter extends AbstractGradientConverter implements ParameterDriversProvider { /** Attitude provider. */ private final AttitudeProvider provider; @@ -67,7 +68,7 @@ protected AbstractAnalyticalGradientConverter(final AbstractAnalyticalPropagator final SpacecraftState state = propagator.getInitialState(); // Position always has derivatives - final Vector3D pos = state.getPVCoordinates().getPosition(); + final Vector3D pos = state.getPosition(); final FieldVector3D posG = new FieldVector3D<>(Gradient.variable(freeStateParameters, 0, pos.getX()), Gradient.variable(freeStateParameters, 1, pos.getY()), Gradient.variable(freeStateParameters, 2, pos.getZ())); @@ -151,37 +152,12 @@ public FieldSpacecraftState getState() { } return gStates.get(nbParams); - } - /** Get the model parameters. - * @param state state as returned by {@link #getState()} - * @return the model parameters - */ - public Gradient[] getParameters(final FieldSpacecraftState state) { - final int freeParameters = state.getMass().getFreeParameters(); - final List drivers = getParametersDrivers(); - final Gradient[] parameters = new Gradient[drivers.size()]; - int index = getFreeStateParameters(); - int i = 0; - for (ParameterDriver driver : drivers) { - parameters[i++] = driver.isSelected() ? - Gradient.variable(freeParameters, index++, driver.getValue()) : - Gradient.constant(freeParameters, driver.getValue()); - } - return parameters; - } - - /** - * Get the parameter drivers related to the analytical propagation model. - * @return a list of parameter drivers - */ - public abstract List getParametersDrivers(); - /** * Get the converted analytical orbit propagator. * @param state state as returned by {@link #getState()} - * @param parameters model parameters as returned by {@link #getParameters(FieldSpacecraftState)} + * @param parameters model parameters * @return the converted analytical orbit propagator */ public abstract FieldAbstractAnalyticalPropagator getPropagator(FieldSpacecraftState state, diff --git a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java index da72be72c0..6b92b52779 100644 --- a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java +++ b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,6 +23,8 @@ import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.AdditionalStateProvider; import org.orekit.propagation.FieldSpacecraftState; @@ -32,6 +34,8 @@ import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** * Base class harvester between two-dimensional Jacobian @@ -61,7 +65,7 @@ public abstract class AbstractAnalyticalMatricesHarvester extends AbstractMatric *

        * The arguments for initial matrices must be compatible with the * {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name @@ -160,7 +164,7 @@ public void setReferenceState(final SpacecraftState reference) { final AbstractAnalyticalGradientConverter converter = getGradientConverter(); final FieldSpacecraftState gState = converter.getState(); - final Gradient[] gParameters = converter.getParameters(gState); + final Gradient[] gParameters = converter.getParameters(gState, converter); final FieldAbstractAnalyticalPropagator gPropagator = converter.getPropagator(gState, gParameters); // Compute Jacobian @@ -190,21 +194,24 @@ public void setReferenceState(final SpacecraftState reference) { for (ParameterDriver driver : converter.getParametersDrivers()) { if (driver.isSelected()) { - // get the partials derivatives for this driver - DoubleArrayDictionary.Entry entry = analyticalDerivativesJacobianColumns.getEntry(driver.getName()); - if (entry == null) { - // create an entry filled with zeroes - analyticalDerivativesJacobianColumns.put(driver.getName(), new double[STATE_DIMENSION]); - entry = analyticalDerivativesJacobianColumns.getEntry(driver.getName()); + final TimeSpanMap driverNameSpanMap = driver.getNamesSpanMap(); + // for each span (for each estimated value) corresponding name is added + for (Span span = driverNameSpanMap.getFirstSpan(); span != null; span = span.next()) { + // get the partials derivatives for this driver + DoubleArrayDictionary.Entry entry = analyticalDerivativesJacobianColumns.getEntry(span.getData()); + if (entry == null) { + // create an entry filled with zeroes + analyticalDerivativesJacobianColumns.put(span.getData(), new double[STATE_DIMENSION]); + entry = analyticalDerivativesJacobianColumns.getEntry(span.getData()); + } + + // add the contribution of the current force model + entry.increment(new double[] { + derivativesX[paramsIndex], derivativesY[paramsIndex], derivativesZ[paramsIndex], + derivativesVx[paramsIndex], derivativesVy[paramsIndex], derivativesVz[paramsIndex] + }); + ++paramsIndex; } - - // add the contribution of the current force model - entry.increment(new double[] { - derivativesX[paramsIndex], derivativesY[paramsIndex], derivativesZ[paramsIndex], - derivativesVx[paramsIndex], derivativesVy[paramsIndex], derivativesVz[paramsIndex] - }); - ++paramsIndex; - } } @@ -263,6 +270,20 @@ private double[] toArray(final double[][] matrix) { return array; } + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + // Set to CARTESIAN because analytical gradient converter uses cartesian representation + return OrbitType.CARTESIAN; + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + // Irrelevant: set a default value + return PositionAngleType.MEAN; + } + /** * Get the gradient converter related to the analytical orbit propagator. * @return the gradient converter diff --git a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java index 01a879606c..f28800728f 100644 --- a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,7 +29,9 @@ import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitInternalError; +import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.propagation.AbstractPropagator; @@ -116,8 +118,8 @@ public void clearEventsDetectors() { /** {@inheritDoc} */ public SpacecraftState propagate(final AbsoluteDate start, final AbsoluteDate target) { + checkStartDateIsNotInfinity(start); try { - initializePropagation(); lastPropagationStart = start; @@ -166,6 +168,16 @@ public SpacecraftState propagate(final AbsoluteDate start, final AbsoluteDate ta } } + /** + * Check the starting date is not {@code AbsoluteDate.PAST_INFINITY} or {@code AbsoluteDate.FUTURE_INFINITY}. + * @param start propagation starting date + */ + private void checkStartDateIsNotInfinity(final AbsoluteDate start) { + if (start.isEqualTo(AbsoluteDate.PAST_INFINITY) || start.isEqualTo(AbsoluteDate.FUTURE_INFINITY)) { + throw new OrekitIllegalArgumentException(OrekitMessages.CANNOT_START_PROPAGATION_FROM_INFINITY); + } + } + /** Accept a step, triggering events and step handlers. * @param interpolator interpolator for the current step * @param target final propagation time diff --git a/src/main/java/org/orekit/propagation/analytical/AdapterPropagator.java b/src/main/java/org/orekit/propagation/analytical/AdapterPropagator.java index 2c4cacb064..644bbad597 100644 --- a/src/main/java/org/orekit/propagation/analytical/AdapterPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/AdapterPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -84,6 +84,8 @@ public AdapterPropagator(final Propagator reference) { super(reference.getAttitudeProvider()); this.reference = reference; this.effects = new ArrayList(); + // Sets initial state + super.resetInitialState(getInitialState()); } /** Add a differential effect. diff --git a/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java b/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java index c57157cb37..f351aa81f2 100644 --- a/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java @@ -17,13 +17,14 @@ package org.orekit.propagation.analytical; import java.util.Collection; +import java.util.Collections; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.TreeMap; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; @@ -46,7 +47,7 @@ public class AggregateBoundedPropagator extends AbstractAnalyticalPropagator implements BoundedPropagator { /** Constituent propagators. */ - private final NavigableMap propagators; + private final NavigableMap propagators; /** Minimum date for {@link #getMinDate()}. */ private final AbsoluteDate min; /** Maximum date for {@link #getMaxDate()}. */ @@ -93,7 +94,7 @@ public AggregateBoundedPropagator( * @param max the value for {@link #getMaxDate()}. */ public AggregateBoundedPropagator( - final NavigableMap propagators, + final NavigableMap propagators, final AbsoluteDate min, final AbsoluteDate max) { super(defaultAttitude(propagators.values())); @@ -116,7 +117,19 @@ private static AttitudeProvider defaultAttitude( if (propagators.isEmpty()) { throw new OrekitException(OrekitMessages.NOT_ENOUGH_PROPAGATORS); } - return new InertialProvider(propagators.iterator().next().getFrame()); + return new FrameAlignedProvider(propagators.iterator().next().getFrame()); + } + + /** Get an unmodifiable view of the propagators map. + *

        + * The key of the map entries are the {@link BoundedPropagator#getMinDate() min dates} + * of each propagator. + *

        + * @return unmodifiable view of the propagators map + * @since 12.0 + */ + public NavigableMap getPropagators() { + return Collections.unmodifiableNavigableMap(propagators); } @Override @@ -191,8 +204,8 @@ public void resetInitialState(final SpacecraftState state) { * @param date of query * @return propagator to use on date. */ - private Propagator getPropagator(final AbsoluteDate date) { - final Entry entry = + private BoundedPropagator getPropagator(final AbsoluteDate date) { + final Entry entry = propagators.floorEntry(date); if (entry != null) { return entry.getValue(); diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java index 28811f08d2..e36c18d061 100644 --- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -63,8 +63,8 @@ public FieldBrouwerLyddanePropagator getPropagator(final FieldSpacecra final Gradient mu = zero.add(propagator.getMu()); // Return the "Field" propagator - return new FieldBrouwerLyddanePropagator(state.getOrbit(), provider, radius, mu, - ck0[2], ck0[3], ck0[4], ck0[5], parameters[0].getValue()); + return new FieldBrouwerLyddanePropagator<>(state.getOrbit(), provider, radius, mu, + ck0[2], ck0[3], ck0[4], ck0[5], parameters[0].getValue()); } diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneHarvester.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneHarvester.java index dee5524af0..e86482ac7f 100644 --- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneHarvester.java +++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.propagation.analytical; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.PositionAngleType; import org.orekit.utils.DoubleArrayDictionary; /** @@ -35,7 +36,7 @@ class BrouwerLyddaneHarvester extends AbstractAnalyticalMatricesHarvester { *

        * The arguments for initial matrices must be compatible with the * {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java index a2a0ca7082..5075f72f70 100644 --- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,7 @@ import org.hipparchus.util.Precision; import org.hipparchus.util.SinCos; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; @@ -36,7 +36,7 @@ import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.MatricesHarvester; import org.orekit.propagation.PropagationType; @@ -45,7 +45,9 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** * This class propagates a {@link org.orekit.propagation.SpacecraftState} @@ -60,7 +62,7 @@ * However, for low Earth orbits, the magnitude of the perturbative acceleration due to * atmospheric drag can be significant. Warren Phipps' 1992 thesis considered the atmospheric * drag by time derivatives of the mean mean anomaly using the catch-all coefficient - * {@link #M2Driver}. + * {@link #M2Driver}. Beware that M2Driver must have only 1 span on its TimeSpanMap value. * * Usually, M2 is adjusted during an orbit determination process and it represents the * combination of all unmodeled secular along-track effects (i.e. not just the atmospheric drag). @@ -87,7 +89,7 @@ * @author Bryan Cazabonne * @since 11.1 */ -public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator { +public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator implements ParameterDriversProvider { /** Parameter name for M2 coefficient. */ public static final String M2_NAME = "M2"; @@ -145,7 +147,7 @@ public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator { public BrouwerLyddanePropagator(final Orbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()), M2); } @@ -209,8 +211,8 @@ public BrouwerLyddanePropagator(final Orbit initialOrbit, final double referenceRadius, final double mu, final double c20, final double c30, final double c40, final double c50, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, M2); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, M2); } /** Build a propagator from orbit, mass and potential provider. @@ -228,7 +230,7 @@ public BrouwerLyddanePropagator(final Orbit initialOrbit, public BrouwerLyddanePropagator(final Orbit initialOrbit, final double mass, final UnnormalizedSphericalHarmonicsProvider provider, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), mass, provider, provider.onDate(initialOrbit.getDate()), M2); } @@ -263,7 +265,7 @@ public BrouwerLyddanePropagator(final Orbit initialOrbit, final double mass, final double referenceRadius, final double mu, final double c20, final double c30, final double c40, final double c50, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), mass, referenceRadius, mu, c20, c30, c40, c50, M2); } @@ -388,7 +390,7 @@ public BrouwerLyddanePropagator(final Orbit initialOrbit, public BrouwerLyddanePropagator(final Orbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final PropagationType initialType, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()), initialType, M2); } @@ -639,7 +641,7 @@ public static KeplerianOrbit computeMeanOrbit(final Orbit osculating, final double epsilon, final int maxIterations) { final BrouwerLyddanePropagator propagator = new BrouwerLyddanePropagator(osculating, - InertialProvider.of(osculating.getFrame()), + FrameAlignedProvider.of(osculating.getFrame()), DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, PropagationType.OSCULATING, M2Value, @@ -759,7 +761,7 @@ private BLModel computeMeanParameters(final KeplerianOrbit osculating, final dou current.mean.getPerigeeArgument() + deltaOmega, current.mean.getRightAscensionOfAscendingNode() + deltaRAAN, current.mean.getMeanAnomaly() + deltaAnom, - PositionAngle.MEAN, + PositionAngleType.MEAN, current.mean.getFrame(), current.mean.getDate(), mu), mass, referenceRadius, mu, ck0); @@ -785,10 +787,14 @@ public KeplerianOrbit propagateOrbit(final AbsoluteDate date) { } /** - * Get the value of the M2 drag parameter. + * Get the value of the M2 drag parameter. Beware that M2Driver + * must have only 1 span on its TimeSpanMap value (that is + * to say setPeriod method should not be called) * @return the value of the M2 drag parameter */ public double getM2() { + // As Brouwer Lyddane is an analytical propagator, for now it is not possible for + // M2Driver to have several values estimated return M2Driver.getValue(); } @@ -843,8 +849,10 @@ protected AbstractMatricesHarvester createHarvester(final String stmName, final protected List getJacobiansColumnsNames() { final List columnsNames = new ArrayList<>(); for (final ParameterDriver driver : getParametersDrivers()) { - if (driver.isSelected() && !columnsNames.contains(driver.getName())) { - columnsNames.add(driver.getName()); + if (driver.isSelected() && !columnsNames.contains(driver.getNamesSpanMap().getFirstSpan().getData())) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + columnsNames.add(span.getData()); + } } } Collections.sort(columnsNames); @@ -1427,7 +1435,7 @@ public KeplerianOrbit propagateParameters(final AbsoluteDate date) { g.getValue(), h.getValue(), l.getValue(), a.getFirstDerivative(), e.getFirstDerivative(), i.getFirstDerivative(), g.getFirstDerivative(), h.getFirstDerivative(), l.getFirstDerivative(), - PositionAngle.MEAN, mean.getFrame(), date, mu); + PositionAngleType.MEAN, mean.getFrame(), date, mu); } diff --git a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java index fa1ad8078e..891ed4f077 100644 --- a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -64,8 +64,8 @@ public FieldEcksteinHechlerPropagator getPropagator(final FieldSpacecr final Gradient mu = zero.add(propagator.getMu()); // Return the "Field" propagator - return new FieldEcksteinHechlerPropagator(state.getOrbit(), provider, radius, mu, - ck0[2], ck0[3], ck0[4], ck0[5], ck0[6]); + return new FieldEcksteinHechlerPropagator<>(state.getOrbit(), provider, radius, mu, + ck0[2], ck0[3], ck0[4], ck0[5], ck0[6]); } diff --git a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerHarvester.java b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerHarvester.java index cb5024ab29..7161796574 100644 --- a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerHarvester.java +++ b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.propagation.analytical; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.PositionAngleType; import org.orekit.utils.DoubleArrayDictionary; /** @@ -35,7 +36,7 @@ class EcksteinHechlerHarvester extends AbstractAnalyticalMatricesHarvester { *

        * The arguments for initial matrices must be compatible with the * {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name diff --git a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java index 8f7cf590a2..b8783e3ba1 100644 --- a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,7 +26,7 @@ import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; @@ -35,7 +35,7 @@ import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; @@ -117,8 +117,8 @@ public class EcksteinHechlerPropagator extends AbstractAnalyticalPropagator { */ public EcksteinHechlerPropagator(final Orbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate())); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate())); } /** @@ -177,8 +177,8 @@ public EcksteinHechlerPropagator(final Orbit initialOrbit, final double referenceRadius, final double mu, final double c20, final double c30, final double c40, final double c50, final double c60) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60); } /** Build a propagator from orbit, mass and potential provider. @@ -194,8 +194,8 @@ public EcksteinHechlerPropagator(final Orbit initialOrbit, */ public EcksteinHechlerPropagator(final Orbit initialOrbit, final double mass, final UnnormalizedSphericalHarmonicsProvider provider) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - mass, provider, provider.onDate(initialOrbit.getDate())); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + mass, provider, provider.onDate(initialOrbit.getDate())); } /** Build a propagator from orbit, mass and potential. @@ -228,8 +228,8 @@ public EcksteinHechlerPropagator(final Orbit initialOrbit, final double mass, final double referenceRadius, final double mu, final double c20, final double c30, final double c40, final double c50, final double c60) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - mass, referenceRadius, mu, c20, c30, c40, c50, c60); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + mass, referenceRadius, mu, c20, c30, c40, c50, c60); } /** Build a propagator from orbit, attitude provider and potential provider. @@ -344,7 +344,7 @@ public EcksteinHechlerPropagator(final Orbit initialOrbit, public EcksteinHechlerPropagator(final Orbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final PropagationType initialType) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()), initialType); } @@ -581,7 +581,7 @@ public static CircularOrbit computeMeanOrbit(final Orbit osculating, final double epsilon, final int maxIterations) { final EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(osculating, - InertialProvider.of(osculating.getFrame()), + FrameAlignedProvider.of(osculating.getFrame()), DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60, PropagationType.OSCULATING, epsilon, maxIterations); @@ -696,7 +696,7 @@ private EHModel computeMeanParameters(final CircularOrbit osculating, final doub current.mean.getI() + deltaI, current.mean.getRightAscensionOfAscendingNode() + deltaRAAN, current.mean.getAlphaM() + deltaAlphaM, - PositionAngle.MEAN, + PositionAngleType.MEAN, current.mean.getFrame(), current.mean.getDate(), mu), mass, referenceRadius, mu, ck0); diff --git a/src/main/java/org/orekit/propagation/analytical/Ephemeris.java b/src/main/java/org/orekit/propagation/analytical/Ephemeris.java index 31a182749c..5c5b922241 100644 --- a/src/main/java/org/orekit/propagation/analytical/Ephemeris.java +++ b/src/main/java/org/orekit/propagation/analytical/Ephemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,131 +16,197 @@ */ package org.orekit.propagation.analytical; -import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; - import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.linear.RealMatrix; -import org.hipparchus.util.FastMath; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.SpacecraftStateInterpolator; +import org.orekit.propagation.StateCovariance; import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; +import org.orekit.time.TimeInterpolator; +import org.orekit.time.TimeStampedPair; import org.orekit.utils.DoubleArrayDictionary; -import org.orekit.utils.ImmutableTimeStampedCache; -import org.orekit.utils.PVCoordinatesProvider; -import org.orekit.utils.TimeStampedPVCoordinates; -/** This class is designed to accept and handle tabulated orbital entries. - * Tabulated entries are classified and then extrapolated in way to obtain - * continuous output, with accuracy and computation methods configured by the user. +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * This class is designed to accept and handle tabulated orbital entries. Tabulated entries are classified and then + * extrapolated in way to obtain continuous output, with accuracy and computation methods configured by the user. * * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @author Luc Maisonobe + * @author Vincent Cucchietti */ public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPropagator { - /** Default extrapolation time threshold: 1ms. - * @since 9.0 - **/ - public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3; - - /** First date in range. */ + /** First date in range. */ private final AbsoluteDate minDate; /** Last date in range. */ private final AbsoluteDate maxDate; - /** The extrapolation threshold beyond which the propagation will fail. **/ - private final double extrapolationThreshold; - /** Reference frame. */ private final Frame frame; /** Names of the additional states. */ private final String[] additional; - /** Local PV Provider used for computing attitude. **/ - private LocalPVProvider pvProvider; + /** List of spacecraft states. */ + private final transient List states; - /** Thread-safe cache. */ - private final transient ImmutableTimeStampedCache cache; + /** List of covariances. **/ + private final transient List covariances; - /** Constructor with tabulated states. + /** Spacecraft state interpolator. */ + private final transient TimeInterpolator stateInterpolator; + + /** State covariance interpolator. */ + private final transient TimeInterpolator> covarianceInterpolator; + + /** Flag defining if states are defined using an orbit or an absolute position-velocity-acceleration. */ + private final transient boolean statesAreOrbitDefined; + + /** + * Legacy constructor with tabulated states and default Hermite interpolation. *

        - * This constructor allows extrapolating outside of the states time span - * by up to the 1ms {@link #DEFAULT_EXTRAPOLATION_THRESHOLD_SEC default - * extrapolation threshold}. - *

        + * As this implementation of interpolation is polynomial, it should be used only with small samples (about 10-20 points) + * in order to avoid Runge's phenomenon and numerical + * problems (including NaN appearing). * - * @param states tabulates states + * @param states list of spacecraft states * @param interpolationPoints number of points to use in interpolation - * @exception MathIllegalArgumentException if the number of states is smaller than - * the number of points to use in interpolation - * @see #Ephemeris(List, int, double) - * @see #Ephemeris(List, int, double, AttitudeProvider) + * + * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in + * interpolation + * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute + * position-velocity-acceleration) + * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator) + * @see SpacecraftStateInterpolator */ public Ephemeris(final List states, final int interpolationPoints) - throws MathIllegalArgumentException { - this(states, interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + throws MathIllegalArgumentException { + // If states is empty an exception will be thrown in the other constructor + this(states, states.isEmpty() ? null : new SpacecraftStateInterpolator(interpolationPoints, + states.get(0).getFrame(), + states.get(0).getFrame()), + new ArrayList<>(), null); } - /** Constructor with tabulated states. + /** + * Constructor with tabulated states. * - * @param states tabulates states - * @param interpolationPoints number of points to use in interpolation - * @param extrapolationThreshold the largest time difference in seconds between - * the start or stop boundary of the ephemeris bounds to be doing extrapolation - * @exception MathIllegalArgumentException if the number of states is smaller than - * the number of points to use in interpolation + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * + * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in + * interpolation + * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute + * position-velocity-acceleration) + * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator) + */ + public Ephemeris(final List states, final TimeInterpolator stateInterpolator) + throws MathIllegalArgumentException { + this(states, stateInterpolator, new ArrayList<>(), null); + } + + /** + * Constructor with tabulated states. + * + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * @param attitudeProvider attitude law to use, null by default + * + * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in + * interpolation + * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute + * position-velocity-acceleration) + * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator) + */ + public Ephemeris(final List states, final TimeInterpolator stateInterpolator, + final AttitudeProvider attitudeProvider) + throws MathIllegalArgumentException { + this(states, stateInterpolator, new ArrayList<>(), null, attitudeProvider); + } + + /** + * Constructor with tabulated states and associated covariances. + * + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * @param covariances tabulated covariances associated to tabulated states ephemeris bounds to be doing extrapolation + * @param covarianceInterpolator covariance interpolator + * + * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in + * interpolation + * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute + * position-velocity-acceleration) + * @throws OrekitIllegalArgumentException if number of states is different from the number of covariances + * @throws OrekitIllegalStateException if dates between states and associated covariances are different + * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator, AttitudeProvider) * @since 9.0 - * @see #Ephemeris(List, int, double, AttitudeProvider) */ - public Ephemeris(final List states, final int interpolationPoints, - final double extrapolationThreshold) - throws MathIllegalArgumentException { - this(states, interpolationPoints, extrapolationThreshold, - // if states is empty an exception will be thrown in the other constructor - states.isEmpty() ? null : InertialProvider.of(states.get(0).getFrame())); + public Ephemeris(final List states, + final TimeInterpolator stateInterpolator, + final List covariances, + final TimeInterpolator> covarianceInterpolator) + throws MathIllegalArgumentException { + this(states, stateInterpolator, covariances, covarianceInterpolator, + // if states is empty an exception will be thrown in the other constructor + states.isEmpty() ? null : FrameAlignedProvider.of(states.get(0).getFrame())); } - /** Constructor with tabulated states. - * @param states tabulates states - * @param interpolationPoints number of points to use in interpolation - * @param extrapolationThreshold the largest time difference in seconds between - * the start or stop boundary of the ephemeris bounds to be doing extrapolation - * @param attitudeProvider attitude law to use. - * @exception MathIllegalArgumentException if the number of states is smaller than - * the number of points to use in interpolation + /** + * Constructor with tabulated states and associated covariances. + *

        + * The user is expected to explicitly define an attitude provider if they want to use one. Otherwise, it is null by + * default + * + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * @param covariances tabulated covariances associated to tabulated states + * @param covarianceInterpolator covariance interpolator + * @param attitudeProvider attitude law to use, null by default + * + * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in + * interpolation + * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute + * position-velocity-acceleration) + * @throws OrekitIllegalArgumentException if number of states is different from the number of covariances + * @throws OrekitIllegalStateException if dates between states and associated covariances are different * @since 10.1 */ public Ephemeris(final List states, - final int interpolationPoints, - final double extrapolationThreshold, + final TimeInterpolator stateInterpolator, + final List covariances, + final TimeInterpolator> covarianceInterpolator, final AttitudeProvider attitudeProvider) - throws MathIllegalArgumentException { + throws MathIllegalArgumentException { super(attitudeProvider); - if (states.size() < interpolationPoints) { - throw new MathIllegalArgumentException(LocalizedCoreFormats.INSUFFICIENT_DIMENSION, - states.size(), interpolationPoints); - } + // Check input consistency + checkInputConsistency(states, stateInterpolator, covariances, covarianceInterpolator); + // Initialize variables final SpacecraftState s0 = states.get(0); minDate = s0.getDate(); maxDate = states.get(states.size() - 1).getDate(); - frame = s0.getFrame(); + frame = s0.getFrame(); final List as = s0.getAdditionalStatesValues().getData(); additional = new String[as.size()]; @@ -148,83 +214,170 @@ public Ephemeris(final List states, additional[i] = as.get(i).getKey(); } - // check all states handle the same additional states - for (final SpacecraftState state : states) { - s0.ensureCompatibleAdditionalStates(state); - } + this.states = states; + this.stateInterpolator = stateInterpolator; + + this.covariances = covariances; + this.covarianceInterpolator = covarianceInterpolator; - pvProvider = new LocalPVProvider(states, interpolationPoints, extrapolationThreshold); + this.statesAreOrbitDefined = s0.isOrbitDefined(); - // user needs to explicitly set attitude provider if they want to use one - setAttitudeProvider(null); + // Initialize initial state + super.resetInitialState(getInitialState()); + } + + /** + * Check input consistency between states, covariances and their associated interpolators. + * + * @param states spacecraft states sample + * @param stateInterpolator spacecraft state interpolator + * @param covariances covariances sample + * @param covarianceInterpolator covariance interpolator + */ + public static void checkInputConsistency(final List states, + final TimeInterpolator stateInterpolator, + final List covariances, + final TimeInterpolator> covarianceInterpolator) { + // Checks to perform is states are provided + if (!states.isEmpty()) { + // Check given that given states definition are consistent + // (all defined by either orbits or absolute position-velocity-acceleration coordinates) + SpacecraftStateInterpolator.checkStatesDefinitionsConsistency(states); + + // Check that every interpolator used in the state interpolator are compatible with the sample size + AbstractTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(stateInterpolator, states.size()); + + // Additional checks if covariances are provided + if (!covariances.isEmpty()) { + // Check that every interpolator used in the state covariance interpolator are compatible with the sample size + AbstractTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(covarianceInterpolator, + covariances.size()); + // Check states and covariances consistency + checkStatesAndCovariancesConsistency(states, covariances); + } + } + else { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0); + } + } - // set up cache - cache = new ImmutableTimeStampedCache(interpolationPoints, states); + /** + * Check that given states and covariances are consistent. + * + * @param states tabulates states to check + * @param covariances tabulated covariances associated to tabulated states to check + */ + public static void checkStatesAndCovariancesConsistency(final List states, + final List covariances) { + final int nbStates = states.size(); + + // Check that we have an equal number of states and covariances + if (nbStates != covariances.size()) { + throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH, + states.size(), + covariances.size()); + } - this.extrapolationThreshold = extrapolationThreshold; + // Check that states and covariance are defined at the same date + for (int i = 0; i < nbStates; i++) { + if (!states.get(i).getDate().isCloseTo(covariances.get(i).getDate(), + TimeStampedPair.DEFAULT_DATE_EQUALITY_THRESHOLD)) { + throw new OrekitIllegalStateException(OrekitMessages.STATE_AND_COVARIANCE_DATES_MISMATCH, + states.get(i).getDate(), covariances.get(i).getDate()); + } + } } - /** Get the first date of the range. + /** + * Get the first date of the range. + * * @return the first date of the range */ public AbsoluteDate getMinDate() { return minDate; } - /** Get the last date of the range. + /** + * Get the last date of the range. + * * @return the last date of the range */ public AbsoluteDate getMaxDate() { return maxDate; } - /** Get the maximum timespan outside of the stored ephemeris that is allowed - * for extrapolation. - * @return the extrapolation threshold in seconds - */ - public double getExtrapolationThreshold() { - return extrapolationThreshold; - } - + /** {@inheritDoc} */ @Override public Frame getFrame() { return frame; } - @Override + /** + * Get the covariance at given date. + *

        + * BEWARE : If this instance has been created without sample of covariances and/or with spacecraft states defined with + * absolute position-velocity-acceleration, it will return an empty {@link Optional}. + * + * @param date date at which the covariance is desired + * + * @return covariance at given date + * + * @see Optional + */ + public Optional getCovariance(final AbsoluteDate date) { + if (covarianceInterpolator != null && statesAreOrbitDefined) { + + // Build list of time stamped pair of orbits and their associated covariances + final List> sample = buildOrbitAndCovarianceSample(); + + // Interpolate + final TimeStampedPair interpolatedOrbitAndCovariance = + covarianceInterpolator.interpolate(date, sample); + + return Optional.of(interpolatedOrbitAndCovariance.getSecond()); + } + else { + return Optional.empty(); + } + + } + + /** @return sample of orbits and their associated covariances */ + private List> buildOrbitAndCovarianceSample() { + final List> sample = new ArrayList<>(); + + for (int i = 0; i < states.size(); i++) { + sample.add(new TimeStampedPair<>(states.get(i).getOrbit(), covariances.get(i))); + } + + return sample; + } + /** {@inheritDoc} */ + @Override public SpacecraftState basicPropagate(final AbsoluteDate date) { - final SpacecraftState evaluatedState; - - final AbsoluteDate central; - if (date.compareTo(minDate) < 0 && FastMath.abs(date.durationFrom(minDate)) <= extrapolationThreshold) { - // avoid TimeStampedCacheException as we are still within the tolerance before minDate - central = minDate; - } else if (date.compareTo(maxDate) > 0 && FastMath.abs(date.durationFrom(maxDate)) <= extrapolationThreshold) { - // avoid TimeStampedCacheException as we are still within the tolerance after maxDate - central = maxDate; - } else { - central = date; - } - final List neighbors = cache.getNeighbors(central).collect(Collectors.toList()); - evaluatedState = neighbors.get(0).interpolate(date, neighbors); + final SpacecraftState evaluatedState = stateInterpolator.interpolate(date, states); final AttitudeProvider attitudeProvider = getAttitudeProvider(); - if (attitudeProvider == null) { return evaluatedState; - } else { - pvProvider.setCurrentState(evaluatedState); - final Attitude calculatedAttitude = attitudeProvider.getAttitude(pvProvider, date, - evaluatedState.getFrame()); - + } + else { + final Attitude calculatedAttitude; // Verify if orbit is defined if (evaluatedState.isOrbitDefined()) { + calculatedAttitude = + attitudeProvider.getAttitude(evaluatedState.getOrbit(), date, evaluatedState.getFrame()); return new SpacecraftState(evaluatedState.getOrbit(), calculatedAttitude, evaluatedState.getMass(), - evaluatedState.getAdditionalStatesValues(), evaluatedState.getAdditionalStatesDerivatives()); - } else { + evaluatedState.getAdditionalStatesValues(), + evaluatedState.getAdditionalStatesDerivatives()); + } + else { + calculatedAttitude = + attitudeProvider.getAttitude(evaluatedState.getAbsPVA(), date, evaluatedState.getFrame()); return new SpacecraftState(evaluatedState.getAbsPVA(), calculatedAttitude, evaluatedState.getMass(), - evaluatedState.getAdditionalStatesValues(), evaluatedState.getAdditionalStatesDerivatives()); + evaluatedState.getAdditionalStatesValues(), + evaluatedState.getAdditionalStatesDerivatives()); } } @@ -240,15 +393,12 @@ protected double getMass(final AbsoluteDate date) { return basicPropagate(date).getMass(); } - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f) { - return propagate(date).getPVCoordinates(f); - } - - /** Try (and fail) to reset the initial state. + /** + * Try (and fail) to reset the initial state. *

        * This method always throws an exception, as ephemerides cannot be reset. *

        + * * @param state new initial state to consider */ public void resetInitialState(final SpacecraftState state) { @@ -289,7 +439,7 @@ public boolean isAdditionalStateManaged(final String name) { @Override public String[] getManagedAdditionalStates() { final String[] upperManaged = super.getManagedAdditionalStates(); - final String[] managed = new String[upperManaged.length + additional.length]; + final String[] managed = new String[upperManaged.length + additional.length]; System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length); System.arraycopy(additional, 0, managed, upperManaged.length, additional.length); return managed; @@ -300,71 +450,23 @@ public String[] getManagedAdditionalStates() { protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm, final DoubleArrayDictionary initialJacobianColumns) { // In order to not throw an Orekit exception during ephemeris based orbit determination - // The default behavior of the method is overrided to return a null parameter + // The default behavior of the method is overridden to return a null parameter return null; } - /** Internal PVCoordinatesProvider for attitude computation. */ - private static class LocalPVProvider implements PVCoordinatesProvider, Serializable { - - /** Serializable UID. */ - private static final long serialVersionUID = 20160115L; - - /** Current state. */ - private SpacecraftState currentState; - - /** List of spacecraft states. */ - private List states; - - /** Interpolation points number. */ - private int interpolationPoints; - - /** Extrapolation threshold. */ - private double extrapolationThreshold; - - /** Constructor. - * @param states list of spacecraft states - * @param interpolationPoints interpolation points number - * @param extrapolationThreshold extrapolation threshold value - */ - LocalPVProvider(final List states, final int interpolationPoints, - final double extrapolationThreshold) { - - this.states = states; - this.interpolationPoints = interpolationPoints; - this.extrapolationThreshold = extrapolationThreshold; - } - - /** Get the current state. - * @return current state - */ - public SpacecraftState getCurrentState() { - return currentState; - } - - /** Set the current state. - * @param state state to set - */ - public void setCurrentState(final SpacecraftState state) { - this.currentState = state; - } - - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f) { - final double dt = getCurrentState().getDate().durationFrom(date); - final double closeEnoughTimeInSec = 1e-9; - - if (FastMath.abs(dt) > closeEnoughTimeInSec) { - - // used in case of attitude transition, the attitude computed is not at the current date. - final Ephemeris ephemeris = new Ephemeris(states, interpolationPoints, extrapolationThreshold, null); - return ephemeris.getPVCoordinates(date, f); - } - - return currentState.getPVCoordinates(f); - - } + /** Get state interpolator. + * @return state interpolator + */ + public TimeInterpolator getStateInterpolator() { + return stateInterpolator; + } + /** Get covariance interpolator. + * @return optional covariance interpolator + * @see Optional + */ + public Optional>> getCovarianceInterpolator() { + return Optional.ofNullable(covarianceInterpolator); } } diff --git a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java index d36997a4b2..b12cc145f6 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,6 @@ import org.hipparchus.Field; import org.hipparchus.exception.MathRuntimeException; import org.hipparchus.ode.events.Action; -import org.hipparchus.util.MathArrays; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FieldAttitude; import org.orekit.errors.OrekitException; @@ -48,6 +47,7 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinatesProvider; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; import org.orekit.utils.TimeStampedFieldPVCoordinates; /** Common handling of {@link org.orekit.propagation.FieldPropagator} methods for analytical propagators. @@ -60,9 +60,11 @@ * propagation starting from some internally stored initial state up to the specified target date. *

        * @author Luc Maisonobe + * @param type of the field elements */ -public abstract class FieldAbstractAnalyticalPropagator> extends FieldAbstractPropagator { +public abstract class FieldAbstractAnalyticalPropagator> extends FieldAbstractPropagator + implements ParameterDriversProvider { /** Provider for attitude computation. */ private FieldPVCoordinatesProvider pvProvider; @@ -365,24 +367,6 @@ public FieldPVCoordinatesProvider getPvProvider() { */ protected abstract FieldOrbit propagateOrbit(FieldAbsoluteDate date, T[] parameters); - /** Get the parameters driver for propagation model. - * @return drivers for propagation model - */ - protected abstract List getParametersDrivers(); - - /** Get model parameters. - * @param field field to which the elements belong - * @return model parameters - */ - public T[] getParameters(final Field field) { - final List drivers = getParametersDrivers(); - final T[] parameters = MathArrays.buildArray(field, drivers.size()); - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue()); - } - return parameters; - } - /** Propagate an orbit without any fancy features. *

        This method is similar in spirit to the {@link #propagate} method, * except that it does not call any handler during @@ -395,7 +379,7 @@ protected FieldSpacecraftState basicPropagate(final FieldAbsoluteDate date try { // evaluate orbit - final FieldOrbit orbit = propagateOrbit(date, getParameters(date.getField())); + final FieldOrbit orbit = propagateOrbit(date, getParameters(date.getField(), date.getDate())); // evaluate attitude final FieldAttitude attitude = @@ -415,7 +399,7 @@ private class FieldLocalPVProvider implements FieldPVCoordinatesProvider { /** {@inheritDoc} */ @Override public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { - return propagateOrbit(date, getParameters(date.getField())).getPVCoordinates(frame); + return propagateOrbit(date, getParameters(date.getField(), date)).getPVCoordinates(frame); } } @@ -513,13 +497,13 @@ public Frame getFrame() { return FieldAbstractAnalyticalPropagator.this.getFrame(); } + /** {@inheritDoc} */ @Override - protected List getParametersDrivers() { + public List getParametersDrivers() { return FieldAbstractAnalyticalPropagator.this.getParametersDrivers(); } } - /** Internal class for local propagation. */ private class FieldBasicStepInterpolator implements FieldOrekitStepInterpolator { diff --git a/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java index 6965e81862..a3d5c6838a 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,7 @@ import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; @@ -36,10 +36,11 @@ import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.analytical.tle.FieldTLE; +import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldTimeSpanMap; import org.orekit.utils.ParameterDriver; @@ -82,8 +83,9 @@ * @author Melina Vanel * @author Bryan Cazabonne * @since 11.1 + * @param type of the field elements */ -public class FieldBrouwerLyddanePropagator> extends FieldAbstractAnalyticalPropagator { +public class FieldBrouwerLyddanePropagator> extends FieldAbstractAnalyticalPropagator { /** Default convergence threshold for mean parameters conversion. */ private static final double EPSILON_DEFAULT = 1.0e-13; @@ -134,7 +136,7 @@ public class FieldBrouwerLyddanePropagator> ex public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), initialOrbit.getMu().newInstance(DEFAULT_MASS), provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), M2); } @@ -196,7 +198,7 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final double referenceRadius, final T mu, final double c20, final double c30, final double c40, final double c50, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), initialOrbit.getMu().newInstance(DEFAULT_MASS), referenceRadius, mu, c20, c30, c40, c50, M2); } @@ -216,7 +218,7 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final T mass, final UnnormalizedSphericalHarmonicsProvider provider, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), M2); } @@ -250,7 +252,7 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final T m final double referenceRadius, final T mu, final double c20, final double c30, final double c40, final double c50, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), mass, referenceRadius, mu, c20, c30, c40, c50, M2); } @@ -375,7 +377,7 @@ public FieldBrouwerLyddanePropagator(final FieldOrbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final PropagationType initialType, final double M2) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), initialOrbit.getMu().newInstance(DEFAULT_MASS), provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, M2); } @@ -633,7 +635,7 @@ public static > FieldKeplerianOrbit compute final double epsilon, final int maxIterations) { final FieldBrouwerLyddanePropagator propagator = new FieldBrouwerLyddanePropagator<>(osculating, - InertialProvider.of(osculating.getFrame()), + FrameAlignedProvider.of(osculating.getFrame()), osculating.getMu().newInstance(DEFAULT_MASS), referenceRadius, osculating.getMu().newInstance(mu), c20, c30, c40, c50, @@ -735,7 +737,7 @@ private FieldBLModel computeMeanParameters(final FieldKeplerianOrbit oscul while (i++ < maxIterations) { // recompute the osculating parameters from the current mean parameters - final FieldKeplerianOrbit parameters = current.propagateParameters(current.mean.getDate(), getParameters(mass.getField())); + final FieldKeplerianOrbit parameters = current.propagateParameters(current.mean.getDate(), getParameters(mass.getField(), current.mean.getDate())); // adapted parameters residuals final T deltaA = osculating.getA() .subtract(parameters.getA()); @@ -753,7 +755,7 @@ private FieldBLModel computeMeanParameters(final FieldKeplerianOrbit oscul current.mean.getPerigeeArgument() .add(deltaOmega), current.mean.getRightAscensionOfAscendingNode().add(deltaRAAN), current.mean.getMeanAnomaly() .add(deltaAnom), - PositionAngle.MEAN, + PositionAngleType.MEAN, current.mean.getFrame(), current.mean.getDate(), mu), mass, referenceRadius, mu, ck0); @@ -786,6 +788,15 @@ public double getM2() { return M2Driver.getValue(); } + /** + * Get the value of the M2 drag parameter. + * @param date date at which the model parameters want to be known + * @return the value of the M2 drag parameter + */ + public double getM2(final AbsoluteDate date) { + return M2Driver.getValue(date); + } + /** Local class for Brouwer-Lyddane model. */ private static class FieldBLModel> { @@ -1386,7 +1397,7 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat g.getValue(), h.getValue(), l.getValue(), a.getFirstDerivative(), e.getFirstDerivative(), i.getFirstDerivative(), g.getFirstDerivative(), h.getFirstDerivative(), l.getFirstDerivative(), - PositionAngle.MEAN, mean.getFrame(), date, this.mu); + PositionAngleType.MEAN, mean.getFrame(), date, this.mu); } } @@ -1399,7 +1410,7 @@ protected T getMass(final FieldAbsoluteDate date) { /** {@inheritDoc} */ @Override - protected List getParametersDrivers() { + public List getParametersDrivers() { return Collections.singletonList(M2Driver); } diff --git a/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java index 9bc80c5a82..674d4394f9 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,8 +19,8 @@ import java.util.Collections; import java.util.List; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; @@ -28,7 +28,7 @@ import org.hipparchus.util.MathArrays; import org.hipparchus.util.MathUtils; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; @@ -37,7 +37,7 @@ import org.orekit.orbits.FieldCircularOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.time.FieldAbsoluteDate; @@ -53,6 +53,7 @@ * retrograde).

        * @see FieldOrbit * @author Guylaine Prat + * @param type of the field elements */ public class FieldEcksteinHechlerPropagator> extends FieldAbstractAnalyticalPropagator { @@ -91,7 +92,7 @@ public class FieldEcksteinHechlerPropagator> e */ public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), initialOrbit.getMu().newInstance(DEFAULT_MASS), provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate())); } @@ -150,7 +151,7 @@ public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, final double referenceRadius, final T mu, final double c20, final double c30, final double c40, final double c50, final double c60) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), initialOrbit.getMu().newInstance(DEFAULT_MASS), referenceRadius, mu, c20, c30, c40, c50, c60); } @@ -168,8 +169,8 @@ public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, */ public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, final T mass, final UnnormalizedSphericalHarmonicsProvider provider) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate())); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate())); } /** Build a propagator from FieldOrbit, mass and potential. @@ -202,8 +203,8 @@ public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, final T final double referenceRadius, final T mu, final double c20, final double c30, final double c40, final double c50, final double c60) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - mass, referenceRadius, mu, c20, c30, c40, c50, c60); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + mass, referenceRadius, mu, c20, c30, c40, c50, c60); } /** Build a propagator from FieldOrbit, attitude provider and potential provider. @@ -317,7 +318,7 @@ public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, public FieldEcksteinHechlerPropagator(final FieldOrbit initialOrbit, final UnnormalizedSphericalHarmonicsProvider provider, final PropagationType initialType) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), initialOrbit.getMu().newInstance(DEFAULT_MASS), provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType); } @@ -562,11 +563,11 @@ public static > FieldCircularOrbit computeM final double epsilon, final int maxIterations) { final FieldEcksteinHechlerPropagator propagator = new FieldEcksteinHechlerPropagator<>(osculating, - InertialProvider.of(osculating.getFrame()), - osculating.getMu().newInstance(DEFAULT_MASS), - referenceRadius, osculating.getMu().newInstance(mu), - c20, c30, c40, c50, c60, - PropagationType.OSCULATING, epsilon, maxIterations); + FrameAlignedProvider.of(osculating.getFrame()), + osculating.getMu().newInstance(DEFAULT_MASS), + referenceRadius, osculating.getMu().newInstance(mu), + c20, c30, c40, c50, c60, + PropagationType.OSCULATING, epsilon, maxIterations); return propagator.initialModel.mean; } @@ -679,7 +680,7 @@ private FieldEHModel computeMeanParameters(final FieldCircularOrbit oscula current.mean.getI() .add( deltaI ), current.mean.getRightAscensionOfAscendingNode().add(deltaRAAN), current.mean.getAlphaM().add(deltaAlphaM), - PositionAngle.MEAN, + PositionAngleType.MEAN, current.mean.getFrame(), current.mean.getDate(), mu), mass, referenceRadius, mu, ck0); @@ -1135,7 +1136,7 @@ protected T getMass(final FieldAbsoluteDate date) { /** {@inheritDoc} */ @Override - protected List getParametersDrivers() { + public List getParametersDrivers() { // Eckstein Hechler propagation model does not have parameter drivers. return Collections.emptyList(); } diff --git a/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java index a386d11406..4a60015bc4 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,11 +24,11 @@ import org.hipparchus.util.MathArrays; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FieldAttitude; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldArrayDictionary; @@ -38,6 +38,7 @@ /** Simple Keplerian orbit propagator. * @see FieldOrbit * @author Guylaine Prat + * @param type of the field elements */ public class FieldKeplerianPropagator> extends FieldAbstractAnalyticalPropagator { @@ -54,8 +55,8 @@ public class FieldKeplerianPropagator> extends * @see #FieldKeplerianPropagator(FieldOrbit, AttitudeProvider) */ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit) { - this(initialFieldOrbit, InertialProvider.of(initialFieldOrbit.getFrame()), - initialFieldOrbit.getMu(), initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); + this(initialFieldOrbit, FrameAlignedProvider.of(initialFieldOrbit.getFrame()), + initialFieldOrbit.getMu(), initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); } /** Build a propagator from orbit and central attraction coefficient μ. @@ -66,8 +67,8 @@ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit) { * @see #FieldKeplerianPropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement) */ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit, final T mu) { - this(initialFieldOrbit, InertialProvider.of(initialFieldOrbit.getFrame()), - mu, initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); + this(initialFieldOrbit, FrameAlignedProvider.of(initialFieldOrbit.getFrame()), + mu, initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); } /** Build a propagator from orbit and attitude provider. @@ -135,8 +136,8 @@ private FieldSpacecraftState fixState(final FieldOrbit orbit, final FieldA final FieldArrayDictionary additionalStatesderivatives) { final OrbitType type = orbit.getType(); final T[] stateVector = MathArrays.buildArray(mass.getField(), 6); - type.mapOrbitToArray(orbit, PositionAngle.TRUE, stateVector, null); - final FieldOrbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngle.TRUE, + type.mapOrbitToArray(orbit, PositionAngleType.TRUE, stateVector, null); + final FieldOrbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngleType.TRUE, orbit.getDate(), mu, orbit.getFrame()); FieldSpacecraftState fixedState = new FieldSpacecraftState<>(fixedOrbit, attitude, mass); if (additionalStates != null) { @@ -199,7 +200,7 @@ protected T getMass(final FieldAbsoluteDate date) { /** {@inheritDoc} */ @Override - protected List getParametersDrivers() { + public List getParametersDrivers() { // Keplerian propagation model does not have parameter drivers. return Collections.emptyList(); } diff --git a/src/main/java/org/orekit/propagation/analytical/J2DifferentialEffect.java b/src/main/java/org/orekit/propagation/analytical/J2DifferentialEffect.java index 02e77c21c6..552c7a8199 100644 --- a/src/main/java/org/orekit/propagation/analytical/J2DifferentialEffect.java +++ b/src/main/java/org/orekit/propagation/analytical/J2DifferentialEffect.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,7 +22,7 @@ import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -224,7 +224,7 @@ private Orbit updateOrbit(final Orbit orbit1) { // build updated orbit final EquinoctialOrbit updated = - new EquinoctialOrbit(original.getA(), ex, ey, hx, hy, lambda, PositionAngle.TRUE, + new EquinoctialOrbit(original.getA(), ex, ey, hx, hy, lambda, PositionAngleType.TRUE, original.getFrame(), date, original.getMu()); // convert to required type diff --git a/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java index fb2015b832..143c28392e 100644 --- a/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -62,7 +62,7 @@ public FieldKeplerianPropagator getPropagator(final FieldSpacecraftSta final Gradient mu = zero.add(propagator.getInitialState().getMu()); // Return the "Field" propagator - return new FieldKeplerianPropagator(state.getOrbit(), provider, mu); + return new FieldKeplerianPropagator<>(state.getOrbit(), provider, mu); } diff --git a/src/main/java/org/orekit/propagation/analytical/KeplerianHarvester.java b/src/main/java/org/orekit/propagation/analytical/KeplerianHarvester.java index 00381153c6..0f2669c79a 100644 --- a/src/main/java/org/orekit/propagation/analytical/KeplerianHarvester.java +++ b/src/main/java/org/orekit/propagation/analytical/KeplerianHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.propagation.analytical; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.PositionAngleType; import org.orekit.utils.DoubleArrayDictionary; /** @@ -35,7 +36,7 @@ class KeplerianHarvester extends AbstractAnalyticalMatricesHarvester { *

        * The arguments for initial matrices must be compatible with the * {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name diff --git a/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java b/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java index fa07d16f1d..f76109933e 100644 --- a/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,10 +19,10 @@ import org.hipparchus.linear.RealMatrix; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -47,8 +47,8 @@ public class KeplerianPropagator extends AbstractAnalyticalPropagator { * @see #KeplerianPropagator(Orbit, AttitudeProvider) */ public KeplerianPropagator(final Orbit initialOrbit) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - initialOrbit.getMu(), DEFAULT_MASS); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + initialOrbit.getMu(), DEFAULT_MASS); } /** Build a propagator from orbit and central attraction coefficient μ. @@ -59,8 +59,8 @@ public KeplerianPropagator(final Orbit initialOrbit) { * @see #KeplerianPropagator(Orbit, AttitudeProvider, double) */ public KeplerianPropagator(final Orbit initialOrbit, final double mu) { - this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()), - mu, DEFAULT_MASS); + this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()), + mu, DEFAULT_MASS); } /** Build a propagator from orbit and attitude provider. @@ -129,8 +129,8 @@ private SpacecraftState fixState(final Orbit orbit, final Attitude attitude, fin final DoubleArrayDictionary additionalStatesDerivatives) { final OrbitType type = orbit.getType(); final double[] stateVector = new double[6]; - type.mapOrbitToArray(orbit, PositionAngle.TRUE, stateVector, null); - final Orbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngle.TRUE, + type.mapOrbitToArray(orbit, PositionAngleType.TRUE, stateVector, null); + final Orbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngleType.TRUE, orbit.getDate(), mu, orbit.getFrame()); SpacecraftState fixedState = new SpacecraftState(fixedOrbit, attitude, mass); if (additionalStates != null) { diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java b/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java index 5f477d3323..3dfdfe41d1 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/ClockCorrectionsProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -42,7 +42,7 @@ * Since Orekit 10.3 the relativistic clock correction can be used as an {@link EstimationModifier} * in orbit determination applications to take into consideration this effect * in measurement modeling. - *

        + *

        * * @author Luc Maisonobe * @since 9.3 diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java index e832f85d61..52ef9b6416 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,6 +24,7 @@ import org.hipparchus.util.MathArrays; import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; +import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; @@ -140,6 +141,10 @@ public class GLONASSAnalyticalPropagator extends AbstractAnalyticalPropagator { this.eci = eci; // Sets the Earth Centered Earth Fixed frame this.ecef = ecef; + // Sets initial state + final Orbit orbit = propagateOrbit(getStartDate()); + final Attitude attitude = provider.getAttitude(orbit, orbit.getDate(), orbit.getFrame()); + super.resetInitialState(new SpacecraftState(orbit, attitude, mass)); } /** diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagatorBuilder.java b/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagatorBuilder.java index a352d8797b..e8721c79d6 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; import org.orekit.frames.Frame; import org.orekit.frames.Frames; @@ -118,13 +118,13 @@ public GLONASSAnalyticalPropagatorBuilder(final GLONASSOrbitalElements glonassOr */ public GLONASSAnalyticalPropagatorBuilder(final GLONASSOrbitalElements glonassOrbElt, final DataContext dataContext) { - this.orbit = glonassOrbElt; - this.dataContext = dataContext; - this.mass = Propagator.DEFAULT_MASS; - final Frames frames = dataContext.getFrames(); - this.eci = frames.getEME2000(); - this.ecef = frames.getITRF(IERSConventions.IERS_2010, true); - attitudeProvider = InertialProvider.of(this.eci); + this.orbit = glonassOrbElt; + this.dataContext = dataContext; + this.mass = Propagator.DEFAULT_MASS; + final Frames frames = dataContext.getFrames(); + this.eci = frames.getEME2000(); + this.ecef = frames.getITRF(IERSConventions.IERS_2010, true); + this.attitudeProvider = FrameAlignedProvider.of(this.eci); } /** Sets the attitude provider. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagator.java b/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagator.java index 04b5a729f3..a1f0502f9c 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,6 +22,7 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; +import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -92,6 +93,10 @@ public class GNSSPropagator extends AbstractAnalyticalPropagator { this.eci = eci; // Sets the Earth Centered Earth Fixed frame this.ecef = ecef; + // Sets initial state + final Orbit orbit = propagateOrbit(getStartDate()); + final Attitude attitude = provider.getAttitude(orbit, orbit.getDate(), orbit.getFrame()); + super.resetInitialState(new SpacecraftState(orbit, attitude, mass)); } /** diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagatorBuilder.java b/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagatorBuilder.java index dbc34e96f0..ae119d0b0f 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/GNSSPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,7 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; import org.orekit.frames.Frame; import org.orekit.frames.Frames; @@ -105,11 +105,11 @@ public GNSSPropagatorBuilder(final GNSSOrbitalElements gnssOrbElt) { * @see #ecef(Frame bodyFixed) */ public GNSSPropagatorBuilder(final GNSSOrbitalElements gnssOrbElt, final Frames frames) { - this.orbit = gnssOrbElt; - this.mass = Propagator.DEFAULT_MASS; - this.eci = frames.getEME2000(); - this.ecef = frames.getITRF(IERSConventions.IERS_2010, true); - attitudeProvider = InertialProvider.of(this.eci); + this.orbit = gnssOrbElt; + this.mass = Propagator.DEFAULT_MASS; + this.eci = frames.getEME2000(); + this.ecef = frames.getITRF(IERSConventions.IERS_2010, true); + this.attitudeProvider = FrameAlignedProvider.of(this.eci); } /** Sets the attitude provider. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagator.java b/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagator.java index 877b430e5b..03fa688e1b 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -83,6 +84,10 @@ public class SBASPropagator extends AbstractAnalyticalPropagator { this.eci = eci; // Sets the Earth Centered Earth Fixed frame this.ecef = ecef; + // Sets initial state + final Orbit orbit = propagateOrbit(getStartDate()); + final Attitude attitude = provider.getAttitude(orbit, orbit.getDate(), orbit.getFrame()); + super.resetInitialState(new SpacecraftState(orbit, attitude, mass)); } /** diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagatorBuilder.java b/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagatorBuilder.java index 75feb96ce6..c474a6ed65 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/SBASPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,10 @@ */ package org.orekit.propagation.analytical.gnss; +import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.data.DataContext; import org.orekit.frames.Frame; import org.orekit.frames.Frames; import org.orekit.propagation.Propagator; @@ -63,6 +65,33 @@ public class SBASPropagatorBuilder { * The ECEF frame is set by default to the * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP CIO/2010-based ITRF simple EOP}. *

        + *

        This constructor uses the {@link DataContext#getDefault() default data context}.

        + * + * @param sbasOrbElt the SBAS orbital elements to be used by the SBAS propagator. + * @see #attitudeProvider(AttitudeProvider provider) + * @see #mu(double coefficient) + * @see #mass(double mass) + * @see #eci(Frame inertial) + * @see #ecef(Frame bodyFixed) + * @since 12.0 + */ + @DefaultDataContext + public SBASPropagatorBuilder(final SBASOrbitalElements sbasOrbElt) { + this(sbasOrbElt, DataContext.getDefault().getFrames()); + } + + /** Initializes the builder. + *

        The SBAS orbital elements is the only requested parameter to build a SBASPropagator.

        + *

        The attitude provider is set by default to be aligned with the EME2000 frame.
        + * The Earth gravity coefficient is set by default to the + * {@link org.orekit.propagation.analytical.gnss.data.GNSSConstants#SBAS_MU SBAS_MU}.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame}.
        + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP CIO/2010-based ITRF simple EOP}. + *

        * * @param sbasOrbElt the SBAS orbital elements to be used by the SBAS propagator. * @param frames set of reference frames to use to initialize {@link @@ -75,12 +104,12 @@ public class SBASPropagatorBuilder { * @see #ecef(Frame bodyFixed) */ public SBASPropagatorBuilder(final SBASOrbitalElements sbasOrbElt, final Frames frames) { - this.orbit = sbasOrbElt; - this.mass = Propagator.DEFAULT_MASS; - this.eci = frames.getEME2000(); - this.ecef = frames.getITRF(IERSConventions.IERS_2010, true); - this.mu = GNSSConstants.SBAS_MU; - this.attitudeProvider = new InertialProvider(eci); + this.orbit = sbasOrbElt; + this.mass = Propagator.DEFAULT_MASS; + this.eci = frames.getEME2000(); + this.ecef = frames.getITRF(IERSConventions.IERS_2010, true); + this.mu = GNSSConstants.SBAS_MU; + this.attitudeProvider = new FrameAlignedProvider(eci); } /** Sets the attitude provider. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractAlmanac.java index 66e5c86369..ab212ca44b 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ * @author Pascal Parraud * @since 11.0 */ -public abstract class AbstractAlmanac extends CommonGnssData { +public abstract class AbstractAlmanac extends CommonGnssData implements GNSSOrbitalElements { /** * Constructor. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractEphemerisMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractEphemerisMessage.java index d30e9abce9..87febac850 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractEphemerisMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractEphemerisMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractNavigationMessage.java index 0aad5d4948..04aa8606e1 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/AbstractNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,13 +24,16 @@ * @author Bryan Cazabonne * @since 11.0 * - * @see GPSNavigationMessage + * @see GPSLegacyNavigationMessage * @see GalileoNavigationMessage - * @see BeidouNavigationMessage - * @see QZSSNavigationMessage + * @see BeidouLegacyNavigationMessage + * @see QZSSLegacyNavigationMessage * @see IRNSSNavigationMessage */ -public abstract class AbstractNavigationMessage extends CommonGnssData { +public abstract class AbstractNavigationMessage extends CommonGnssData implements GNSSOrbitalElements { + + /** Square root of a. */ + private double sqrtA; /** Mean Motion Difference from Computed Value. */ private double deltaN; @@ -62,6 +65,11 @@ public abstract class AbstractNavigationMessage extends CommonGnssData { /** Amplitude of the Sine Harmonic Correction Term to the Angle of Inclination. */ private double cis; + /** Transmission time. + * @since 12.0 + */ + private double transmissionTime; + /** * Constructor. * @param mu Earth's universal gravitational parameter @@ -75,14 +83,23 @@ public AbstractNavigationMessage(final double mu, } /** - * Setter for the Square Root of Semi-Major Axis (m^1/2). + * Getter for Square Root of Semi-Major Axis (√m). + * @return Square Root of Semi-Major Axis (√m) + */ + public double getSqrtA() { + return sqrtA; + } + + /** + * Setter for the Square Root of Semi-Major Axis (√m). *

        * In addition, this method set the value of the Semi-Major Axis. *

        - * @param sqrtA the Square Root of Semi-Major Axis (m^1/2) + * @param sqrtA the Square Root of Semi-Major Axis (√m) */ public void setSqrtA(final double sqrtA) { - super.setSma(sqrtA * sqrtA); + this.sqrtA = sqrtA; + setSma(sqrtA * sqrtA); } /** @@ -94,6 +111,14 @@ public double getMeanMotion() { return FastMath.sqrt(getMu() / absA) / absA + deltaN; } + /** + * Getter for the delta of satellite mean motion. + * @return delta of satellite mean motion + */ + public double getDeltaN() { + return deltaN; + } + /** * Setter for the delta of satellite mean motion. * @param deltaN the value to set @@ -246,4 +271,22 @@ public void setCis(final double cis) { this.cis = cis; } + /** + * Getter for transmission time. + * @return transmission time + * @since 12.0 + */ + public double getTransmissionTime() { + return transmissionTime; + } + + /** + * Setter for transmission time. + * @param transmissionTime transmission time + * @since 12.0 + */ + public void setTransmissionTime(final double transmissionTime) { + this.transmissionTime = transmissionTime; + } + } diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouAlmanac.java index 102d935129..055561d9e1 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,7 +26,7 @@ * @since 10.0 * */ -public class BeidouAlmanac extends AbstractAlmanac implements GNSSOrbitalElements { +public class BeidouAlmanac extends AbstractAlmanac { /** Health status. */ private int health; diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouCivilianNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouCivilianNavigationMessage.java new file mode 100644 index 0000000000..d0a6866703 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouCivilianNavigationMessage.java @@ -0,0 +1,399 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +import org.orekit.gnss.Frequency; + +/** + * Container for data contained in a Beidou civilian navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class BeidouCivilianNavigationMessage extends AbstractNavigationMessage { + + /** Identifier for Beidou-3 B1C message type. */ + public static final String CNV1 = "CNV1"; + + /** Identifier for Beidou-3 B2A message type. */ + public static final String CNV2 = "CNV2"; + + /** Identifier for Beidou-3 B2B message type. */ + public static final String CNV3 = "CNV3"; + + /** Signal on which navigation signal is sent. */ + private final Frequency signal; + + /** Change rate in semi-major axis (m/s). */ + private double aDot; + + /** Change rate in Δn₀. */ + private double deltaN0Dot; + + /** Issue of Data, Ephemeris. */ + private int iode; + + /** Issue of Data, Clock. */ + private int iodc; + + /** Inter Signal Delay for B1 CD. */ + private double iscB1CD; + + /** Inter Signal Delay for B1 CP. */ + private double iscB1CP; + + /** Inter Signal Delay for B2 AD. */ + private double iscB2AD; + + /** Signal In Space Accuracy Index (along track and across track). */ + private int sisaiOe; + + /** Signal In Space Accuracy Index (radial and clock). */ + private int sisaiOcb; + + /** Signal In Space Accuracy Index (clock drift accuracy). */ + private int sisaiOc1; + + /** Signal In Space Accuracy Index (clock drift rate accuracy). */ + private int sisaiOc2; + + /** Signal In Space Monitoring Accuracy Index. */ + private int sismai; + + /** Health. */ + private int health; + + /** Integrity flags. */ + private int integrityFlags; + + /** B1/B3 Group Delay Differential (s). */ + private double tgdB1Cp; + + /** B2 AP Group Delay Differential (s). */ + private double tgdB2ap; + + /** B2B_i / B3I Group Delay Differential (s). */ + private double tgdB2bI; + + /** Satellite type. */ + private BeidouSatelliteType satelliteType; + + /** + * Constructor. + * @param signal signal on which navigation signal is sent + */ + public BeidouCivilianNavigationMessage(final Frequency signal) { + super(GNSSConstants.BEIDOU_MU, GNSSConstants.BEIDOU_AV, GNSSConstants.BEIDOU_WEEK_NB); + this.signal = signal; + } + + /** + * Getter for signal. + * @return signal on which navigation signal is sent + */ + public Frequency getSignal() { + return signal; + } + + /** + * Getter for the change rate in semi-major axis. + * @return the change rate in semi-major axis + */ + public double getADot() { + return aDot; + } + + /** + * Setter for the change rate in semi-major axis. + * @param value the change rate in semi-major axis + */ + public void setADot(final double value) { + this.aDot = value; + } + + /** + * Getter for change rate in Δn₀. + * @return change rate in Δn₀ + */ + public double getDeltaN0Dot() { + return deltaN0Dot; + } + + /** + * Setter for change rate in Δn₀. + * @param deltaN0Dot change rate in Δn₀ + */ + public void setDeltaN0Dot(final double deltaN0Dot) { + this.deltaN0Dot = deltaN0Dot; + } + + /** + * Getter for the Issue Of Data Ephemeris (IODE). + * @return the Issue Of Data Ephemeris (IODE) + */ + public int getIODE() { + return iode; + } + + /** + * Setter for the Issue of Data Ephemeris. + * @param value the IODE to set + */ + public void setIODE(final int value) { + this.iode = value; + } + + /** + * Getter for the Issue Of Data Clock (IODC). + * @return the Issue Of Data Clock (IODC) + */ + public int getIODC() { + return iodc; + } + + /** + * Setter for the Issue of Data Clock. + * @param value the IODC to set + */ + public void setIODC(final int value) { + this.iodc = value; + } + + /** + * Getter for inter Signal Delay for B1 CD. + * @return inter signal delay + */ + public double getIscB1CD() { + return iscB1CD; + } + + /** + * Setter for inter Signal Delay for B1 CD. + * @param delay delay to set + */ + public void setIscB1CD(final double delay) { + this.iscB1CD = delay; + } + + /** + * Getter for inter Signal Delay for B2 AD. + * @return inter signal delay + */ + public double getIscB2AD() { + return iscB2AD; + } + + /** + * Setter for inter Signal Delay for B2 AD. + * @param delay delay to set + */ + public void setIscB2AD(final double delay) { + this.iscB2AD = delay; + } + + /** + * Getter for inter Signal Delay for B1 CP. + * @return inter signal delay + */ + public double getIscB1CP() { + return iscB1CP; + } + + /** + * Setter for inter Signal Delay for B1 CP. + * @param delay delay to set + */ + public void setIscB1CP(final double delay) { + this.iscB1CP = delay; + } + + /** + * Getter for Signal In Space Accuracy Index (along track and across track). + * @return Signal In Space Accuracy Index (along track and across track) + */ + public int getSisaiOe() { + return sisaiOe; + } + + /** + * Setter for Signal In Space Accuracy Index (along track and across track). + * @param sisaiOe Signal In Space Accuracy Index (along track and across track) + */ + public void setSisaiOe(final int sisaiOe) { + this.sisaiOe = sisaiOe; + } + + /** + * Getter for Signal In Space Accuracy Index (radial and clock). + * @return Signal In Space Accuracy Index (radial and clock) + */ + public int getSisaiOcb() { + return sisaiOcb; + } + + /** + * Setter for Signal In Space Accuracy Index (radial and clock). + * @param sisaiOcb Signal In Space Accuracy Index (radial and clock) + */ + public void setSisaiOcb(final int sisaiOcb) { + this.sisaiOcb = sisaiOcb; + } + + /** + * Getter for Signal In Space Accuracy Index (clock drift accuracy). + * @return Signal In Space Accuracy Index (clock drift accuracy) + */ + public int getSisaiOc1() { + return sisaiOc1; + } + + /** + * Setter for Signal In Space Accuracy Index (clock drift accuracy). + * @param sisaiOc1 Signal In Space Accuracy Index (clock drift accuracy) + */ + public void setSisaiOc1(final int sisaiOc1) { + this.sisaiOc1 = sisaiOc1; + } + + /** + * Getter for Signal In Space Accuracy Index (clock drift rate accuracy). + * @return Signal In Space Accuracy Index (clock drift rate accuracy) + */ + public int getSisaiOc2() { + return sisaiOc2; + } + + /** + * Setter for Signal In Space Accuracy Index (clock drift rate accuracy). + * @param sisaiOc2 Signal In Space Accuracy Index (clock drift rate accuracy) + */ + public void setSisaiOc2(final int sisaiOc2) { + this.sisaiOc2 = sisaiOc2; + } + + /** + * Getter for Signal In Space Monitoring Accuracy Index. + * @return Signal In Space Monitoring Accuracy Index + */ + public int getSismai() { + return sismai; + } + + /** + * Setter for Signal In Space Monitoring Accuracy Index. + * @param sismai Signal In Space Monitoring Accuracy Index + */ + public void setSismai(final int sismai) { + this.sismai = sismai; + } + + /** + * Getter for health. + * @return health + */ + public int getHealth() { + return health; + } + + /** + * Setter for health. + * @param health health + */ + public void setHealth(final int health) { + this.health = health; + } + + /** + * Getter for B1C integrity flags. + * @return B1C integrity flags + */ + public int getIntegrityFlags() { + return integrityFlags; + } + + /** + * Setter for B1C integrity flags. + * @param integrityFlags integrity flags + */ + public void setIntegrityFlags(final int integrityFlags) { + this.integrityFlags = integrityFlags; + } + + /** + * Getter for B1/B3 Group Delay Differential (s). + * @return B1/B3 Group Delay Differential (s) + */ + public double getTgdB1Cp() { + return tgdB1Cp; + } + + /** + * Setter for B1/B3 Group Delay Differential (s). + * @param tgdB1Cp B1/B3 Group Delay Differential (s) + */ + public void setTgdB1Cp(final double tgdB1Cp) { + this.tgdB1Cp = tgdB1Cp; + } + + /** + * Getter for B2 AP Group Delay Differential (s). + * @return B2 AP Group Delay Differential (s) + */ + public double getTgdB2ap() { + return tgdB2ap; + } + + /** + * Setter for B2 AP Group Delay Differential (s). + * @param tgdB2ap B2 AP Group Delay Differential (s) + */ + public void setTgdB2ap(final double tgdB2ap) { + this.tgdB2ap = tgdB2ap; + } + + /** + * Getter for B2B_i / B3I Group Delay Differential (s). + * @return B2B_i / B3I Group Delay Differential (s) + */ + public double getTgdB2bI() { + return tgdB2bI; + } + + /** + * Setter for B2B_i / B3I Group Delay Differential (s). + * @param tgdB2bI B2B_i / B3I Group Delay Differential (s) + */ + public void setTgdB2bI(final double tgdB2bI) { + this.tgdB2bI = tgdB2bI; + } + + /** + * Getter for satellite type. + * @return satellite type + */ + public BeidouSatelliteType getSatelliteType() { + return satelliteType; + } + + /** + * Setter for satellite type. + * @param satelliteType satellite type + */ + public void setSatelliteType(final BeidouSatelliteType satelliteType) { + this.satelliteType = satelliteType; + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouLegacyNavigationMessage.java similarity index 90% rename from src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouNavigationMessage.java rename to src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouLegacyNavigationMessage.java index 501a4b3e99..25687ad499 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouLegacyNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,13 @@ * @author Bryan Cazabonne * @since 11.0 */ -public class BeidouNavigationMessage extends AbstractNavigationMessage implements GNSSOrbitalElements { +public class BeidouLegacyNavigationMessage extends AbstractNavigationMessage { + + /** Identifier for message type. */ + public static final String D1 = "D1"; + + /** Identifier for message type. */ + public static final String D2 = "D2"; /** Age of Data, Ephemeris. */ private int aode; @@ -39,7 +45,7 @@ public class BeidouNavigationMessage extends AbstractNavigationMessage implement private double svAccuracy; /** Constructor. */ - public BeidouNavigationMessage() { + public BeidouLegacyNavigationMessage() { super(GNSSConstants.BEIDOU_MU, GNSSConstants.BEIDOU_AV, GNSSConstants.BEIDOU_WEEK_NB); } @@ -69,7 +75,7 @@ public int getAODE() { } /** - * Setter for the age of data ephemeric. + * Setter for the age of data ephemeris. * @param aod the age of data to set */ public void setAODE(final double aod) { diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouSatelliteType.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouSatelliteType.java new file mode 100644 index 0000000000..bf567f4dac --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/BeidouSatelliteType.java @@ -0,0 +1,37 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +/** Enumerate for Beidou satellite type. + * @author Luc Maisonobe + * @since 12.0 + */ +public enum BeidouSatelliteType { + + /** Reserved. */ + RESERVED, + + /** Geostationary satellite. */ + GEO, + + /** Inclined geosynchronous. */ + IGSO, + + /** Middle Earth Orbit. */ + MEO; + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/CivilianNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/CivilianNavigationMessage.java new file mode 100644 index 0000000000..37044ce98f --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/CivilianNavigationMessage.java @@ -0,0 +1,342 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +/** + * Container for data contained in a GPS/QZNSS civilian navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class CivilianNavigationMessage extends AbstractNavigationMessage implements GNSSClockElements { + + /** Identifier for message type. */ + public static final String CNAV = "CNAV"; + + /** Identifier for message type. */ + public static final String CNV2 = "CNV2"; + + /** Indicator for CNV 2 messages. */ + private final boolean cnv2; + + /** Change rate in semi-major axis (m/s). */ + private double aDot; + + /** Change rate in Δn₀. */ + private double deltaN0Dot; + + /** Group Delay Differential (s). */ + private double tgd; + + /** The user SV accuracy (m). */ + private double svAccuracy; + + /** Satellite health status. */ + private int svHealth; + + /** Inter Signal Delay for L1 C/A. */ + private double iscL1CA; + + /** Inter Signal Delay for L1 CD. */ + private double iscL1CD; + + /** Inter Signal Delay for L1 CP. */ + private double iscL1CP; + + /** Inter Signal Delay for L2 C. */ + private double iscL2C; + + /** Inter Signal Delay for L5I. */ + private double iscL5I5; + + /** Inter Signal Delay for L5Q. */ + private double iscL5Q5; + + /** Elevation-Dependent User Range Accuracy. */ + private int uraiEd; + + /** Term 0 of Non-Elevation-Dependent User Range Accuracy. */ + private int uraiNed0; + + /** Term 1 of Non-Elevation-Dependent User Range Accuracy. */ + private int uraiNed1; + + /** Term 2 of Non-Elevation-Dependent User Range Accuracy. */ + private int uraiNed2; + + /** + * Constructor. + * @param cnv2 indicator for CNV2 messages + * @param mu Earth's universal gravitational parameter + * @param angularVelocity mean angular velocity of the Earth for the GNSS model + * @param weekNumber number of weeks in the GNSS cycle + */ + protected CivilianNavigationMessage(final boolean cnv2, + final double mu, + final double angularVelocity, + final int weekNumber) { + super(mu, angularVelocity, weekNumber); + this.cnv2 = cnv2; + } + + /** Check it message is a CNV2 message. + * @return true if message is a CNV2 message + */ + public boolean isCnv2() { + return cnv2; + } + + /** + * Getter for the change rate in semi-major axis. + * @return the change rate in semi-major axis + */ + public double getADot() { + return aDot; + } + + /** + * Setter for the change rate in semi-major axis. + * @param value the change rate in semi-major axis + */ + public void setADot(final double value) { + this.aDot = value; + } + + /** + * Getter for change rate in Δn₀. + * @return change rate in Δn₀ + */ + public double getDeltaN0Dot() { + return deltaN0Dot; + } + + /** + * Setter for change rate in Δn₀. + * @param deltaN0Dot change rate in Δn₀ + */ + public void setDeltaN0Dot(final double deltaN0Dot) { + this.deltaN0Dot = deltaN0Dot; + } + + /** + * Getter for the Group Delay Differential (s). + * @return the Group Delay Differential in seconds + */ + public double getTGD() { + return tgd; + } + + /** + * Setter for the Group Delay Differential (s). + * @param time the group delay differential to set + */ + public void setTGD(final double time) { + this.tgd = time; + } + + /** + * Getter for the user SV accuray (meters). + * @return the user SV accuracy + */ + public double getSvAccuracy() { + return svAccuracy; + } + + /** + * Setter for the user SV accuracy. + * @param svAccuracy the value to set + */ + public void setSvAccuracy(final double svAccuracy) { + this.svAccuracy = svAccuracy; + } + + /** + * Getter for the satellite health status. + * @return the satellite health status + */ + public int getSvHealth() { + return svHealth; + } + + /** + * Setter for the satellite health status. + * @param svHealth the value to set + */ + public void setSvHealth(final int svHealth) { + this.svHealth = svHealth; + } + + /** + * Getter for inter Signal Delay for L1 C/A. + * @return inter signal delay + */ + public double getIscL1CA() { + return iscL1CA; + } + + /** + * Setter for inter Signal Delay for L1 C/A. + * @param delay delay to set + */ + public void setIscL1CA(final double delay) { + this.iscL1CA = delay; + } + + /** + * Getter for inter Signal Delay for L1 CD. + * @return inter signal delay + */ + public double getIscL1CD() { + return iscL1CD; + } + + /** + * Setter for inter Signal Delay for L1 CD. + * @param delay delay to set + */ + public void setIscL1CD(final double delay) { + this.iscL1CD = delay; + } + + /** + * Getter for inter Signal Delay for L1 CP. + * @return inter signal delay + */ + public double getIscL1CP() { + return iscL1CP; + } + + /** + * Setter for inter Signal Delay for L1 CP. + * @param delay delay to set + */ + public void setIscL1CP(final double delay) { + this.iscL1CP = delay; + } + + /** + * Getter for inter Signal Delay for L2 C. + * @return inter signal delay + */ + public double getIscL2C() { + return iscL2C; + } + + /** + * Setter for inter Signal Delay for L2 C. + * @param delay delay to set + */ + public void setIscL2C(final double delay) { + this.iscL2C = delay; + } + + /** + * Getter for inter Signal Delay for L5I. + * @return inter signal delay + */ + public double getIscL5I5() { + return iscL5I5; + } + + /** + * Setter for inter Signal Delay for L5I. + * @param delay delay to set + */ + public void setIscL5I5(final double delay) { + this.iscL5I5 = delay; + } + + /** + * Getter for inter Signal Delay for L5Q. + * @return inter signal delay + */ + public double getIscL5Q5() { + return iscL5Q5; + } + + /** + * Setter for inter Signal Delay for L5Q. + * @param delay delay to set + */ + public void setIscL5Q5(final double delay) { + this.iscL5Q5 = delay; + } + + /** + * Getter for Elevation-Dependent User Range Accuracy. + * @return Elevation-Dependent User Range Accuracy + */ + public int getUraiEd() { + return uraiEd; + } + + /** + * Setter for Elevation-Dependent User Range Accuracy. + * @param uraiEd Elevation-Dependent User Range Accuracy + */ + public void setUraiEd(final int uraiEd) { + this.uraiEd = uraiEd; + } + + /** + * Getter for term 0 of Non-Elevation-Dependent User Range Accuracy. + * @return term 0 of Non-Elevation-Dependent User Range Accuracy + */ + public int getUraiNed0() { + return uraiNed0; + } + + /** + * Setter for term 0 of Non-Elevation-Dependent User Range Accuracy. + * @param uraiNed0 term 0 of Non-Elevation-Dependent User Range Accuracy + */ + public void setUraiNed0(final int uraiNed0) { + this.uraiNed0 = uraiNed0; + } + + /** + * Getter for term 1 of Non-Elevation-Dependent User Range Accuracy. + * @return term 1 of Non-Elevation-Dependent User Range Accuracy + */ + public int getUraiNed1() { + return uraiNed1; + } + + /** + * Setter for term 1 of Non-Elevation-Dependent User Range Accuracy. + * @param uraiNed1 term 1 of Non-Elevation-Dependent User Range Accuracy + */ + public void setUraiNed1(final int uraiNed1) { + this.uraiNed1 = uraiNed1; + } + + /** + * Getter for term 2 of Non-Elevation-Dependent User Range Accuracy. + * @return term 2 of Non-Elevation-Dependent User Range Accuracy + */ + public int getUraiNed2() { + return uraiNed2; + } + + /** + * Setter for term 2 of Non-Elevation-Dependent User Range Accuracy. + * @param uraiNed2 term 2 of Non-Elevation-Dependent User Range Accuracy + */ + public void setUraiNed2(final int uraiNed2) { + this.uraiNed2 = uraiNed2; + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/CommonGnssData.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/CommonGnssData.java index 215aacd947..4c13bc4962 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/CommonGnssData.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/CommonGnssData.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -169,7 +169,7 @@ public double getTime() { /** * Setter for the reference time of the orbit as a duration from week start. - * @param time the time to set + * @param time the time to set in seconds */ public void setTime(final double time) { this.time = time; diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSAlmanac.java index 642b10b23c..95d364afe9 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,12 +17,18 @@ package org.orekit.propagation.analytical.gnss.data; import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.frames.Frames; +import org.orekit.propagation.analytical.gnss.GLONASSAnalyticalPropagator; +import org.orekit.propagation.analytical.gnss.GLONASSAnalyticalPropagatorBuilder; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.GLONASSDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; +import org.orekit.utils.IERSConventions; /** * This class holds a GLONASS almanac as read from .agl files. @@ -162,6 +168,75 @@ public GLONASSAlmanac(final int channel, final int health, this.glonass = glonass; } + /** + * Get the propagator corresponding to the navigation message. + *

        + * The attitude provider is set by default to be aligned with the EME2000 frame.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The data context is by default to the + * {@link DataContext#getDefault() default data context}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame} in the default data + * context.
        + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP + * CIO/2010-based ITRF simple EOP} in the default data context. + *

        + * @return the propagator corresponding to the navigation message + * @see #getPropagator(DataContext) + * @see #getPropagator(DataContext, AttitudeProvider, Frame, Frame, double) + * @since 12.0 + */ + @DefaultDataContext + public GLONASSAnalyticalPropagator getPropagator() { + return new GLONASSAnalyticalPropagatorBuilder(this).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + *

        + * The attitude provider is set by default to be aligned with the EME2000 frame.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The ECI frame is set by default to the + * {@link Frames#getEME2000() EME2000 frame}.
        + * The ECEF frame is set by default to the + * {@link Frames#getITRF(IERSConventions, boolean) CIO/2010-based ITRF simple + * EOP}. + *

        + * @param context the data context to use for frames and time scales. + * @return the propagator corresponding to the navigation message + * @see #getPropagator() + * @see #getPropagator(DataContext, AttitudeProvider, Frame, Frame, double) + * @since 12.0 + */ + public GLONASSAnalyticalPropagator getPropagator(final DataContext context) { + return new GLONASSAnalyticalPropagatorBuilder(this, context).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + * @param context the data context to use for frames and time scales. + * @param provider attitude provider + * @param inertial inertial frame, use to provide the propagated orbit + * @param bodyFixed body fixed frame, corresponding to the navigation message + * @param mass spacecraft mass in kg + * @return the propagator corresponding to the navigation message + * @see #getPropagator() + * @see #getPropagator(DataContext) + * @since 12.0 + */ + public GLONASSAnalyticalPropagator getPropagator(final DataContext context, final AttitudeProvider provider, + final Frame inertial, final Frame bodyFixed, + final double mass) { + return new GLONASSAnalyticalPropagatorBuilder(this, context).attitudeProvider(provider) + .eci(inertial) + .ecef(bodyFixed) + .mass(mass) + .build(); + } + @Override public AbsoluteDate getDate() { final DateComponents date = new DateComponents(year, month, day); diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSEphemeris.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSEphemeris.java index 5deef1b7cb..96b41ddc39 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSEphemeris.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSEphemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSNavigationMessage.java index 25665692c4..bed112b613 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,15 @@ */ package org.orekit.propagation.analytical.gnss.data; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.propagation.numerical.GLONASSNumericalPropagator; +import org.orekit.propagation.numerical.GLONASSNumericalPropagatorBuilder; + /** * Container for data contained in a Glonass navigation message. * @author Bryan Cazabonne @@ -35,11 +44,101 @@ public class GLONASSNavigationMessage extends AbstractEphemerisMessage implement /** Frequency number. */ private int frequencyNumber; + /** Status flags. + * @since 12.0 + */ + private int statusFlags; + + /** Health flags. + * @since 12.0 + */ + private int healthFlags; + + /** Group Delay Difference (s). + * @since 12.0 + */ + private double groupDelayDifference; + + /** User range accuracy (m). + * @since 12.0 + */ + private double ura; + /** Constructor. */ public GLONASSNavigationMessage() { // Nothing to do ... } + /** + * Get the propagator corresponding to the navigation message. + *

        The attitude provider is set by default to EME2000 aligned in the + * default data context.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The data context is by default to the + * {@link DataContext#getDefault() default data context}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame} in the default data + * context.
        + *

        + * @param step integration step in seconds + * @return the propagator corresponding to the navigation message + * @see #getPropagator(double, DataContext) + * @see #getPropagator(double, DataContext, AttitudeProvider, Frame, double) + * @since 12.0 + */ + @DefaultDataContext + public GLONASSNumericalPropagator getPropagator(final double step) { + return new GLONASSNumericalPropagatorBuilder(new ClassicalRungeKuttaIntegrator(step), + this, isAccAvailable()).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + *

        The attitude provider is set by default to EME2000 aligned in the + * default data context.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The data context is by default to the + * {@link DataContext#getDefault() default data context}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame} in the default data + * context.
        + *

        + * @param step integration step in seconds + * @param context data context + * @return the propagator corresponding to the navigation message + * @see #getPropagator(double) + * @see #getPropagator(double, DataContext, AttitudeProvider, Frame, double) + * @since 12.0 + */ + public GLONASSNumericalPropagator getPropagator(final double step, final DataContext context) { + return new GLONASSNumericalPropagatorBuilder(new ClassicalRungeKuttaIntegrator(step), + this, isAccAvailable(), context).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + * @param step integration step in seconds + * @param context data context + * @param provider attitude provider + * @param inertial inertial frame, use to provide the propagated orbit + * @param mass spacecraft mass in kg + * @return the propagator corresponding to the navigation message + * @see #getPropagator(double) + * @see #getPropagator(double, DataContext) + * @since 12.0 + */ + public GLONASSNumericalPropagator getPropagator(final double step, final DataContext context, + final AttitudeProvider provider, final Frame inertial, + final double mass) { + return new GLONASSNumericalPropagatorBuilder(new ClassicalRungeKuttaIntegrator(step), + this, isAccAvailable(), context).attitudeProvider(provider) + .eci(inertial) + .mass(mass) + .build(); + } + /** {@inheritDoc} */ @Override public double getTN() { @@ -98,4 +197,80 @@ public void setTime(final double time) { this.time = time; } + /** Get status flags. + * @return status flags + * @since 12.0 + */ + public int getStatusFlags() { + return statusFlags; + } + + /** Set status flag. + * @param statusFlags status flag (parsed as a double) + * @since 12.0 + */ + public void setStatusFlags(final double statusFlags) { + this.statusFlags = (int) FastMath.rint(statusFlags); + } + + /** Set health flag. + * @param healthFlags health flag (parsed as a double) + * @since 12.0 + */ + public void setHealthFlags(final double healthFlags) { + this.healthFlags = Double.isNaN(healthFlags) ? 15 : (int) FastMath.rint(healthFlags); + } + + /** Get health flags. + * @return health flags + * @since 12.0 + */ + public int getHealthFlags() { + return healthFlags; + } + + /** Get group delay difference. + * @return group delay difference + * @since 12.0 + */ + public double getGroupDelayDifference() { + return groupDelayDifference; + } + + /** Set group delay difference. + * @param groupDelayDifference group delay difference + * @since 12.0 + */ + public void setGroupDelayDifference(final double groupDelayDifference) { + this.groupDelayDifference = Double.isNaN(groupDelayDifference) ? + 0.999999999999e+09 : + groupDelayDifference; + } + + /** + * Getter for the user range accuray (meters). + * @return the user range accuracy + * @since 12.0 + */ + public double getURA() { + return ura; + } + + /** + * Setter for the user range accuracy. + * @param accuracy the value to set + * @since 12.0 + */ + public void setURA(final double accuracy) { + this.ura = accuracy; + } + + /** + * Check if the acceleration is available in the navigation message. + * @return true if the acceleration is available + */ + private boolean isAccAvailable() { + return getXDotDot() != 0.0 || getYDotDot() != 0.0 || getZDotDot() != 0.0; + } + } diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSOrbitalElements.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSOrbitalElements.java index 563d57d486..f8da76062c 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSOrbitalElements.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GLONASSOrbitalElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSClockElements.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSClockElements.java index 5409a11130..81295f3451 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSClockElements.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSClockElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSConstants.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSConstants.java index 1e22214d54..6adc21da9e 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSConstants.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSConstants.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSOrbitalElements.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSOrbitalElements.java index 04274798d4..896125bfff 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSOrbitalElements.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GNSSOrbitalElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,13 @@ */ package org.orekit.propagation.analytical.gnss.data; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.frames.Frames; import org.orekit.propagation.analytical.gnss.GNSSPropagator; +import org.orekit.propagation.analytical.gnss.GNSSPropagatorBuilder; import org.orekit.time.TimeStamped; /** This interface provides the minimal set of orbital elements needed by the {@link GNSSPropagator}. @@ -173,4 +179,73 @@ public interface GNSSOrbitalElements extends TimeStamped { */ double getCycleDuration(); + /** + * Get the propagator corresponding to the navigation message. + *

        + * The attitude provider is set by default to be aligned with the EME2000 frame.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame} in the default data + * context.
        + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP + * CIO/2010-based ITRF simple EOP} in the default data context. + *

        + * This constructor uses the {@link DataContext#getDefault() default data context} + *

        + * @return the propagator corresponding to the navigation message + * @see #getPropagator(Frames) + * @see #getPropagator(Frames, AttitudeProvider, Frame, Frame, double) + * @since 12.0 + */ + @DefaultDataContext + default GNSSPropagator getPropagator() { + return new GNSSPropagatorBuilder(this).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + *

        + * The attitude provider is set by default to be aligned with the EME2000 frame.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame} in the default data + * context.
        + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP + * CIO/2010-based ITRF simple EOP} in the default data context. + *

        + * @param frames set of frames to use + * @return the propagator corresponding to the navigation message + * @see #getPropagator() + * @see #getPropagator(Frames, AttitudeProvider, Frame, Frame, double) + * @since 12.0 + */ + default GNSSPropagator getPropagator(final Frames frames) { + return new GNSSPropagatorBuilder(this, frames).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + * @param frames set of frames to use + * @param provider attitude provider + * @param inertial inertial frame, use to provide the propagated orbit + * @param bodyFixed body fixed frame, corresponding to the navigation message + * @param mass spacecraft mass in kg + * @return the propagator corresponding to the navigation message + * @see #getPropagator() + * @see #getPropagator(Frames) + * @since 12.0 + */ + default GNSSPropagator getPropagator(final Frames frames, final AttitudeProvider provider, + final Frame inertial, final Frame bodyFixed, final double mass) { + return new GNSSPropagatorBuilder(this, frames).attitudeProvider(provider) + .eci(inertial) + .ecef(bodyFixed) + .mass(mass) + .build(); + } + } diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSAlmanac.java index 17c48cbfc2..1b6ecf9257 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,7 +27,7 @@ * @since 8.0 * */ -public class GPSAlmanac extends AbstractAlmanac implements GNSSOrbitalElements, GNSSClockElements { +public class GPSAlmanac extends AbstractAlmanac implements GNSSClockElements { /** Source of the almanac. */ private String src; diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSCivilianNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSCivilianNavigationMessage.java new file mode 100644 index 0000000000..5bb78dd977 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSCivilianNavigationMessage.java @@ -0,0 +1,33 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +/** + * Container for data contained in a GPS navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class GPSCivilianNavigationMessage extends CivilianNavigationMessage { + + /** Constructor. + * @param cnv2 indicator for CNV2 messages + */ + public GPSCivilianNavigationMessage(final boolean cnv2) { + super(cnv2, GNSSConstants.GPS_MU, GNSSConstants.GPS_AV, GNSSConstants.GPS_WEEK_NB); + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSLegacyNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSLegacyNavigationMessage.java new file mode 100644 index 0000000000..db7d19972c --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSLegacyNavigationMessage.java @@ -0,0 +1,31 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +/** + * Container for data contained in a GPS navigation message. + * @author Bryan Cazabonne + * @since 11.0 + */ +public class GPSLegacyNavigationMessage extends LegacyNavigationMessage { + + /** Constructor. */ + public GPSLegacyNavigationMessage() { + super(GNSSConstants.GPS_MU, GNSSConstants.GPS_AV, GNSSConstants.GPS_WEEK_NB); + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoAlmanac.java index dda0aafc17..3ef946d3cd 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,7 @@ * @since 10.0 * */ -public class GalileoAlmanac extends AbstractAlmanac implements GNSSOrbitalElements { +public class GalileoAlmanac extends AbstractAlmanac { /** Nominal inclination (Ref: Galileo ICD - Table 75). */ private static final double I0 = FastMath.toRadians(56.0); diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoNavigationMessage.java index bdd28f836e..9e360a6696 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/GalileoNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,11 +21,16 @@ * @author Bryan Cazabonne * @since 11.0 */ -public class GalileoNavigationMessage extends AbstractNavigationMessage implements GNSSOrbitalElements { +public class GalileoNavigationMessage extends AbstractNavigationMessage { /** Issue of Data of the navigation batch. */ private int iodNav; + /** Data source. + * @since 12.0 + */ + private int dataSource; + /** E1/E5a broadcast group delay (s). */ private double bgbE1E5a; @@ -55,9 +60,26 @@ public int getIODNav() { * Setter for the Issue of Data of the navigation batch. * @param iod the IOD to set */ - public void setIODNav(final double iod) { - // The value is given as a floating number in the navigation message - this.iodNav = (int) iod; + public void setIODNav(final int iod) { + this.iodNav = iod; + } + + /** + * Getter for the the data source. + * @return the data source + * @since 12.0 + */ + public int getDataSource() { + return dataSource; + } + + /** + * Setter for the data source. + * @param dataSource data source + * @since 12.0 + */ + public void setDataSource(final int dataSource) { + this.dataSource = dataSource; } /** @@ -124,5 +146,4 @@ public void setSvHealth(final double svHealth) { this.svHealth = svHealth; } - } diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSAlmanac.java index 82b461143d..99937dc0aa 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,7 +26,7 @@ * @since 10.1 * */ -public class IRNSSAlmanac extends AbstractAlmanac implements GNSSOrbitalElements { +public class IRNSSAlmanac extends AbstractAlmanac { /** * Constructor. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSNavigationMessage.java index f4397f66ee..a73691fbd1 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/IRNSSNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ * @author Bryan Cazabonne * @since 11.0 */ -public class IRNSSNavigationMessage extends AbstractNavigationMessage implements GNSSOrbitalElements { +public class IRNSSNavigationMessage extends AbstractNavigationMessage { /** Issue of Data, Ephemeris and Clock. */ private int iodec; diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/LegacyNavigationMessage.java similarity index 67% rename from src/main/java/org/orekit/propagation/analytical/gnss/data/GPSNavigationMessage.java rename to src/main/java/org/orekit/propagation/analytical/gnss/data/LegacyNavigationMessage.java index 73e0e45e3d..202672e544 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/GPSNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/LegacyNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,11 +17,14 @@ package org.orekit.propagation.analytical.gnss.data; /** - * Container for data contained in a GPS navigation message. + * Container for data contained in a GPS/QZNSS legacy navigation message. * @author Bryan Cazabonne * @since 11.0 */ -public class GPSNavigationMessage extends AbstractNavigationMessage implements GNSSOrbitalElements, GNSSClockElements { +public class LegacyNavigationMessage extends AbstractNavigationMessage implements GNSSClockElements { + + /** Identifier for message type. */ + public static final String LNAV = "LNAV"; /** Issue of Data, Ephemeris. */ private int iode; @@ -36,11 +39,23 @@ public class GPSNavigationMessage extends AbstractNavigationMessage implements G private double svAccuracy; /** Satellite health status. */ - private double svHealth; + private int svHealth; + + /** Fit interval. + * @since 12.0 + */ + private int fitInterval; - /** Constructor. */ - public GPSNavigationMessage() { - super(GNSSConstants.GPS_MU, GNSSConstants.GPS_AV, GNSSConstants.GPS_WEEK_NB); + /** + * Constructor. + * @param mu Earth's universal gravitational parameter + * @param angularVelocity mean angular velocity of the Earth for the GNSS model + * @param weekNumber number of weeks in the GNSS cycle + */ + protected LegacyNavigationMessage(final double mu, + final double angularVelocity, + final int weekNumber) { + super(mu, angularVelocity, weekNumber); } /** @@ -72,9 +87,8 @@ public int getIODC() { * Setter for the Issue of Data Clock. * @param value the IODC to set */ - public void setIODC(final double value) { - // The value is given as a floating number in the navigation message - this.iodc = (int) value; + public void setIODC(final int value) { + this.iodc = value; } /** @@ -113,7 +127,7 @@ public void setSvAccuracy(final double svAccuracy) { * Getter for the satellite health status. * @return the satellite health status */ - public double getSvHealth() { + public int getSvHealth() { return svHealth; } @@ -121,8 +135,26 @@ public double getSvHealth() { * Setter for the satellite health status. * @param svHealth the value to set */ - public void setSvHealth(final double svHealth) { + public void setSvHealth(final int svHealth) { this.svHealth = svHealth; } + /** + * Getter for the fit interval. + * @return the fit interval + * @since 12.0 + */ + public int getFitInterval() { + return fitInterval; + } + + /** + * Setter for the fit interval. + * @param fitInterval fit interval + * @since 12.0 + */ + public void setFitInterval(final int fitInterval) { + this.fitInterval = fitInterval; + } + } diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSAlmanac.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSAlmanac.java index e980f0ed92..a5db3e1e27 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSAlmanac.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSAlmanac.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ * @since 10.0 * */ -public class QZSSAlmanac extends AbstractAlmanac implements GNSSOrbitalElements { +public class QZSSAlmanac extends AbstractAlmanac { /** Source of the almanac. */ private String src; diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSCivilianNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSCivilianNavigationMessage.java new file mode 100644 index 0000000000..9939540b6b --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSCivilianNavigationMessage.java @@ -0,0 +1,33 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +/** + * Container for data contained in a QZSS navigation message. + * @author Luc Maisonobe + * @since 12.0 + */ +public class QZSSCivilianNavigationMessage extends CivilianNavigationMessage { + + /** Constructor. + * @param cnv2 indicator for CNV2 messages + */ + public QZSSCivilianNavigationMessage(final boolean cnv2) { + super(cnv2, GNSSConstants.GPS_MU, GNSSConstants.GPS_AV, GNSSConstants.GPS_WEEK_NB); + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSLegacyNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSLegacyNavigationMessage.java new file mode 100644 index 0000000000..5ddf5c38e1 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSLegacyNavigationMessage.java @@ -0,0 +1,31 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.gnss.data; + +/** + * Container for data contained in a QZSS navigation message. + * @author Bryan Cazabonne + * @since 11.0 + */ +public class QZSSLegacyNavigationMessage extends LegacyNavigationMessage { + + /** Constructor. */ + public QZSSLegacyNavigationMessage() { + super(GNSSConstants.QZSS_MU, GNSSConstants.QZSS_AV, GNSSConstants.QZSS_WEEK_NB); + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSNavigationMessage.java deleted file mode 100644 index d2c40fb369..0000000000 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/QZSSNavigationMessage.java +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.analytical.gnss.data; - -/** - * Container for data contained in a QZSS navigation message. - * @author Bryan Cazabonne - * @since 11.0 - */ -public class QZSSNavigationMessage extends AbstractNavigationMessage implements GNSSOrbitalElements { - - /** Issue of Data, Ephemeris. */ - private int iode; - - /** Issue of Data, Clock. */ - private int iodc; - - /** Group Delay Differential (s). */ - private double tgd; - - /** The user SV accuracy (m). */ - private double svAccuracy; - - /** Satellite health status. */ - private double svHealth; - - /** Constructor. */ - public QZSSNavigationMessage() { - super(GNSSConstants.QZSS_MU, GNSSConstants.QZSS_AV, GNSSConstants.QZSS_WEEK_NB); - } - - /** - * Getter for the Issue Of Data Ephemeris (IODE). - * @return the Issue Of Data Ephemeris (IODE) - */ - public int getIODE() { - return iode; - } - - /** - * Setter for the Issue of Data, Ephemeris. - * @param value the IODE to set - */ - public void setIODE(final double value) { - // The value is given as a floating number in the navigation message - this.iode = (int) value; - } - - /** - * Getter for the Issue Of Data Clock (IODC). - * @return the Issue Of Data Clock (IODC) - */ - public int getIODC() { - return iodc; - } - - /** - * Setter for the Issue of Data, Clock. - * @param value the IODC to set - */ - public void setIODC(final double value) { - // The value is given as a floating number in the navigation message - this.iodc = (int) value; - } - - /** - * Getter for the Group Delay Differential (s). - * @return the Group Delay Differential in seconds - */ - public double getTGD() { - return tgd; - } - - /** - * Setter for the Group Delay Differential (s). - * @param time the group delay differential to set - */ - public void setTGD(final double time) { - this.tgd = time; - } - - /** - * Getter for the user SV accuray (meters). - * @return the user SV accuracy - */ - public double getSvAccuracy() { - return svAccuracy; - } - - /** - * Setter for the user SV accuracy. - * @param svAccuracy the value to set - */ - public void setSvAccuracy(final double svAccuracy) { - this.svAccuracy = svAccuracy; - } - - /** - * Getter for the satellite health status. - * @return the satellite health status - */ - public double getSvHealth() { - return svHealth; - } - - /** - * Setter for the satellite health status. - * @param svHealth the value to set - */ - public void setSvHealth(final double svHealth) { - this.svHealth = svHealth; - } - -} diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASNavigationMessage.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASNavigationMessage.java index 63abd3e1b3..c3717a0694 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASNavigationMessage.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASNavigationMessage.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,14 @@ */ package org.orekit.propagation.analytical.gnss.data; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.frames.Frames; +import org.orekit.propagation.analytical.gnss.SBASPropagator; +import org.orekit.propagation.analytical.gnss.SBASPropagatorBuilder; + /** * Container for data contained in a SBAS navigation message. * @author Bryan Cazabonne @@ -43,6 +51,76 @@ public SBASNavigationMessage() { // Nothing to do ... } + /** + * Get the propagator corresponding to the navigation message. +

        The attitude provider is set by default be aligned with the EME2000 frame.
        + * The Earth gravity coefficient is set by default to the + * {@link org.orekit.propagation.analytical.gnss.data.GNSSConstants#SBAS_MU SBAS_MU}.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame}.
        + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP CIO/2010-based ITRF simple EOP}. + *

        + * This constructor uses the {@link DataContext#getDefault() default data context} + *

        + * @return the propagator corresponding to the navigation message + * @see #getPropagator(Frames) + * @see #getPropagator(Frames, AttitudeProvider, Frame, Frame, double, double) + * @since 12.0 + */ + @DefaultDataContext + public SBASPropagator getPropagator() { + return new SBASPropagatorBuilder(this).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + *

        The attitude provider is set by default be aligned with the EME2000 frame.
        + * The Earth gravity coefficient is set by default to the + * {@link org.orekit.propagation.analytical.gnss.data.GNSSConstants#SBAS_MU SBAS_MU}.
        + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
        + * The ECI frame is set by default to the + * {@link org.orekit.frames.Predefined#EME2000 EME2000 frame}.
        + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP CIO/2010-based ITRF simple EOP}. + *

        + * @param frames set of frames to use + * @return the propagator corresponding to the navigation message + * @see #getPropagator() + * @see #getPropagator(Frames, AttitudeProvider, Frame, Frame, double, double) + * @since 12.0 + */ + public SBASPropagator getPropagator(final Frames frames) { + return new SBASPropagatorBuilder(this, frames).build(); + } + + /** + * Get the propagator corresponding to the navigation message. + * @param frames set of frames to use + * @param provider attitude provider + * @param inertial inertial frame, use to provide the propagated orbit + * @param bodyFixed body fixed frame, corresponding to the navigation message + * @param mass spacecraft mass in kg + * @param mu central attraction coefficient + * @return the propagator corresponding to the navigation message + * @see #getPropagator() + * @see #getPropagator(Frames) + * @since 12.0 + */ + public SBASPropagator getPropagator(final Frames frames, final AttitudeProvider provider, + final Frame inertial, final Frame bodyFixed, + final double mass, final double mu) { + return new SBASPropagatorBuilder(this, frames).attitudeProvider(provider) + .eci(inertial) + .ecef(bodyFixed) + .mass(mass) + .mu(mu) + .build(); + } + /** {@inheritDoc} */ @Override public int getWeek() { diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASOrbitalElements.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASOrbitalElements.java index 8f5251bb91..c6e89cd61a 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASOrbitalElements.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/SBASOrbitalElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/data/package-info.java b/src/main/java/org/orekit/propagation/analytical/gnss/data/package-info.java index 8dfa0da74d..3ed527647c 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/data/package-info.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/data/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/package-info.java b/src/main/java/org/orekit/propagation/analytical/gnss/package-info.java index 862af87ee6..344913622e 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/package-info.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/package-info.java b/src/main/java/org/orekit/propagation/analytical/package-info.java index 9d33570d51..d89bbfa0ec 100644 --- a/src/main/java/org/orekit/propagation/analytical/package-info.java +++ b/src/main/java/org/orekit/propagation/analytical/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/tle/DeepSDP4.java b/src/main/java/org/orekit/propagation/analytical/tle/DeepSDP4.java index d45ab0de2b..abaf56a658 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/DeepSDP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/DeepSDP4.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java index f71acb4e27..3faa765d39 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -43,6 +43,7 @@ * @author Fabien Maussion (java translation) * @author Thomas Paulet (field translation) * @since 11.0 + * @param type of the field elements */ public class FieldDeepSDP4> extends FieldSDP4 { diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java index cab4b31892..994972d64c 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -39,6 +39,7 @@ * @author Fabien Maussion (java translation) * @author Thomas Paulet (field translation) * @since 11.0 + * @param type of the field elements */ abstract class FieldSDP4> extends FieldTLEPropagator { diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java index efbb3f0137..ab24f562b7 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -37,6 +37,7 @@ * @author Fabien Maussion (java translation) * @author Thomas Paulet (field translation) * @since 11.0 + * @param type of the field elements */ public class FieldSGP4> extends FieldTLEPropagator { diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java index c217951631..876d08b660 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,31 +26,25 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.util.ArithmeticUtils; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; import org.hipparchus.util.MathUtils; import org.orekit.annotation.DefaultDataContext; -import org.orekit.attitudes.InertialProvider; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; -import org.orekit.frames.Frame; -import org.orekit.orbits.FieldEquinoctialOrbit; -import org.orekit.orbits.FieldKeplerianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.analytical.tle.generation.TleGenerationAlgorithm; import org.orekit.time.DateComponents; import org.orekit.time.DateTimeComponents; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeStamped; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; +import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** This class is a container for a single set of TLE data. * @@ -69,8 +63,9 @@ * @author Luc Maisonobe * @author Thomas Paulet (field translation) * @since 11.0 + * @param type of the field elements */ -public class FieldTLE> implements FieldTimeStamped, Serializable { +public class FieldTLE> implements FieldTimeStamped, Serializable, ParameterDriversProvider { /** Identifier for default type of ephemeris (SGP4/SDP4). */ public static final int DEFAULT = 0; @@ -93,12 +88,6 @@ public class FieldTLE> implements FieldTimeSta /** Parameter name for B* coefficient. */ public static final String B_STAR = "BSTAR"; - /** Default value for epsilon. */ - private static final double EPSILON_DEFAULT = 1.0e-10; - - /** Default value for maxIterations. */ - private static final int MAX_ITERATIONS_DEFAULT = 100; - /** B* scaling factor. *

        * We use a power of 2 to avoid numeric noise introduction @@ -190,7 +179,7 @@ public class FieldTLE> implements FieldTimeSta * DataContext#getDefault() default data context}. * *

        The static method {@link #isFormatOK(String, String)} should be called - * before trying to build this object.

        + * before trying to build this object.

        * @param field field utilized by default * @param line1 the first element (69 char String) * @param line2 the second element (69 char String) @@ -206,7 +195,7 @@ public FieldTLE(final Field field, final String line1, final String line2) { *

        This method uses the {@link DataContext#getDefault() default data context}. * *

        The static method {@link #isFormatOK(String, String)} should be called - * before trying to build this object.

        + * before trying to build this object.

        * @param field field utilized by default * @param line1 the first element (69 char String) * @param line2 the second element (69 char String) @@ -471,12 +460,17 @@ private void buildLine1() { buffer.append(ParseUtils.addPadding("launchPiece", launchPiece, ' ', 3, false, satelliteNumber)); buffer.append(' '); - final DateTimeComponents dtc = epoch.getComponents(utc); + DateTimeComponents dtc = epoch.getComponents(utc); + int fraction = (int) FastMath.rint(31250 * dtc.getTime().getSecondsInUTCDay() / 27.0); + if (fraction >= 100000000) { + dtc = epoch.shiftedBy(Constants.JULIAN_DAY).getComponents(utc); + fraction -= 100000000; + } buffer.append(ParseUtils.addPadding("year", dtc.getDate().getYear() % 100, '0', 2, true, satelliteNumber)); buffer.append(ParseUtils.addPadding("day", dtc.getDate().getDayOfYear(), '0', 3, true, satelliteNumber)); buffer.append('.'); // nota: 31250/27 == 100000000/86400 - final int fraction = (int) FastMath.rint(31250 * dtc.getTime().getSecondsInUTCDay() / 27.0); + buffer.append(ParseUtils.addPadding("fraction", fraction, '0', 8, true, satelliteNumber)); buffer.append(' '); @@ -573,27 +567,15 @@ private void buildLine2() { } - /** Get the drivers for TLE propagation SGP4 and SDP4. + /** {@inheritDoc}. + *

        Get the drivers for TLE propagation SGP4 and SDP4. * @return drivers for SGP4 and SDP4 model parameters */ + @Override public List getParametersDrivers() { return Collections.singletonList(bStarParameterDriver); } - /** Get model parameters. - * @param field field to which the elements belong - * @return model parameters - */ - public T[] getParameters(final Field field) { - final List drivers = getParametersDrivers(); - final T[] parameters = MathArrays.buildArray(field, drivers.size()); - int i = 0; - for (ParameterDriver driver : drivers) { - parameters[i++] = field.getZero().add(driver.getValue()); - } - return parameters; - } - /** Get the satellite id. * @return the satellite number */ @@ -718,7 +700,7 @@ public int getRevolutionNumberAtEpoch() { * @return bStar */ public double getBStar() { - return bStarParameterDriver.getValue(); + return bStarParameterDriver.getValue(getDate().toAbsoluteDate()); } /** Get a string representation of this TLE set. @@ -736,196 +718,19 @@ public String toString() { /** * Convert Spacecraft State into TLE. - * This converter uses Newton method to reverse SGP4 and SDP4 propagation algorithm - * and generates a usable TLE version of a state. - * New TLE epoch is state epoch. - * - *

        - * This method uses the {@link DataContext#getDefault() default data context}, - * as well as {@link #EPSILON_DEFAULT} and {@link #MAX_ITERATIONS_DEFAULT} for method convergence. * * @param state Spacecraft State to convert into TLE * @param templateTLE first guess used to get identification and estimate new TLE + * @param generationAlgorithm TLE generation algorithm * @param type of the element - * @return TLE matching with Spacecraft State and template identification - * @see #stateToTLE(FieldSpacecraftState, FieldTLE, TimeScale, Frame) - * @see #stateToTLE(FieldSpacecraftState, FieldTLE, TimeScale, Frame, double, int) - * @since 11.0 - */ - @DefaultDataContext - public static > FieldTLE stateToTLE(final FieldSpacecraftState state, final FieldTLE templateTLE) { - return stateToTLE(state, templateTLE, - DataContext.getDefault().getTimeScales().getUTC(), - DataContext.getDefault().getFrames().getTEME()); - } - - /** - * Convert Spacecraft State into TLE. - * This converter uses Newton method to reverse SGP4 and SDP4 propagation algorithm - * and generates a usable TLE version of a state. - * New TLE epoch is state epoch. - * - *

        - * This method uses {@link #EPSILON_DEFAULT} and {@link #MAX_ITERATIONS_DEFAULT} - * for method convergence. - * - * @param state Spacecraft State to convert into TLE - * @param templateTLE first guess used to get identification and estimate new TLE - * @param utc the UTC time scale - * @param teme the TEME frame to use for propagation - * @param type of the element - * @return TLE matching with Spacecraft State and template identification - * @see #stateToTLE(FieldSpacecraftState, FieldTLE, TimeScale, Frame, double, int) - * @since 11.0 + * @return a generated TLE + * @since 12.0 */ public static > FieldTLE stateToTLE(final FieldSpacecraftState state, final FieldTLE templateTLE, - final TimeScale utc, final Frame teme) { - return stateToTLE(state, templateTLE, utc, teme, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); + final TleGenerationAlgorithm generationAlgorithm) { + return generationAlgorithm.generate(state, templateTLE); } - /** - * Convert Spacecraft State into TLE. - * This converter uses Newton method to reverse SGP4 and SDP4 propagation algorithm - * and generates a usable TLE version of a state. - * New TLE epoch is state epoch. - * - * @param state Spacecraft State to convert into TLE - * @param templateTLE first guess used to get identification and estimate new TLE - * @param utc the UTC time scale - * @param teme the TEME frame to use for propagation - * @param epsilon used to compute threshold for convergence check - * @param maxIterations maximum number of iterations for convergence - * @param type of the element - * @return TLE matching with Spacecraft State and template identification - * @since 11.0 - */ - public static > FieldTLE stateToTLE(final FieldSpacecraftState state, final FieldTLE templateTLE, - final TimeScale utc, final Frame teme, - final double epsilon, final int maxIterations) { - - // Gets equinoctial parameters in TEME frame from state - final FieldEquinoctialOrbit equiOrbit = convert(state.getOrbit(), teme); - T sma = equiOrbit.getA(); - T ex = equiOrbit.getEquinoctialEx(); - T ey = equiOrbit.getEquinoctialEy(); - T hx = equiOrbit.getHx(); - T hy = equiOrbit.getHy(); - T lv = equiOrbit.getLv(); - - // Rough initialization of the TLE - final FieldKeplerianOrbit keplerianOrbit = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(equiOrbit); - FieldTLE current = newTLE(keplerianOrbit, templateTLE, utc); - - // Field - final Field field = state.getDate().getField(); - - // threshold for each parameter - final T thrA = sma.add(1).multiply(epsilon); - final T thrE = FastMath.hypot(ex, ey).add(1).multiply(epsilon); - final T thrH = FastMath.hypot(hx, hy).add(1).multiply(epsilon); - final T thrV = sma.getPi().multiply(epsilon); - - int k = 0; - while (k++ < maxIterations) { - - // recompute the state from the current TLE - final FieldTLEPropagator propagator = FieldTLEPropagator.selectExtrapolator(current, new InertialProvider(Rotation.IDENTITY, teme), state.getMass(), teme, templateTLE.getParameters(field)); - final FieldOrbit recovOrbit = propagator.getInitialState().getOrbit(); - final FieldEquinoctialOrbit recovEquiOrbit = (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recovOrbit); - - // adapted parameters residuals - final T deltaSma = equiOrbit.getA().subtract(recovEquiOrbit.getA()); - final T deltaEx = equiOrbit.getEquinoctialEx().subtract(recovEquiOrbit.getEquinoctialEx()); - final T deltaEy = equiOrbit.getEquinoctialEy().subtract(recovEquiOrbit.getEquinoctialEy()); - final T deltaHx = equiOrbit.getHx().subtract(recovEquiOrbit.getHx()); - final T deltaHy = equiOrbit.getHy().subtract(recovEquiOrbit.getHy()); - final T deltaLv = MathUtils.normalizeAngle(equiOrbit.getLv().subtract(recovEquiOrbit.getLv()), field.getZero()); - - // check convergence - if (FastMath.abs(deltaSma.getReal()) < thrA.getReal() && - FastMath.abs(deltaEx.getReal()) < thrE.getReal() && - FastMath.abs(deltaEy.getReal()) < thrE.getReal() && - FastMath.abs(deltaHx.getReal()) < thrH.getReal() && - FastMath.abs(deltaHy.getReal()) < thrH.getReal() && - FastMath.abs(deltaLv.getReal()) < thrV.getReal()) { - - return current; - } - - // update state - sma = sma.add(deltaSma); - ex = ex.add(deltaEx); - ey = ey.add(deltaEy); - hx = hx.add(deltaHx); - hy = hy.add(deltaHy); - lv = lv.add(deltaLv); - final FieldEquinoctialOrbit newEquiOrbit = - new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv, PositionAngle.TRUE, - equiOrbit.getFrame(), equiOrbit.getDate(), equiOrbit.getMu()); - final FieldKeplerianOrbit newKeplOrbit = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquiOrbit); - - // update TLE - current = newTLE(newKeplOrbit, templateTLE, utc); - } - - throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k); - } - - /** - * Converts an orbit into an equinoctial orbit expressed in TEME frame. - * - * @param orbitIn the orbit to convert - * @param teme the TEME frame to use for propagation - * @param type of the element - * @return the converted orbit, i.e. equinoctial in TEME frame - */ - private static > FieldEquinoctialOrbit convert(final FieldOrbit orbitIn, final Frame teme) { - return new FieldEquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu()); - } - - /** - * Builds a new TLE from Keplerian parameters and a template for TLE data. - * @param keplerianOrbit the Keplerian parameters to build the TLE from - * @param templateTLE TLE used to get object identification - * @param utc the UTC time scale - * @param type of the element - * @return TLE with template identification and new orbital parameters - */ - private static > FieldTLE newTLE(final FieldKeplerianOrbit keplerianOrbit, final FieldTLE templateTLE, - final TimeScale utc) { - // Keplerian parameters - final T meanMotion = keplerianOrbit.getKeplerianMeanMotion(); - final T e = keplerianOrbit.getE(); - final T i = keplerianOrbit.getI(); - final T raan = keplerianOrbit.getRightAscensionOfAscendingNode(); - final T pa = keplerianOrbit.getPerigeeArgument(); - final T meanAnomaly = keplerianOrbit.getMeanAnomaly(); - // TLE epoch is state epoch - final FieldAbsoluteDate epoch = keplerianOrbit.getDate(); - // Identification - final int satelliteNumber = templateTLE.getSatelliteNumber(); - final char classification = templateTLE.getClassification(); - final int launchYear = templateTLE.getLaunchYear(); - final int launchNumber = templateTLE.getLaunchNumber(); - final String launchPiece = templateTLE.getLaunchPiece(); - final int ephemerisType = templateTLE.getEphemerisType(); - final int elementNumber = templateTLE.getElementNumber(); - // Updates revolutionNumberAtEpoch - final int revolutionNumberAtEpoch = templateTLE.getRevolutionNumberAtEpoch(); - final T dt = epoch.durationFrom(templateTLE.getDate()); - final int newRevolutionNumberAtEpoch = (int) ((int) revolutionNumberAtEpoch + FastMath.floor(MathUtils.normalizeAngle(meanAnomaly, e.getPi()).add(dt.multiply(meanMotion)).divide(e.getPi().multiply(2.0))).getReal()); - // Gets B* - final double bStar = templateTLE.getBStar(); - // Gets Mean Motion derivatives - final T meanMotionFirstDerivative = templateTLE.getMeanMotionFirstDerivative(); - final T meanMotionSecondDerivative = templateTLE.getMeanMotionSecondDerivative(); - // Returns the new TLE - return new FieldTLE<>(satelliteNumber, classification, launchYear, launchNumber, launchPiece, ephemerisType, - elementNumber, epoch, meanMotion, meanMotionFirstDerivative, meanMotionSecondDerivative, - e, i, pa, raan, meanAnomaly, newRevolutionNumberAtEpoch, bStar, utc); - } - - /** Check the lines format validity. * @param line1 the first element * @param line2 the second element diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java index 1c98ea9395..4dc6f28e68 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,16 +26,16 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FieldAttitude; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; -import org.orekit.frames.Frames; import org.orekit.orbits.FieldCartesianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator; +import org.orekit.propagation.analytical.tle.generation.TleGenerationAlgorithm; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.utils.FieldPVCoordinates; @@ -69,6 +69,7 @@ * @author Thomas Paulet (field translation) * @since 11.0 * @see FieldTLE + * @param type of the field elements */ public abstract class FieldTLEPropagator> extends FieldAbstractAnalyticalPropagator { @@ -231,11 +232,11 @@ protected FieldTLEPropagator(final FieldTLE initialTLE, * @param parameters SGP4 and SDP4 model parameters * @return the correct propagator. * @param elements type - * @see #selectExtrapolator(FieldTLE, Frames, CalculusFieldElement[]) + * @see #selectExtrapolator(FieldTLE, Frame, CalculusFieldElement[]) */ @DefaultDataContext public static > FieldTLEPropagator selectExtrapolator(final FieldTLE tle, final T[] parameters) { - return selectExtrapolator(tle, DataContext.getDefault().getFrames(), parameters); + return selectExtrapolator(tle, DataContext.getDefault().getFrames().getTEME(), parameters); } /** Selects the extrapolator to use with the selected TLE. @@ -243,17 +244,17 @@ public static > FieldTLEPropagator selectEx *

        This method uses the {@link DataContext#getDefault() default data context}. * * @param tle the TLE to propagate. - * @param frames set of Frames to use in the propagator. + * @param teme TEME frame. * @param parameters SGP4 and SDP4 model parameters * @return the correct propagator. * @param elements type */ - public static > FieldTLEPropagator selectExtrapolator(final FieldTLE tle, final Frames frames, final T[] parameters) { + public static > FieldTLEPropagator selectExtrapolator(final FieldTLE tle, final Frame teme, final T[] parameters) { return selectExtrapolator( tle, - InertialProvider.of(frames.getTEME()), + FrameAlignedProvider.of(teme), tle.getE().getField().getZero().add(DEFAULT_MASS), - frames.getTEME(), + teme, parameters); } @@ -586,7 +587,8 @@ public List getParametersDrivers() { public void resetInitialState(final FieldSpacecraftState state) { super.resetInitialState(state); super.setStartDate(state.getDate()); - final FieldTLE newTLE = FieldTLE.stateToTLE(state, tle, utc, teme); + final TleGenerationAlgorithm algorithm = TLEPropagator.getDefaultTleGenerationAlgorithm(utc, teme); + final FieldTLE newTLE = algorithm.generate(state, tle); this.tle = newTLE; initializeCommons(tle.getParameters(state.getDate().getField())); sxpInitialize(tle.getParameters(state.getDate().getField())); diff --git a/src/main/java/org/orekit/propagation/analytical/tle/ParseUtils.java b/src/main/java/org/orekit/propagation/analytical/tle/ParseUtils.java index bf3e1fa924..cc68437af3 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/ParseUtils.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/ParseUtils.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/analytical/tle/SDP4.java b/src/main/java/org/orekit/propagation/analytical/tle/SDP4.java index 8a5cc5a869..7d5afb7c3c 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/SDP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/SDP4.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -96,7 +96,7 @@ protected void sxpPropagate(final double tSince) { final double tempa = 1 - c1 * tSince; a = FastMath.pow(TLEConstants.XKE / xn, TLEConstants.TWO_THIRD) * tempa * tempa; - em -= tle.getBStar() * c4 * tSince; + em -= tle.getBStar(tle.getDate().shiftedBy(tSince)) * c4 * tSince; // Update for deep-space periodic effects xll += xn0dp * t2cof * tSinceSq; diff --git a/src/main/java/org/orekit/propagation/analytical/tle/SGP4.java b/src/main/java/org/orekit/propagation/analytical/tle/SGP4.java index 16e0ce7604..b0fb5b1b8b 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/SGP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/SGP4.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -137,7 +137,7 @@ protected void sxpPropagate(final double tSince) { final double tsq = tSince * tSince; xnode = xn0ddf + xnodcf * tsq; double tempa = 1 - c1 * tSince; - double tempe = tle.getBStar() * c4 * tSince; + double tempe = tle.getBStar(tle.getDate().shiftedBy(tSince)) * c4 * tSince; double templ = t2cof * tsq; if (!lessThan220) { @@ -150,7 +150,7 @@ protected void sxpPropagate(final double tSince) { final double tcube = tsq * tSince; final double tfour = tSince * tcube; tempa = tempa - d2 * tsq - d3 * tcube - d4 * tfour; - tempe = tempe + tle.getBStar() * c5 * (FastMath.sin(xmp) - sinM0); + tempe = tempe + tle.getBStar(tle.getDate().shiftedBy(tSince)) * c5 * (FastMath.sin(xmp) - sinM0); templ = templ + t3cof * tcube + tfour * (t4cof + tSince * t5cof); } diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLE.java b/src/main/java/org/orekit/propagation/analytical/tle/TLE.java index 0a20c57078..3e8c9eafa6 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLE.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLE.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,29 +25,24 @@ import java.util.Objects; import java.util.regex.Pattern; -import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.util.ArithmeticUtils; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.orekit.annotation.DefaultDataContext; -import org.orekit.attitudes.InertialProvider; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.frames.Frame; -import org.orekit.orbits.EquinoctialOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.generation.TleGenerationAlgorithm; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.DateTimeComponents; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeStamped; +import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** This class is a container for a single set of TLE data. * @@ -65,7 +60,7 @@ * @author Fabien Maussion * @author Luc Maisonobe */ -public class TLE implements TimeStamped, Serializable { +public class TLE implements TimeStamped, Serializable, ParameterDriversProvider { /** Identifier for SGP type of ephemeris. */ public static final int SGP = 1; @@ -88,12 +83,6 @@ public class TLE implements TimeStamped, Serializable { /** Parameter name for B* coefficient. */ public static final String B_STAR = "BSTAR"; - /** Default value for epsilon. */ - private static final double EPSILON_DEFAULT = 1.0e-10; - - /** Default value for maxIterations. */ - private static final int MAX_ITERATIONS_DEFAULT = 100; - /** B* scaling factor. *

        * We use a power of 2 to avoid numeric noise introduction @@ -196,7 +185,7 @@ public class TLE implements TimeStamped, Serializable { * DataContext#getDefault() default data context}. * *

        The static method {@link #isFormatOK(String, String)} should be called - * before trying to build this object.

        + * before trying to build this object.

        * @param line1 the first element (69 char String) * @param line2 the second element (69 char String) * @see #TLE(String, String, TimeScale) @@ -209,7 +198,7 @@ public TLE(final String line1, final String line2) { /** Simple constructor from unparsed two lines using the given time scale as UTC. * *

        The static method {@link #isFormatOK(String, String)} should be called - * before trying to build this object.

        + * before trying to build this object.

        * @param line1 the first element (69 char String) * @param line2 the second element (69 char String) * @param utc the UTC time scale. @@ -472,12 +461,17 @@ private void buildLine1() { buffer.append(ParseUtils.addPadding("launchPiece", launchPiece, ' ', 3, false, satelliteNumber)); buffer.append(' '); - final DateTimeComponents dtc = epoch.getComponents(utc); + DateTimeComponents dtc = epoch.getComponents(utc); + int fraction = (int) FastMath.rint(31250 * dtc.getTime().getSecondsInUTCDay() / 27.0); + if (fraction >= 100000000) { + dtc = epoch.shiftedBy(Constants.JULIAN_DAY).getComponents(utc); + fraction -= 100000000; + } buffer.append(ParseUtils.addPadding("year", dtc.getDate().getYear() % 100, '0', 2, true, satelliteNumber)); buffer.append(ParseUtils.addPadding("day", dtc.getDate().getDayOfYear(), '0', 3, true, satelliteNumber)); buffer.append('.'); // nota: 31250/27 == 100000000/86400 - final int fraction = (int) FastMath.rint(31250 * dtc.getTime().getSecondsInUTCDay() / 27.0); + buffer.append(ParseUtils.addPadding("fraction", fraction, '0', 8, true, satelliteNumber)); buffer.append(' '); @@ -692,11 +686,19 @@ public int getRevolutionNumberAtEpoch() { return revolutionNumberAtEpoch; } - /** Get the ballistic coefficient. + /** Get the ballistic coefficient at tle date. * @return bStar */ public double getBStar() { - return bStarParameterDriver.getValue(); + return bStarParameterDriver.getValue(getDate()); + } + + /** Get the ballistic coefficient at a specific date. + * @param date at which the ballistic coefficient wants to be known. + * @return bStar + */ + public double getBStar(final AbsoluteDate date) { + return bStarParameterDriver.getValue(date); } /** Get a string representation of this TLE set. @@ -710,196 +712,16 @@ public String toString() { /** * Convert Spacecraft State into TLE. - * This converter uses Fixed Point method to reverse SGP4 and SDP4 propagation algorithm - * and generates a usable TLE version of a state. - * Equinocital orbital parameters are used in order to get a stiff method. - * New TLE epoch is state epoch. - * - *

        - * This method uses the {@link DataContext#getDefault() default data context}, - * as well as {@link #EPSILON_DEFAULT} and {@link #MAX_ITERATIONS_DEFAULT} for method convergence. * * @param state Spacecraft State to convert into TLE * @param templateTLE first guess used to get identification and estimate new TLE - * @return TLE matching with Spacecraft State and template identification - * @see #stateToTLE(SpacecraftState, TLE, TimeScale, Frame) - * @see #stateToTLE(SpacecraftState, TLE, TimeScale, Frame, double, int) - * @since 11.0 - */ - @DefaultDataContext - public static TLE stateToTLE(final SpacecraftState state, final TLE templateTLE) { - return stateToTLE(state, templateTLE, - DataContext.getDefault().getTimeScales().getUTC(), - DataContext.getDefault().getFrames().getTEME()); - } - - /** - * Convert Spacecraft State into TLE. - * This converter uses Fixed Point method to reverse SGP4 and SDP4 propagation algorithm - * and generates a usable TLE version of a state. - * Equinocital orbital parameters are used in order to get a stiff method. - * New TLE epoch is state epoch. - * - *

        - * This method uses {@link #EPSILON_DEFAULT} and {@link #MAX_ITERATIONS_DEFAULT} - * for method convergence. - * - * @param state Spacecraft State to convert into TLE - * @param templateTLE first guess used to get identification and estimate new TLE - * @param utc the UTC time scale - * @param teme the TEME frame to use for propagation - * @return TLE matching with Spacecraft State and template identification - * @see #stateToTLE(SpacecraftState, TLE, TimeScale, Frame, double, int) - * @since 11.0 + * @param generationAlgorithm TLE generation algorithm + * @return a generated TLE + * @since 12.0 */ public static TLE stateToTLE(final SpacecraftState state, final TLE templateTLE, - final TimeScale utc, final Frame teme) { - return stateToTLE(state, templateTLE, utc, teme, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); - } - - /** - * Convert Spacecraft State into TLE. - * This converter uses Newton method to reverse SGP4 and SDP4 propagation algorithm - * and generates a usable TLE version of a state. - * New TLE epoch is state epoch. - * - * @param state Spacecraft State to convert into TLE - * @param templateTLE first guess used to get identification and estimate new TLE - * @param utc the UTC time scale - * @param teme the TEME frame to use for propagation - * @param epsilon used to compute threshold for convergence check - * @param maxIterations maximum number of iterations for convergence - * @return TLE matching with Spacecraft State and template identification - * @since 11.0 - */ - public static TLE stateToTLE(final SpacecraftState state, final TLE templateTLE, - final TimeScale utc, final Frame teme, - final double epsilon, final int maxIterations) { - - // Gets equinoctial parameters in TEME frame from state - final EquinoctialOrbit equiOrbit = convert(state.getOrbit(), teme); - double sma = equiOrbit.getA(); - double ex = equiOrbit.getEquinoctialEx(); - double ey = equiOrbit.getEquinoctialEy(); - double hx = equiOrbit.getHx(); - double hy = equiOrbit.getHy(); - double lv = equiOrbit.getLv(); - - // Rough initialization of the TLE - final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(equiOrbit); - TLE current = newTLE(keplerianOrbit, templateTLE, utc); - - // threshold for each parameter - final double thrA = epsilon * (1 + sma); - final double thrE = epsilon * (1 + FastMath.hypot(ex, ey)); - final double thrH = epsilon * (1 + FastMath.hypot(hx, hy)); - final double thrV = epsilon * FastMath.PI; - - int k = 0; - while (k++ < maxIterations) { - - // recompute the state from the current TLE - final TLEPropagator propagator = TLEPropagator.selectExtrapolator(current, new InertialProvider(Rotation.IDENTITY, teme), state.getMass(), teme); - final Orbit recovOrbit = propagator.getInitialState().getOrbit(); - final EquinoctialOrbit recovEquiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recovOrbit); - - // adapted parameters residuals - final double deltaSma = equiOrbit.getA() - recovEquiOrbit.getA(); - final double deltaEx = equiOrbit.getEquinoctialEx() - recovEquiOrbit.getEquinoctialEx(); - final double deltaEy = equiOrbit.getEquinoctialEy() - recovEquiOrbit.getEquinoctialEy(); - final double deltaHx = equiOrbit.getHx() - recovEquiOrbit.getHx(); - final double deltaHy = equiOrbit.getHy() - recovEquiOrbit.getHy(); - final double deltaLv = MathUtils.normalizeAngle(equiOrbit.getLv() - recovEquiOrbit.getLv(), 0.0); - - // check convergence - if (FastMath.abs(deltaSma) < thrA && - FastMath.abs(deltaEx) < thrE && - FastMath.abs(deltaEy) < thrE && - FastMath.abs(deltaHx) < thrH && - FastMath.abs(deltaHy) < thrH && - FastMath.abs(deltaLv) < thrV) { - - // Verify if parameters are estimated - for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) { - if (templateDrivers.isSelected()) { - // Set to selected for the new TLE - current.getParameterDriver(templateDrivers.getName()).setSelected(true); - } - } - - // Return - return current; - } - - // update state - sma += deltaSma; - ex += deltaEx; - ey += deltaEy; - hx += deltaHx; - hy += deltaHy; - lv += deltaLv; - final EquinoctialOrbit newEquiOrbit = - new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, PositionAngle.TRUE, - equiOrbit.getFrame(), equiOrbit.getDate(), equiOrbit.getMu()); - final KeplerianOrbit newKeplOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquiOrbit); - - // update TLE - current = newTLE(newKeplOrbit, templateTLE, utc); - } - - throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k); - } - - /** - * Converts an orbit into an equinoctial orbit expressed in TEME frame. - * - * @param orbitIn the orbit to convert - * @param teme the TEME frame to use for propagation - * @return the converted orbit, i.e. equinoctial in TEME frame - */ - private static EquinoctialOrbit convert(final Orbit orbitIn, final Frame teme) { - return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu()); - } - - /** - * Builds a new TLE from Keplerian parameters and a template for TLE data. - * @param keplerianOrbit the Keplerian parameters to build the TLE from - * @param templateTLE TLE used to get object identification - * @param utc the UTC time scale - * @return TLE with template identification and new orbital parameters - */ - private static TLE newTLE(final KeplerianOrbit keplerianOrbit, final TLE templateTLE, - final TimeScale utc) { - // Keplerian parameters - final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); - final double e = keplerianOrbit.getE(); - final double i = keplerianOrbit.getI(); - final double raan = keplerianOrbit.getRightAscensionOfAscendingNode(); - final double pa = keplerianOrbit.getPerigeeArgument(); - final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); - // TLE epoch is state epoch - final AbsoluteDate epoch = keplerianOrbit.getDate(); - // Identification - final int satelliteNumber = templateTLE.getSatelliteNumber(); - final char classification = templateTLE.getClassification(); - final int launchYear = templateTLE.getLaunchYear(); - final int launchNumber = templateTLE.getLaunchNumber(); - final String launchPiece = templateTLE.getLaunchPiece(); - final int ephemerisType = templateTLE.getEphemerisType(); - final int elementNumber = templateTLE.getElementNumber(); - // Updates revolutionNumberAtEpoch - final int revolutionNumberAtEpoch = templateTLE.getRevolutionNumberAtEpoch(); - final double dt = epoch.durationFrom(templateTLE.getDate()); - final int newRevolutionNumberAtEpoch = (int) (revolutionNumberAtEpoch + FastMath.floor((MathUtils.normalizeAngle(meanAnomaly, FastMath.PI) + dt * meanMotion) / (2 * FastMath.PI))); - // Gets B* - final double bStar = templateTLE.getBStar(); - // Gets Mean Motion derivatives - final double meanMotionFirstDerivative = templateTLE.getMeanMotionFirstDerivative(); - final double meanMotionSecondDerivative = templateTLE.getMeanMotionSecondDerivative(); - // Returns the new TLE - return new TLE(satelliteNumber, classification, launchYear, launchNumber, launchPiece, ephemerisType, - elementNumber, epoch, meanMotion, meanMotionFirstDerivative, meanMotionSecondDerivative, - e, i, pa, raan, meanAnomaly, newRevolutionNumberAtEpoch, bStar, utc); + final TleGenerationAlgorithm generationAlgorithm) { + return generationAlgorithm.generate(state, templateTLE); } /** Check the lines format validity. @@ -1023,33 +845,6 @@ public List getParametersDrivers() { return Collections.singletonList(bStarParameterDriver); } - /** Get parameter driver from its name. - * @param name parameter name - * @return parameter driver - * @since 11.1 - */ - public ParameterDriver getParameterDriver(final String name) { - // Loop on known drivers - for (final ParameterDriver driver : getParametersDrivers()) { - if (name.equals(driver.getName())) { - // we have found a parameter with that name - return driver; - } - } - - // build the list of supported parameters - final StringBuilder sBuilder = new StringBuilder(); - for (final ParameterDriver driver : getParametersDrivers()) { - if (sBuilder.length() > 0) { - sBuilder.append(", "); - } - sBuilder.append(driver.getName()); - } - throw new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, - name, sBuilder.toString()); - - } - /** Replace the instance with a data transfer object for serialization. * @return data transfer object that will be serialized */ diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEConstants.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEConstants.java index c8bbdc2a63..135a605c5d 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEConstants.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEConstants.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -40,59 +40,96 @@ public class TLEConstants { /** Time units per julian day. */ public static final double MINUTES_PER_DAY = 1440.0; - // CHECKSTYLE: stop JavadocVariable check // Potential perturbation coefficients + /** XKE. */ public static final double XKE = 0.0743669161331734132; // mu = 3.986008e+14; + /** XJ3. */ public static final double XJ3 = -2.53881e-6; + /** XJ2. */ public static final double XJ2 = 1.082616e-3; + /** XJ4. */ public static final double XJ4 = -1.65597e-6; + /** CK2. */ public static final double CK2 = 0.5 * XJ2 * NORMALIZED_EQUATORIAL_RADIUS * NORMALIZED_EQUATORIAL_RADIUS; + /** CK4. */ public static final double CK4 = -0.375 * XJ4 * NORMALIZED_EQUATORIAL_RADIUS * NORMALIZED_EQUATORIAL_RADIUS * NORMALIZED_EQUATORIAL_RADIUS * NORMALIZED_EQUATORIAL_RADIUS; + /** S. */ public static final double S = NORMALIZED_EQUATORIAL_RADIUS * (1. + 78. / EARTH_RADIUS); + /** QOMS2T. */ public static final double QOMS2T = 1.880279159015270643865e-9; + /** A3OVK2. */ public static final double A3OVK2 = -XJ3 / CK2 * NORMALIZED_EQUATORIAL_RADIUS * NORMALIZED_EQUATORIAL_RADIUS * NORMALIZED_EQUATORIAL_RADIUS; // Deep SDP4 constants + /** ZNS. */ public static final double ZNS = 1.19459E-5; + /** ZES. */ public static final double ZES = 0.01675; + /** ZNL. */ public static final double ZNL = 1.5835218E-4; + /** ZEL. */ public static final double ZEL = 0.05490; + /** THDT. */ public static final double THDT = 4.3752691E-3; + /** C1SS. */ public static final double C1SS = 2.9864797E-6; + /** C1L. */ public static final double C1L = 4.7968065E-7; + /** ROOT22. */ public static final double ROOT22 = 1.7891679E-6; + /** ROOT32. */ public static final double ROOT32 = 3.7393792E-7; + /** ROOT44. */ public static final double ROOT44 = 7.3636953E-9; + /** ROOT52. */ public static final double ROOT52 = 1.1428639E-7; + /** ROOT54. */ public static final double ROOT54 = 2.1765803E-9; + /** Q22. */ public static final double Q22 = 1.7891679E-6; + /** Q31. */ public static final double Q31 = 2.1460748E-6; + /** Q33. */ public static final double Q33 = 2.2123015E-7; + /** C_FASX2. */ public static final double C_FASX2 = 0.99139134268488593; + /** S_FASX2. */ public static final double S_FASX2 = 0.13093206501640101; + /** C_2FASX4. */ public static final double C_2FASX4 = 0.87051638752972937; + /** S_2FASX4. */ public static final double S_2FASX4 = -0.49213943048915526; + /** C_3FASX6. */ public static final double C_3FASX6 = 0.43258117585763334; + /** S_3FASX6. */ public static final double S_3FASX6 = 0.90159499016666422; + /** C_G22. */ public static final double C_G22 = 0.87051638752972937; + /** S_G22. */ public static final double S_G22 = -0.49213943048915526; + /** C_G32. */ public static final double C_G32 = 0.57972190187001149; + /** S_G32. */ public static final double S_G32 = 0.81481440616389245; + /** C_G44. */ public static final double C_G44 = -0.22866241528815548; + /** S_G44. */ public static final double S_G44 = 0.97350577801807991; + /** C_G52. */ public static final double C_G52 = 0.49684831179884198; + /** S_G52. */ public static final double S_G52 = 0.86783740128127729; + /** C_G54. */ public static final double C_G54 = -0.29695209575316894; + /** S_G54. */ public static final double S_G54 = -0.95489237761529999; - // CHECKSTYLE: resume JavadocVariable check - /** Earth gravity coefficient in m³/s². */ public static final double MU = XKE * XKE * EARTH_RADIUS * EARTH_RADIUS * EARTH_RADIUS * (1000 * 1000 * 1000) / (60 * 60); diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEGradientConverter.java index 166b0047a7..f732862eac 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,6 +25,7 @@ import org.orekit.propagation.analytical.AbstractAnalyticalGradientConverter; import org.orekit.time.TimeScale; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; /** Converter for TLE propagator. * @author Luc Maisonobe @@ -32,7 +33,7 @@ * @author Thomas Paulet * @since 11.0 */ -class TLEGradientConverter extends AbstractAnalyticalGradientConverter { +class TLEGradientConverter extends AbstractAnalyticalGradientConverter implements ParameterDriversProvider { /** Fixed dimension of the state. */ public static final int FREE_STATE_PARAMETERS = 6; @@ -78,7 +79,7 @@ public FieldTLEPropagator getPropagator(final FieldSpacecraftState templateTLE = new FieldTLE<>(satelliteNumber, classification, @@ -87,7 +88,7 @@ public FieldTLEPropagator getPropagator(final FieldSpacecraftState gTLE = FieldTLE.stateToTLE(state, templateTLE, utc, teme); + final FieldTLE gTLE = TLEPropagator.getDefaultTleGenerationAlgorithm(utc, teme).generate(state, templateTLE); // Return the "Field" propagator return FieldTLEPropagator.selectExtrapolator(gTLE, provider, state.getMass(), teme, parameters); diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEHarvester.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEHarvester.java index db489d16e7..4767a9d2f1 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEHarvester.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.propagation.analytical.tle; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.analytical.AbstractAnalyticalGradientConverter; import org.orekit.propagation.analytical.AbstractAnalyticalMatricesHarvester; import org.orekit.utils.DoubleArrayDictionary; @@ -36,7 +37,7 @@ class TLEHarvester extends AbstractAnalyticalMatricesHarvester { *

        * The arguments for initial matrices must be compatible with the * {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEJacobiansMapper.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEJacobiansMapper.java deleted file mode 100644 index cdbf087146..0000000000 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEJacobiansMapper.java +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.analytical.tle; - -import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.orbits.FieldOrbit; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.integration.AbstractJacobiansMapper; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.FieldPVCoordinates; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; - -/** Mapper between two-dimensional Jacobian matrices and one-dimensional {@link - * SpacecraftState#getAdditionalState(String) additional state arrays}. - *

        - * This class does not hold the states by itself. Instances of this class are guaranteed - * to be immutable. - *

        - * @author Luc Maisonobe - * @author Bryan Cazabonne - * @author Thomas Paulet - * @since 11.0 - * @see org.orekit.propagation.analytical.tle.TLEPartialDerivativesEquations - * @see org.orekit.propagation.analytical.tle.TLEPropagator - * @see SpacecraftState#getAdditionalState(String) - * @see org.orekit.propagation.AbstractPropagator - */ -public class TLEJacobiansMapper extends AbstractJacobiansMapper { - - /** State dimension, fixed to 6. */ - public static final int STATE_DIMENSION = 6; - - /** Selected parameters for Jacobian computation. */ - private final ParameterDriversList parameters; - - /** TLE propagator. */ - private final FieldTLEPropagator gPropagator; - - /** Parameters. */ - private final Gradient[] gParameters; - - /** Placeholder for the derivatives of state. */ - private double[] stateTransition; - - /** Simple constructor. - * @param name name of the Jacobians - * @param parameters selected parameters for Jacobian computation - * @param propagator the propagator that will handle the orbit propagation - */ - public TLEJacobiansMapper(final String name, - final ParameterDriversList parameters, - final TLEPropagator propagator) { - super(name, parameters); - - // Initialize fields - this.parameters = parameters; - this.stateTransition = null; - - // Intialize "field" propagator - final TLEGradientConverter converter = new TLEGradientConverter(propagator); - final FieldSpacecraftState gState = converter.getState(); - this.gParameters = converter.getParameters(gState); - this.gPropagator = converter.getPropagator(gState, gParameters); - } - - /** {@inheritDoc} */ - @Override - public void setInitialJacobians(final SpacecraftState state, final double[][] dY1dY0, - final double[][] dY1dP, final double[] p) { - - // map the converted state Jacobian to one-dimensional array - int index = 0; - for (int i = 0; i < STATE_DIMENSION; ++i) { - for (int j = 0; j < STATE_DIMENSION; ++j) { - p[index++] = (i == j) ? 1.0 : 0.0; - } - } - - if (parameters.getNbParams() != 0) { - - // map the converted parameters Jacobian to one-dimensional array - for (int i = 0; i < STATE_DIMENSION; ++i) { - for (int j = 0; j < parameters.getNbParams(); ++j) { - p[index++] = dY1dP[i][j]; - } - } - } - - } - - /** {@inheritDoc} */ - @Override - public void getStateJacobian(final SpacecraftState state, final double[][] dYdY0) { - computeDerivatives(state); - for (int i = 0; i < STATE_DIMENSION; i++) { - final double[] row = dYdY0[i]; - for (int j = 0; j < STATE_DIMENSION; j++) { - row[j] = stateTransition[i * STATE_DIMENSION + j]; - } - } - } - - - /** {@inheritDoc} */ - @Override - public void getParametersJacobian(final SpacecraftState state, final double[][] dYdP) { - - if (parameters.getNbParams() != 0) { - - computeDerivatives(state); - for (int i = 0; i < STATE_DIMENSION; i++) { - final double[] row = dYdP[i]; - for (int j = 0; j < parameters.getNbParams(); j++) { - row[j] = stateTransition[STATE_DIMENSION * STATE_DIMENSION + (j + parameters.getNbParams() * i)]; - } - } - - } - - } - - /** {@inheritDoc} - * @deprecated as of 11.1, not used anymore - */ - @Deprecated - public void analyticalDerivatives(final SpacecraftState s) { - computeDerivatives(s); - } - - /** Compute analytical derivatives. - * @param state current state - * @since 11.1 - */ - private void computeDerivatives(final SpacecraftState state) { - - // Initialize Jacobians to zero - final int dim = STATE_DIMENSION; - final int paramDim = parameters.getNbParams(); - final double[][] stateGrad = new double[dim][dim]; - final double[][] paramGrad = new double[dim][paramDim]; - - // Initialize matrix - if (stateTransition == null) { - stateTransition = state.getAdditionalState(getName()); - } - - // Compute Jacobian - final AbsoluteDate target = state.getDate(); - final FieldAbsoluteDate init = gPropagator.getTLE().getDate(); - final double dt = target.durationFrom(init.toAbsoluteDate()); - final FieldOrbit gOrbit = gPropagator.propagateOrbit(init.shiftedBy(dt), gParameters); - final FieldPVCoordinates gPv = gOrbit.getPVCoordinates(); - - final double[] derivativesX = gPv.getPosition().getX().getGradient(); - final double[] derivativesY = gPv.getPosition().getY().getGradient(); - final double[] derivativesZ = gPv.getPosition().getZ().getGradient(); - final double[] derivativesVx = gPv.getVelocity().getX().getGradient(); - final double[] derivativesVy = gPv.getVelocity().getY().getGradient(); - final double[] derivativesVz = gPv.getVelocity().getZ().getGradient(); - - // Update Jacobian with respect to state - addToRow(derivativesX, 0, stateGrad); - addToRow(derivativesY, 1, stateGrad); - addToRow(derivativesZ, 2, stateGrad); - addToRow(derivativesVx, 3, stateGrad); - addToRow(derivativesVy, 4, stateGrad); - addToRow(derivativesVz, 5, stateGrad); - - int index = TLEGradientConverter.FREE_STATE_PARAMETERS; - int parameterIndex = 0; - for (ParameterDriver driver : parameters.getDrivers()) { - if (driver.isSelected()) { - paramGrad[0][parameterIndex] += derivativesX[index]; - paramGrad[1][parameterIndex] += derivativesY[index]; - paramGrad[2][parameterIndex] += derivativesZ[index]; - paramGrad[3][parameterIndex] += derivativesVx[index]; - paramGrad[4][parameterIndex] += derivativesVy[index]; - paramGrad[5][parameterIndex] += derivativesVz[index]; - ++index; - } - ++parameterIndex; - } - - // State derivatives - for (int i = 0; i < dim; i++) { - for (int j = 0; j < dim; j++) { - stateTransition[j + dim * i] = stateGrad[i][j]; - } - } - - // Propagation parameters derivatives - final int columnTop = dim * dim; - for (int k = 0; k < paramDim; k++) { - for (int i = 0; i < dim; ++i) { - stateTransition[columnTop + (i + dim * k)] = paramGrad[i][k]; - } - } - - } - - /** Fill Jacobians rows. - * @param derivatives derivatives of a component - * @param index component index (0 for X, 1 for Y, 2 for Z, 3 for Vx, 4 for Vy, 5 for Vz) - * @param grad Jacobian of mean elements rate with respect to mean elements - */ - private void addToRow(final double[] derivatives, final int index, - final double[][] grad) { - for (int i = 0; i < 6; i++) { - grad[index][i] += derivatives[i]; - } - } - -} diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEPartialDerivativesEquations.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEPartialDerivativesEquations.java deleted file mode 100644 index be3ea346b0..0000000000 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEPartialDerivativesEquations.java +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.analytical.tle; - -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; - -/** Set of additional equations computing the partial derivatives - * of the state (orbit) with respect to initial state. - *

        - * This set of equations are automatically added to an {@link AbstractAnalyticalPropagator analytical propagator} - * in order to compute partial derivatives of the orbit along with the orbit itself. This is - * useful for example in orbit determination applications. - *

        - *

        - * The partial derivatives with respect to initial state are dimension 6 (orbit only). - *

        - * @author Bryan Cazabonne - * @author Thomas Paulet - * @since 11.0 - */ -@Deprecated -public class TLEPartialDerivativesEquations { - - /** Propagator computing state evolution. */ - private final TLEPropagator propagator; - - /** Selected parameters for Jacobian computation. */ - private ParameterDriversList selected; - - /** Name. */ - private final String name; - - /** Flag for Jacobian matrices initialization. */ - private boolean initialized; - - /** Simple constructor. - *

        - * Instance regrouping equations to compute derivatives. - *

        - * @param name name of the partial derivatives equations - * @param propagator the propagator that will handle the orbit propagation - */ - public TLEPartialDerivativesEquations(final String name, - final TLEPropagator propagator) { - this.name = name; - this.selected = null; - this.propagator = propagator; - this.initialized = false; - } - - /** Get the name of the additional state. - * @return name of the additional state - */ - public String getName() { - return name; - } - - /** Freeze the selected parameters from the force models. - */ - private void freezeParametersSelection() { - if (selected == null) { - // create new selected parameter driver list - selected = new ParameterDriversList(); - for (final ParameterDriver driver : propagator.getTLE().getParametersDrivers()) { - if (driver.isSelected()) { - selected.add(driver); - } - } - } - } - - /** Set the initial value of the Jacobian with respect to state and parameter. - *

        - * This method is equivalent to call {@link #setInitialJacobians(SpacecraftState, - * double[][], double[][])} with dYdY0 set to the identity matrix and dYdP set - * to a zero matrix. - *

        - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, so proper matrices dimensions are used. - *

        - * @param s0 initial state - * @return state with initial Jacobians added - */ - public SpacecraftState setInitialJacobians(final SpacecraftState s0) { - freezeParametersSelection(); - final int stateDimension = 6; - final double[][] dYdY0 = new double[stateDimension][stateDimension]; - final double[][] dYdP = new double[stateDimension][selected.getNbParams()]; - for (int i = 0; i < stateDimension; ++i) { - dYdY0[i][i] = 1.0; - } - return setInitialJacobians(s0, dYdY0, dYdP); - } - - /** Set the initial value of the Jacobian with respect to state and parameter. - *

        - * The returned state must be added to the propagator (it is not done - * automatically, as the user may need to add more states to it). - *

        - * @param s1 current state - * @param dY1dY0 Jacobian of current state at time t₁ with respect - * to state at some previous time t₀ (must be 6x6) - * @param dY1dP Jacobian of current state at time t₁ with respect - * to parameters (may be null if no parameters are selected) - * @return state with initial Jacobians added - */ - public SpacecraftState setInitialJacobians(final SpacecraftState s1, - final double[][] dY1dY0, final double[][] dY1dP) { - - freezeParametersSelection(); - - // Check dimensions - final int stateDim = dY1dY0.length; - if (stateDim != 6 || stateDim != dY1dY0[0].length) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_6X6, - stateDim, dY1dY0[0].length); - } - if (dY1dP != null && stateDim != dY1dP.length) { - throw new OrekitException(OrekitMessages.STATE_AND_PARAMETERS_JACOBIANS_ROWS_MISMATCH, - stateDim, dY1dP.length); - } - if (dY1dP == null && selected.getNbParams() != 0 || - dY1dP != null && selected.getNbParams() != dY1dP[0].length) { - throw new OrekitException(new OrekitException(OrekitMessages.INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH, - dY1dP == null ? 0 : dY1dP[0].length, selected.getNbParams())); - } - - // store the matrices as a single dimension array - initialized = true; - final TLEJacobiansMapper mapper = getMapper(); - final double[] p = new double[mapper.getAdditionalStateDimension()]; - mapper.setInitialJacobians(s1, dY1dY0, dY1dP, p); - - // set value in propagator - return s1.addAdditionalState(name, p); - - } - - /** Get a mapper between two-dimensional Jacobians and one-dimensional additional state. - * @return a mapper between two-dimensional Jacobians and one-dimensional additional state, - * with the same name as the instance - * @see #setInitialJacobians(SpacecraftState) - * @see #setInitialJacobians(SpacecraftState, double[][], double[][]) - */ - public TLEJacobiansMapper getMapper() { - if (!initialized) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_INITIALIZED); - } - return new TLEJacobiansMapper(name, selected, propagator); - } - -} diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java index c72ec280f0..29a76fead1 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,23 +28,25 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; -import org.orekit.frames.Frames; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.MatricesHarvester; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; +import org.orekit.propagation.analytical.tle.generation.FixedPointTleGenerationAlgorithm; +import org.orekit.propagation.analytical.tle.generation.TleGenerationAlgorithm; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; /** This class provides elements to propagate TLE's. @@ -227,25 +229,25 @@ protected TLEPropagator(final TLE initialTLE, * * @param tle the TLE to propagate. * @return the correct propagator. - * @see #selectExtrapolator(TLE, Frames) + * @see #selectExtrapolator(TLE, Frame) */ @DefaultDataContext public static TLEPropagator selectExtrapolator(final TLE tle) { - return selectExtrapolator(tle, DataContext.getDefault().getFrames()); + return selectExtrapolator(tle, DataContext.getDefault().getFrames().getTEME()); } /** Selects the extrapolator to use with the selected TLE. * @param tle the TLE to propagate. - * @param frames set of Frames to use in the propagator. + * @param teme TEME frame. * @return the correct propagator. * @since 10.1 */ - public static TLEPropagator selectExtrapolator(final TLE tle, final Frames frames) { + public static TLEPropagator selectExtrapolator(final TLE tle, final Frame teme) { return selectExtrapolator( tle, - InertialProvider.of(frames.getTEME()), + FrameAlignedProvider.of(teme), DEFAULT_MASS, - frames.getTEME()); + teme); } /** Selects the extrapolator to use with the selected TLE. @@ -262,7 +264,7 @@ public static TLEPropagator selectExtrapolator(final TLE tle, final Frames frame public static TLEPropagator selectExtrapolator(final TLE tle, final AttitudeProvider attitudeProvider, final double mass) { return selectExtrapolator(tle, attitudeProvider, mass, - DataContext.getDefault().getFrames().getTEME()); + DataContext.getDefault().getFrames().getTEME()); } /** Selects the extrapolator to use with the selected TLE. @@ -557,7 +559,8 @@ private PVCoordinates computePVCoordinates() { public void resetInitialState(final SpacecraftState state) { super.resetInitialState(state); super.setStartDate(state.getDate()); - final TLE newTLE = TLE.stateToTLE(state, tle, utc, teme); + final TleGenerationAlgorithm algorithm = getDefaultTleGenerationAlgorithm(utc, teme); + final TLE newTLE = algorithm.generate(state, tle); this.tle = newTLE; initializeCommons(); sxpInitialize(); @@ -609,12 +612,30 @@ protected AbstractMatricesHarvester createHarvester(final String stmName, final protected List getJacobiansColumnsNames() { final List columnsNames = new ArrayList<>(); for (final ParameterDriver driver : tle.getParametersDrivers()) { - if (driver.isSelected() && !columnsNames.contains(driver.getName())) { - columnsNames.add(driver.getName()); + + if (driver.isSelected() && !columnsNames.contains(driver.getNamesSpanMap().getFirstSpan().getData())) { + // As driver with same name should have same NamesSpanMap we only check the if condition on the + // first span map and then if the condition is OK all the span names are added to the jacobian column names + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + columnsNames.add(span.getData()); + } } } Collections.sort(columnsNames); return columnsNames; } + /** + * Get the default TLE generation algorithm. + * @param utc UTC time scale + * @param teme TEME frame + * @return a TLE generation algorithm + * @since 12.0 + */ + public static TleGenerationAlgorithm getDefaultTleGenerationAlgorithm(final TimeScale utc, final Frame teme) { + return new FixedPointTleGenerationAlgorithm(FixedPointTleGenerationAlgorithm.EPSILON_DEFAULT, + FixedPointTleGenerationAlgorithm.MAX_ITERATIONS_DEFAULT, + FixedPointTleGenerationAlgorithm.SCALE_DEFAULT, utc, teme); + } + } diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java new file mode 100644 index 0000000000..9e88ca2ed5 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java @@ -0,0 +1,317 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.tle.generation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.data.DataContext; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.FieldTLE; +import org.orekit.propagation.analytical.tle.FieldTLEPropagator; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.utils.ParameterDriver; + +/** + * Fixed Point method to reverse SGP4 and SDP4 propagation algorithm + * and generate a usable TLE from a spacecraft state. + *

        + * Using this algorithm, the B* value is not computed. In other words, + * the B* value from the template TLE is set to the generated one. + *

        + * @author Thomas Paulet + * @author Bryan Cazabonne + * @since 12.0 + */ +public class FixedPointTleGenerationAlgorithm implements TleGenerationAlgorithm { + + /** Default value for epsilon. */ + public static final double EPSILON_DEFAULT = 1.0e-10; + + /** Default value for maxIterations. */ + public static final int MAX_ITERATIONS_DEFAULT = 100; + + /** Default value for scale. */ + public static final double SCALE_DEFAULT = 1.0; + + /** Used to compute threshold for convergence check. */ + private final double epsilon; + + /** Maximum number of iterations for convergence. */ + private final int maxIterations; + + /** Scale factor of the Fixed Point algorithm. */ + private final double scale; + + /** UTC scale. */ + private final TimeScale utc; + + /** TEME frame. */ + private final Frame teme; + + /** + * Default constructor. + *

        + * Uses the {@link DataContext#getDefault() default data context} + * as well as {@link #EPSILON_DEFAULT}, {@link #MAX_ITERATIONS_DEFAULT}, + * {@link #SCALE_DEFAULT} for method convergence. + *

        + */ + @DefaultDataContext + public FixedPointTleGenerationAlgorithm() { + this(EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT, SCALE_DEFAULT); + } + + /** + * Constructor. + *

        + * Uses the {@link DataContext#getDefault() default data context}. + *

        + * @param epsilon used to compute threshold for convergence check + * @param maxIterations maximum number of iterations for convergence + * @param scale scale factor of the Fixed Point algorithm + */ + @DefaultDataContext + public FixedPointTleGenerationAlgorithm(final double epsilon, final int maxIterations, + final double scale) { + this(epsilon, maxIterations, scale, + DataContext.getDefault().getTimeScales().getUTC(), + DataContext.getDefault().getFrames().getTEME()); + } + + /** + * Constructor. + * @param epsilon used to compute threshold for convergence check + * @param maxIterations maximum number of iterations for convergence + * @param scale scale factor of the Fixed Point algorithm + * @param utc UTC time scale + * @param teme TEME frame + */ + public FixedPointTleGenerationAlgorithm(final double epsilon, final int maxIterations, + final double scale, final TimeScale utc, + final Frame teme) { + this.epsilon = epsilon; + this.maxIterations = maxIterations; + this.scale = scale; + this.utc = utc; + this.teme = teme; + } + + /** {@inheritDoc} */ + @Override + public TLE generate(final SpacecraftState state, final TLE templateTLE) { + + // Generation epoch + final AbsoluteDate epoch = state.getDate(); + + // gets equinoctial parameters in TEME frame from state + final EquinoctialOrbit equinoctialOrbit = convert(state.getOrbit()); + double sma = equinoctialOrbit.getA(); + double ex = equinoctialOrbit.getEquinoctialEx(); + double ey = equinoctialOrbit.getEquinoctialEy(); + double hx = equinoctialOrbit.getHx(); + double hy = equinoctialOrbit.getHy(); + double lv = equinoctialOrbit.getLv(); + + // rough initialization of the TLE + final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(equinoctialOrbit); + TLE current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, templateTLE.getBStar(epoch), utc); + + // threshold for each parameter + final double thrA = epsilon * (1 + sma); + final double thrE = epsilon * (1 + FastMath.hypot(ex, ey)); + final double thrH = epsilon * (1 + FastMath.hypot(hx, hy)); + final double thrV = epsilon * FastMath.PI; + + int k = 0; + while (k++ < maxIterations) { + + // recompute the state from the current TLE + final TLEPropagator propagator = TLEPropagator.selectExtrapolator(current, + new FrameAlignedProvider(Rotation.IDENTITY, teme), + state.getMass(), teme); + final Orbit recoveredOrbit = propagator.getInitialState().getOrbit(); + final EquinoctialOrbit recoveredEquiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recoveredOrbit); + + // adapted parameters residuals + final double deltaSma = equinoctialOrbit.getA() - recoveredEquiOrbit.getA(); + final double deltaEx = equinoctialOrbit.getEquinoctialEx() - recoveredEquiOrbit.getEquinoctialEx(); + final double deltaEy = equinoctialOrbit.getEquinoctialEy() - recoveredEquiOrbit.getEquinoctialEy(); + final double deltaHx = equinoctialOrbit.getHx() - recoveredEquiOrbit.getHx(); + final double deltaHy = equinoctialOrbit.getHy() - recoveredEquiOrbit.getHy(); + final double deltaLv = MathUtils.normalizeAngle(equinoctialOrbit.getLv() - recoveredEquiOrbit.getLv(), 0.0); + + // check convergence + if (FastMath.abs(deltaSma) < thrA && + FastMath.abs(deltaEx) < thrE && + FastMath.abs(deltaEy) < thrE && + FastMath.abs(deltaHx) < thrH && + FastMath.abs(deltaHy) < thrH && + FastMath.abs(deltaLv) < thrV) { + + // verify if parameters are estimated + for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) { + if (templateDrivers.isSelected()) { + // set to selected for the new TLE + current.getParameterDriver(templateDrivers.getName()).setSelected(true); + } + } + + // return + return current; + } + + // update state + sma += scale * deltaSma; + ex += scale * deltaEx; + ey += scale * deltaEy; + hx += scale * deltaHx; + hy += scale * deltaHy; + lv += scale * deltaLv; + final EquinoctialOrbit newEquinoctialOrbit = + new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE, + equinoctialOrbit.getFrame(), equinoctialOrbit.getDate(), equinoctialOrbit.getMu()); + final KeplerianOrbit newKeplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquinoctialOrbit); + + // update TLE + current = TleGenerationUtil.newTLE(newKeplerianOrbit, templateTLE, templateTLE.getBStar(epoch), utc); + + } + + // unable to generate a TLE + throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k); + + } + + /** {@inheritDoc} */ + @Override + public > FieldTLE generate(final FieldSpacecraftState state, + final FieldTLE templateTLE) { + + // gets equinoctial parameters in TEME frame from state + final FieldEquinoctialOrbit equinoctialOrbit = convert(state.getOrbit()); + T sma = equinoctialOrbit.getA(); + T ex = equinoctialOrbit.getEquinoctialEx(); + T ey = equinoctialOrbit.getEquinoctialEy(); + T hx = equinoctialOrbit.getHx(); + T hy = equinoctialOrbit.getHy(); + T lv = equinoctialOrbit.getLv(); + + // rough initialization of the TLE + final T bStar = state.getA().getField().getZero().add(templateTLE.getBStar()); + final FieldKeplerianOrbit keplerianOrbit = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(equinoctialOrbit); + FieldTLE current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, bStar, utc); + + // field + final Field field = state.getDate().getField(); + + // threshold for each parameter + final T thrA = sma.add(1).multiply(epsilon); + final T thrE = FastMath.hypot(ex, ey).add(1).multiply(epsilon); + final T thrH = FastMath.hypot(hx, hy).add(1).multiply(epsilon); + final T thrV = sma.getPi().multiply(epsilon); + + int k = 0; + while (k++ < maxIterations) { + + // recompute the state from the current TLE + final FieldTLEPropagator propagator = FieldTLEPropagator.selectExtrapolator(current, new FrameAlignedProvider(Rotation.IDENTITY, teme), + state.getMass(), teme, templateTLE.getParameters(field)); + final FieldOrbit recoveredOrbit = propagator.getInitialState().getOrbit(); + final FieldEquinoctialOrbit recoveredEquinoctialOrbit = (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recoveredOrbit); + + // adapted parameters residuals + final T deltaSma = equinoctialOrbit.getA().subtract(recoveredEquinoctialOrbit.getA()); + final T deltaEx = equinoctialOrbit.getEquinoctialEx().subtract(recoveredEquinoctialOrbit.getEquinoctialEx()); + final T deltaEy = equinoctialOrbit.getEquinoctialEy().subtract(recoveredEquinoctialOrbit.getEquinoctialEy()); + final T deltaHx = equinoctialOrbit.getHx().subtract(recoveredEquinoctialOrbit.getHx()); + final T deltaHy = equinoctialOrbit.getHy().subtract(recoveredEquinoctialOrbit.getHy()); + final T deltaLv = MathUtils.normalizeAngle(equinoctialOrbit.getLv().subtract(recoveredEquinoctialOrbit.getLv()), field.getZero()); + + // check convergence + if (FastMath.abs(deltaSma.getReal()) < thrA.getReal() && + FastMath.abs(deltaEx.getReal()) < thrE.getReal() && + FastMath.abs(deltaEy.getReal()) < thrE.getReal() && + FastMath.abs(deltaHx.getReal()) < thrH.getReal() && + FastMath.abs(deltaHy.getReal()) < thrH.getReal() && + FastMath.abs(deltaLv.getReal()) < thrV.getReal()) { + + // return + return current; + + } + + // update state + sma = sma.add(deltaSma.multiply(scale)); + ex = ex.add(deltaEx.multiply(scale)); + ey = ey.add(deltaEy.multiply(scale)); + hx = hx.add(deltaHx.multiply(scale)); + hy = hy.add(deltaHy.multiply(scale)); + lv = lv.add(deltaLv.multiply(scale)); + final FieldEquinoctialOrbit newEquinoctialOrbit = + new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE, + equinoctialOrbit.getFrame(), equinoctialOrbit.getDate(), equinoctialOrbit.getMu()); + final FieldKeplerianOrbit newKeplerianOrbit = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquinoctialOrbit); + + // update TLE + current = TleGenerationUtil.newTLE(newKeplerianOrbit, templateTLE, bStar, utc); + + } + + throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k); + + } + + /** + * Converts an orbit into an equinoctial orbit expressed in TEME frame. + * @param orbitIn the orbit to convert + * @return the converted orbit, i.e. equinoctial in TEME frame + */ + private EquinoctialOrbit convert(final Orbit orbitIn) { + return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu()); + } + + /** + * Converts an orbit into an equinoctial orbit expressed in TEME frame. + * @param orbitIn the orbit to convert + * @param type of the element + * @return the converted orbit, i.e. equinoctial in TEME frame + */ + private > FieldEquinoctialOrbit convert(final FieldOrbit orbitIn) { + return new FieldEquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu()); + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java new file mode 100644 index 0000000000..b671adc3d3 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java @@ -0,0 +1,318 @@ +/* Copyright 2002-2023 Mark Rutten + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Mark Rutten licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.tle.generation; + +import java.util.function.UnaryOperator; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.DiagonalMatrix; +import org.hipparchus.linear.FieldVector; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.linear.RealVector; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer; +import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction; +import org.hipparchus.util.Pair; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.FieldTLE; +import org.orekit.propagation.analytical.tle.FieldTLEPropagator; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; + +/** + * Least squares method to generate a usable TLE from a spacecraft state. + * + * @author Mark Rutten + * @since 12.0 + */ +public class LeastSquaresTleGenerationAlgorithm implements TleGenerationAlgorithm { + + /** Default value for maximum number of iterations.*/ + public static final int DEFAULT_MAX_ITERATIONS = 1000; + + /** UTC scale. */ + private final TimeScale utc; + + /** TEME frame. */ + private final Frame teme; + + /** Maximum number of iterations. */ + private final int maxIterations; + + /** RMS. */ + private double rms; + + /** + * Default constructor. + *

        + * Uses the {@link DataContext#getDefault() default data context} as well as + * {@link #DEFAULT_MAX_ITERATIONS}. + *

        + */ + @DefaultDataContext + public LeastSquaresTleGenerationAlgorithm() { + this(DEFAULT_MAX_ITERATIONS); + } + + /** + * Default constructor. + *

        + * Uses the {@link DataContext#getDefault() default data context}. + *

        + * @param maxIterations maximum number of iterations for convergence + */ + @DefaultDataContext + public LeastSquaresTleGenerationAlgorithm(final int maxIterations) { + this(maxIterations, DataContext.getDefault().getTimeScales().getUTC(), + DataContext.getDefault().getFrames().getTEME()); + } + + /** + * Constructor. + * @param maxIterations maximum number of iterations for convergence + * @param utc UTC time scale + * @param teme TEME frame + */ + public LeastSquaresTleGenerationAlgorithm(final int maxIterations, + final TimeScale utc, final Frame teme) { + this.maxIterations = maxIterations; + this.utc = utc; + this.teme = teme; + this.rms = Double.MAX_VALUE; + } + + /** {@inheritDoc} */ + @Override + public TLE generate(final SpacecraftState state, final TLE templateTLE) { + + // Generation epoch + final AbsoluteDate epoch = state.getDate(); + + // State vector + final RealVector stateVector = MatrixUtils.createRealVector(6); + // Position/Velocity + final Vector3D position = state.getPVCoordinates().getPosition(); + final Vector3D velocity = state.getPVCoordinates().getVelocity(); + + // Fill state vector + stateVector.setEntry(0, position.getX()); + stateVector.setEntry(1, position.getY()); + stateVector.setEntry(2, position.getZ()); + stateVector.setEntry(3, velocity.getX()); + stateVector.setEntry(4, velocity.getY()); + stateVector.setEntry(5, velocity.getZ()); + + // Create the initial guess of the least squares problem + final RealVector startState = MatrixUtils.createRealVector(7); + startState.setSubVector(0, stateVector.getSubVector(0, 6)); + + // Weights + final double[] weights = new double[6]; + final double velocityWeight = state.getPVCoordinates().getVelocity().getNorm() * state.getPosition().getNormSq() / state.getMu(); + for (int i = 0; i < 3; i++) { + weights[i] = 1.0; + weights[i + 3] = velocityWeight; + } + + // Time difference between template TLE and spacecraft state + final double dt = state.getDate().durationFrom(templateTLE.getDate()); + + // Construct least squares problem + final LeastSquaresProblem problem = + new LeastSquaresBuilder().maxIterations(maxIterations) + .maxEvaluations(maxIterations) + .model(new ObjectiveFunction(templateTLE, dt)) + .target(stateVector) + .weight(new DiagonalMatrix(weights)) + .start(startState) + .build(); + + // Solve least squares + final LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer(); + final LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(problem); + rms = optimum.getRMS(); + + // Create new TLE from mean state + final Vector3D positionEstimated = new Vector3D(optimum.getPoint().getSubVector(0, 3).toArray()); + final Vector3D velocityEstimated = new Vector3D(optimum.getPoint().getSubVector(3, 3).toArray()); + final PVCoordinates pvCoordinates = new PVCoordinates(positionEstimated, velocityEstimated); + final KeplerianOrbit orbit = new KeplerianOrbit(pvCoordinates, teme, epoch, state.getMu()); + final TLE generated = TleGenerationUtil.newTLE(orbit, templateTLE, templateTLE.getBStar(), utc); + + // Verify if parameters are estimated + for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) { + if (templateDrivers.isSelected()) { + // Set to selected for the new TLE + generated.getParameterDriver(templateDrivers.getName()).setSelected(true); + } + } + + // Return + return generated; + + } + + /** + * Get the Root Mean Square of the TLE estimation. + *

        + * Be careful that the RMS is updated each time the + * {@link LeastSquaresTleGenerationAlgorithm#generate(SpacecraftState, TLE)} + * method is called. + *

        + * @return the RMS + */ + public double getRms() { + return rms; + } + + /** {@inheritDoc} */ + @Override + public > FieldTLE generate(final FieldSpacecraftState state, + final FieldTLE templateTLE) { + throw new UnsupportedOperationException(); + } + + /** Least squares model. */ + private class ObjectiveFunction implements MultivariateJacobianFunction { + + /** Template TLE. */ + private final FieldTLE templateTLE; + + /** Time difference between template TLE and spacecraft state (s). */ + private final double dt; + + /** + * Constructor. + * @param templateTLE template TLE + * @param dt time difference between template TLE and spacecraft state (s) + */ + ObjectiveFunction(final TLE templateTLE, final double dt) { + this.dt = dt; + // Conversion of template TLE to a field TLE + final Field field = GradientField.getField(7); + this.templateTLE = new FieldTLE<>(field, templateTLE.getLine1(), templateTLE.getLine2(), utc); + } + + /** {@inheritDoc} */ + @Override + public Pair value(final RealVector point) { + final RealVector objectiveOscState = MatrixUtils.createRealVector(6); + final RealMatrix objectiveJacobian = MatrixUtils.createRealMatrix(6, 7); + getTransformedAndJacobian(state -> meanStateToPV(state), point, objectiveOscState, objectiveJacobian); + return new Pair<>(objectiveOscState, objectiveJacobian); + } + + /** + * Fill model. + * @param operator state vector propagation + * @param state state vector + * @param transformed value to fill + * @param jacobian Jacobian to fill + */ + private void getTransformedAndJacobian(final UnaryOperator> operator, + final RealVector state, final RealVector transformed, + final RealMatrix jacobian) { + + // State dimension + final int stateDim = state.getDimension(); + + // Initialise the state as field to calculate the gradient + final GradientField field = GradientField.getField(stateDim); + final FieldVector fieldState = MatrixUtils.createFieldVector(field, stateDim); + for (int i = 0; i < stateDim; ++i) { + fieldState.setEntry(i, Gradient.variable(stateDim, i, state.getEntry(i))); + } + + // Call operator + final FieldVector fieldTransformed = operator.apply(fieldState); + + // Output dimension + final int outDim = fieldTransformed.getDimension(); + + // Extract transform and Jacobian as real values + for (int i = 0; i < outDim; ++i) { + transformed.setEntry(i, fieldTransformed.getEntry(i).getReal()); + jacobian.setRow(i, fieldTransformed.getEntry(i).getGradient()); + } + + } + + /** + * Operator to propagate the state vector. + * @param state state vector + * @return propagated state vector + */ + private FieldVector meanStateToPV(final FieldVector state) { + // Epoch + final FieldAbsoluteDate epoch = templateTLE.getDate(); + + // B* + final Gradient[] bStar = state.getSubVector(6, 1).toArray(); + + // Field + final Field field = epoch.getField(); + + // Extract mean state + final FieldVector3D position = new FieldVector3D<>(state.getSubVector(0, 3).toArray()); + final FieldVector3D velocity = new FieldVector3D<>(state.getSubVector(3, 3).toArray()); + final FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity); + final FieldKeplerianOrbit orbit = new FieldKeplerianOrbit<>(pvCoordinates, teme, epoch, field.getZero().add(TLEPropagator.getMU())); + + // Convert to TLE + final FieldTLE tle = TleGenerationUtil.newTLE(orbit, templateTLE, bStar[0], utc); + + // Propagate to epoch + final FieldPVCoordinates propagatedCoordinates = + FieldTLEPropagator.selectExtrapolator(tle, teme, bStar).getPVCoordinates(epoch.shiftedBy(dt), bStar); + + // Osculating + final FieldVector osculating = MatrixUtils.createFieldVector(field, 6); + osculating.setEntry(0, propagatedCoordinates.getPosition().getX()); + osculating.setEntry(1, propagatedCoordinates.getPosition().getY()); + osculating.setEntry(2, propagatedCoordinates.getPosition().getZ()); + osculating.setEntry(3, propagatedCoordinates.getVelocity().getX()); + osculating.setEntry(4, propagatedCoordinates.getVelocity().getY()); + osculating.setEntry(5, propagatedCoordinates.getVelocity().getZ()); + + // Return + return osculating; + + } + + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/TleGenerationAlgorithm.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/TleGenerationAlgorithm.java new file mode 100644 index 0000000000..642dfbed3b --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/TleGenerationAlgorithm.java @@ -0,0 +1,62 @@ +/* Copyright 2023 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.tle.generation; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.FieldTLE; +import org.orekit.propagation.analytical.tle.TLE; + +/** + * This interface provides a way to generate a TLE from a spacecraft state. + * @author Bryan Cazabonne + * @since 12.0 + */ +public interface TleGenerationAlgorithm { + + /** + * Generate a TLE from a given spacecraft state and a template TLE. + *

        + * The template TLE is only used to get identifiers like satellite + * number, launch year, etc. + * In other words, the keplerian elements contained in the generate + * TLE a based on the provided state and not the template TLE. + *

        + * @param state spacecraft state + * @param templateTLE template TLE + * @return a TLE corresponding to the given state + */ + TLE generate(SpacecraftState state, TLE templateTLE); + + /** + * Generate a TLE from a given spacecraft state and a template TLE. + *

        + * The template TLE is only used to get identifiers like satellite + * number, launch year, etc. + * In other words, the keplerian elements contained in the generate + * TLE a based on the provided state and not the template TLE. + *

        + * @param type of the elements + * @param state spacecraft state + * @param templateTLE template TLE + * @return a TLE corresponding to the given state + */ + > FieldTLE generate(FieldSpacecraftState state, + FieldTLE templateTLE); + +} diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/TleGenerationUtil.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/TleGenerationUtil.java new file mode 100644 index 0000000000..18543159bc --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/TleGenerationUtil.java @@ -0,0 +1,142 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.analytical.tle.generation; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.propagation.analytical.tle.FieldTLE; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; + +/** + * Utility class for TLE generation algorithm. + * @author Bryan Cazabonne + * @author Thomas Paulet + * @author Mark Rutten + */ +public final class TleGenerationUtil { + + /** Private constructor. + *

        This class is a utility class, it should neither have a public + * nor a default constructor. This private constructor prevents + * the compiler from generating one automatically.

        + */ + private TleGenerationUtil() { + } + + /** + * Builds a new TLE from Keplerian parameters and a template for TLE data. + * @param keplerianOrbit the Keplerian parameters to build the TLE from + * @param templateTLE TLE used to get object identification + * @param bStar TLE B* parameter + * @param utc UTC scale + * @return TLE with template identification and new orbital parameters + */ + public static TLE newTLE(final KeplerianOrbit keplerianOrbit, final TLE templateTLE, + final double bStar, final TimeScale utc) { + + // Keplerian parameters + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double e = keplerianOrbit.getE(); + final double i = keplerianOrbit.getI(); + final double raan = keplerianOrbit.getRightAscensionOfAscendingNode(); + final double pa = keplerianOrbit.getPerigeeArgument(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + + // TLE epoch is state epoch + final AbsoluteDate epoch = keplerianOrbit.getDate(); + + // Identification + final int satelliteNumber = templateTLE.getSatelliteNumber(); + final char classification = templateTLE.getClassification(); + final int launchYear = templateTLE.getLaunchYear(); + final int launchNumber = templateTLE.getLaunchNumber(); + final String launchPiece = templateTLE.getLaunchPiece(); + final int ephemerisType = templateTLE.getEphemerisType(); + final int elementNumber = templateTLE.getElementNumber(); + + // Updates revolutionNumberAtEpoch + final int revolutionNumberAtEpoch = templateTLE.getRevolutionNumberAtEpoch(); + final double dt = epoch.durationFrom(templateTLE.getDate()); + final int newRevolutionNumberAtEpoch = (int) (revolutionNumberAtEpoch + FastMath.floor((MathUtils.normalizeAngle(meanAnomaly, FastMath.PI) + dt * meanMotion) / (MathUtils.TWO_PI))); + + // Gets Mean Motion derivatives + final double meanMotionFirstDerivative = templateTLE.getMeanMotionFirstDerivative(); + final double meanMotionSecondDerivative = templateTLE.getMeanMotionSecondDerivative(); + + // Returns the new TLE + return new TLE(satelliteNumber, classification, launchYear, launchNumber, launchPiece, ephemerisType, + elementNumber, epoch, meanMotion, meanMotionFirstDerivative, meanMotionSecondDerivative, + e, i, pa, raan, meanAnomaly, newRevolutionNumberAtEpoch, bStar, utc); + + } + + /** + * Builds a new TLE from Keplerian parameters and a template for TLE data. + * @param keplerianOrbit the Keplerian parameters to build the TLE from + * @param templateTLE TLE used to get object identification + * @param bStar TLE B* parameter + * @param utc UTC scale + * @param type of the element + * @return TLE with template identification and new orbital parameters + */ + public static > FieldTLE newTLE(final FieldKeplerianOrbit keplerianOrbit, + final FieldTLE templateTLE, final T bStar, + final TimeScale utc) { + + // Keplerian parameters + final T meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final T e = keplerianOrbit.getE(); + final T i = keplerianOrbit.getI(); + final T raan = keplerianOrbit.getRightAscensionOfAscendingNode(); + final T pa = keplerianOrbit.getPerigeeArgument(); + final T meanAnomaly = keplerianOrbit.getMeanAnomaly(); + + // TLE epoch is state epoch + final FieldAbsoluteDate epoch = keplerianOrbit.getDate(); + + // Identification + final int satelliteNumber = templateTLE.getSatelliteNumber(); + final char classification = templateTLE.getClassification(); + final int launchYear = templateTLE.getLaunchYear(); + final int launchNumber = templateTLE.getLaunchNumber(); + final String launchPiece = templateTLE.getLaunchPiece(); + final int ephemerisType = templateTLE.getEphemerisType(); + final int elementNumber = templateTLE.getElementNumber(); + + // Updates revolutionNumberAtEpoch + final int revolutionNumberAtEpoch = templateTLE.getRevolutionNumberAtEpoch(); + final T dt = epoch.durationFrom(templateTLE.getDate()); + final int newRevolutionNumberAtEpoch = (int) ((int) revolutionNumberAtEpoch + FastMath.floor(MathUtils.normalizeAngle(meanAnomaly, e.getPi()).add(dt.multiply(meanMotion)).divide(MathUtils.TWO_PI)).getReal()); + + // Gets Mean Motion derivatives + final T meanMotionFirstDerivative = templateTLE.getMeanMotionFirstDerivative(); + final T meanMotionSecondDerivative = templateTLE.getMeanMotionSecondDerivative(); + + // Returns the new TLE + return new FieldTLE<>(satelliteNumber, classification, launchYear, launchNumber, launchPiece, ephemerisType, + elementNumber, epoch, meanMotion, meanMotionFirstDerivative, meanMotionSecondDerivative, + e, i, pa, raan, meanAnomaly, newRevolutionNumberAtEpoch, bStar.getReal(), utc); + + } + +} diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/package-info.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/package-info.java new file mode 100644 index 0000000000..99ff6fe5a4 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2023 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * This package provides classes related to TLE generation. + * + * @author Bryan Cazabonne + * + */ +package org.orekit.propagation.analytical.tle.generation; diff --git a/src/main/java/org/orekit/propagation/analytical/tle/package-info.java b/src/main/java/org/orekit/propagation/analytical/tle/package-info.java index 3d98be390c..23f348c6f1 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/package-info.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java new file mode 100644 index 0000000000..8541e86cb8 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AbstractFieldIntegratorBuilder.java @@ -0,0 +1,55 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Abstract class for {@link FieldODEIntegratorBuilder}. + * + * @param type of the field elements + * + * @author Vincent Cucchietti + */ +public abstract class AbstractFieldIntegratorBuilder> + implements FieldODEIntegratorBuilder { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public AbstractFieldIntegratorBuilder() { + // nothing to do + } + + /** {@inheritDoc} */ + public abstract AbstractFieldIntegrator buildIntegrator(Field field, Orbit orbit, OrbitType orbitType); + + /** {@inheritDoc} */ + public AbstractFieldIntegrator buildIntegrator(final FieldOrbit orbit, final OrbitType orbitType) { + return buildIntegrator(orbit.getA().getField(), orbit.toOrbit(), orbitType); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java new file mode 100644 index 0000000000..c53f1d3b68 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java @@ -0,0 +1,88 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.exception.MathIllegalArgumentException; + +/** + * Abstract class for integrator builder using fixed step size. + * + * @param Type of the field elements + * + * @author Vincent Cucchietti + */ +public abstract class AbstractFixedStepFieldIntegratorBuilder> + extends AbstractFieldIntegratorBuilder { + + /** Step size (s). */ + private double step; + + /** Step size (s). */ + private T fieldStep; + + /** + * Constructor. + * + * @param step step size (s) + */ + AbstractFixedStepFieldIntegratorBuilder(final double step) { + // Check that given step size is strictly positive + checkStep(step); + + this.step = step; + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + */ + AbstractFixedStepFieldIntegratorBuilder(final T step) { + // Check that given step size is strictly positive + checkStep(step.getReal()); + + this.fieldStep = step; + } + + /** + * Check that given step size is not equal to 0. + * + * @param stepToCheck step size (s) to check + */ + protected void checkStep(final double stepToCheck) { + if (stepToCheck == 0) { + throw new MathIllegalArgumentException(LocalizedCoreFormats.ZERO_NOT_ALLOWED, stepToCheck); + } + } + + /** + * Get "fielded" step size (s). + * + * @param field field to which the element belong + * + * @return "fielded" step size (s) + */ + protected T getFieldStep(final Field field) { + return fieldStep != null ? fieldStep : field.getOne().multiply(step); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java new file mode 100644 index 0000000000..39d6642611 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AbstractLimitedVariableStepFieldIntegratorBuilder.java @@ -0,0 +1,49 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; + +/** + * Abstract class for integrator using a limited number of variable steps. + * + * @param type of the field elements + * + * @author Vincent Cucchietti + */ +public abstract class AbstractLimitedVariableStepFieldIntegratorBuilder> + extends AbstractVariableStepFieldIntegratorBuilder { + + // CHECKSTYLE: stop VisibilityModifier check + /** Number of steps. */ + protected final int nSteps; + // CHECKSTYLE: resume VisibilityModifier check + + /** + * Constructor. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * @param nSteps number of steps + */ + AbstractLimitedVariableStepFieldIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP) { + super(minStep, maxStep, dP); + this.nSteps = nSteps; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorBuilder.java index ff692e2d42..f79928e865 100644 --- a/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,19 +22,21 @@ import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.util.FastMath; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.forces.gravity.NewtonianAttraction; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.integration.AdditionalDerivativesProvider; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** Base class for propagator builders. * @author Pascal Parraud @@ -69,7 +71,7 @@ public abstract class AbstractPropagatorBuilder implements PropagatorBuilder { private final OrbitType orbitType; /** Position angle type to use. */ - private final PositionAngle positionAngle; + private final PositionAngleType positionAngleType; /** Position scale to use for the orbital drivers. */ private final double positionScale; @@ -77,10 +79,6 @@ public abstract class AbstractPropagatorBuilder implements PropagatorBuilder { /** Attitude provider for the propagator. */ private AttitudeProvider attitudeProvider; - /** Additional equations. */ - @Deprecated - private List additionalEquations; - /** Additional derivatives providers. * @since 11.1 */ @@ -93,8 +91,8 @@ public abstract class AbstractPropagatorBuilder implements PropagatorBuilder { * inertial frame, the central attraction coefficient, the orbit type, and is also * used together with the {@code positionScale} to convert from the {@link * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the - * callers of this builder to the real orbital parameters. The initial attitude - * provider is aligned with the inertial frame. + * callers of this builder to the real orbital parameters. The default attitude + * provider is aligned with the orbit's inertial frame. *

        *

        * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers} @@ -105,19 +103,19 @@ public abstract class AbstractPropagatorBuilder implements PropagatorBuilder { * {@link ParameterDriver#setSelected(boolean) setSelected(false)}. *

        * @param templateOrbit reference orbit from which real orbits will be built - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should * be set up for central attraction coefficient * @since 8.0 - * @see #AbstractPropagatorBuilder(Orbit, PositionAngle, double, boolean, + * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean, * AttitudeProvider) */ - protected AbstractPropagatorBuilder(final Orbit templateOrbit, final PositionAngle positionAngle, + protected AbstractPropagatorBuilder(final Orbit templateOrbit, final PositionAngleType positionAngleType, final double positionScale, final boolean addDriverForCentralAttraction) { - this(templateOrbit, positionAngle, positionScale, addDriverForCentralAttraction, - new InertialProvider(templateOrbit.getFrame())); + this(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction, + new FrameAlignedProvider(templateOrbit.getFrame())); } /** Build a new instance. @@ -138,17 +136,17 @@ protected AbstractPropagatorBuilder(final Orbit templateOrbit, final PositionAng * {@link ParameterDriver#setSelected(boolean) setSelected(false)}. *

        * @param templateOrbit reference orbit from which real orbits will be built - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should * be set up for central attraction coefficient * @param attitudeProvider for the propagator. * @since 10.1 - * @see #AbstractPropagatorBuilder(Orbit, PositionAngle, double, boolean) + * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean) */ protected AbstractPropagatorBuilder(final Orbit templateOrbit, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final boolean addDriverForCentralAttraction, final AttitudeProvider attitudeProvider) { @@ -158,15 +156,14 @@ protected AbstractPropagatorBuilder(final Orbit templateOrbit, this.mu = templateOrbit.getMu(); this.propagationDrivers = new ParameterDriversList(); this.orbitType = templateOrbit.getType(); - this.positionAngle = positionAngle; + this.positionAngleType = positionAngleType; this.positionScale = positionScale; - this.orbitalDrivers = orbitType.getDrivers(positionScale, templateOrbit, positionAngle); + this.orbitalDrivers = orbitType.getDrivers(positionScale, templateOrbit, positionAngleType); this.attitudeProvider = attitudeProvider; for (final DelegatingDriver driver : orbitalDrivers.getDrivers()) { driver.setSelected(true); } - this.additionalEquations = new ArrayList<>(); this.additionalDerivativesProviders = new ArrayList<>(); if (addDriverForCentralAttraction) { @@ -175,7 +172,14 @@ protected AbstractPropagatorBuilder(final Orbit templateOrbit, muDriver.addObserver(new ParameterObserver() { /** {@inheridDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { + // getValue(), can be called without argument as mu driver should have only one span + AbstractPropagatorBuilder.this.mu = driver.getValue(); + } + + @Override + public void valueSpanMapChanged(final TimeSpanMap previousValueSpanMap, final ParameterDriver driver) { + // getValue(), can be called without argument as mu driver should have only one span AbstractPropagatorBuilder.this.mu = driver.getValue(); } }); @@ -190,8 +194,8 @@ public OrbitType getOrbitType() { } /** {@inheritDoc} */ - public PositionAngle getPositionAngle() { - return positionAngle; + public PositionAngleType getPositionAngleType() { + return positionAngleType; } /** {@inheritDoc} */ @@ -241,32 +245,30 @@ public double getPositionScale() { return positionScale; } - /** Get the central attraction coefficient (µ - m³/s²) value. - * @return the central attraction coefficient (µ - m³/s²) value - * @since 9.2 - */ + /** {@inheritDoc} */ + @Override public double getMu() { return mu; } - /** Get the number of selected parameters. - * @return number of selected parameters + /** Get the number of estimated values for selected parameters. + * @return number of estimated values for selected parameters */ - private int getNbSelected() { + private int getNbValuesForSelected() { int count = 0; // count orbital parameters for (final ParameterDriver driver : orbitalDrivers.getDrivers()) { if (driver.isSelected()) { - ++count; + count += driver.getNbOfValues(); } } // count propagation parameters for (final ParameterDriver driver : propagationDrivers.getDrivers()) { if (driver.isSelected()) { - ++count; + count += driver.getNbOfValues(); } } @@ -278,18 +280,22 @@ private int getNbSelected() { public double[] getSelectedNormalizedParameters() { // allocate array - final double[] selected = new double[getNbSelected()]; + final double[] selected = new double[getNbValuesForSelected()]; // fill data int index = 0; for (final ParameterDriver driver : orbitalDrivers.getDrivers()) { if (driver.isSelected()) { - selected[index++] = driver.getNormalizedValue(); + for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) { + selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH); + } } } for (final ParameterDriver driver : propagationDrivers.getDrivers()) { if (driver.isSelected()) { - selected[index++] = driver.getNormalizedValue(); + for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) { + selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH); + } } } @@ -308,9 +314,9 @@ public double[] getSelectedNormalizedParameters() { protected Orbit createInitialOrbit() { final double[] unNormalized = new double[orbitalDrivers.getNbParams()]; for (int i = 0; i < unNormalized.length; ++i) { - unNormalized[i] = orbitalDrivers.getDrivers().get(i).getValue(); + unNormalized[i] = orbitalDrivers.getDrivers().get(i).getValue(initialOrbitDate); } - return getOrbitType().mapArrayToOrbit(unNormalized, null, positionAngle, initialOrbitDate, mu, frame); + return getOrbitType().mapArrayToOrbit(unNormalized, null, positionAngleType, initialOrbitDate, mu, frame); } /** Set the selected parameters. @@ -319,10 +325,10 @@ protected Orbit createInitialOrbit() { protected void setParameters(final double[] normalizedParameters) { - if (normalizedParameters.length != getNbSelected()) { + if (normalizedParameters.length != getNbValuesForSelected()) { throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH, normalizedParameters.length, - getNbSelected()); + getNbValuesForSelected()); } int index = 0; @@ -330,24 +336,39 @@ protected void setParameters(final double[] normalizedParameters) { // manage orbital parameters for (final ParameterDriver driver : orbitalDrivers.getDrivers()) { if (driver.isSelected()) { - driver.setNormalizedValue(normalizedParameters[index++]); + // If the parameter driver contains only 1 value to estimate over the all time range, which + // is normally always the case for orbital drivers + if (driver.getNbOfValues() == 1) { + driver.setNormalizedValue(normalizedParameters[index++], null); + + } else { + + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + driver.setNormalizedValue(normalizedParameters[index++], span.getStart()); + } + } } } // manage propagation parameters for (final ParameterDriver driver : propagationDrivers.getDrivers()) { + if (driver.isSelected()) { - driver.setNormalizedValue(normalizedParameters[index++]); + + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + driver.setNormalizedValue(normalizedParameters[index++], span.getStart()); + } } } - } - /** Add a supported parameter. - * @param driver driver for the parameter + /** + * Add supported parameters. + * + * @param drivers drivers for the parameters */ - protected void addSupportedParameter(final ParameterDriver driver) { - propagationDrivers.add(driver); + protected void addSupportedParameters(final List drivers) { + drivers.forEach(propagationDrivers::add); propagationDrivers.sort(); } @@ -358,7 +379,7 @@ public void resetOrbit(final Orbit newOrbit) { // Map the new orbit in an array of double final double[] orbitArray = new double[6]; - orbitType.mapOrbitToArray(newOrbit, getPositionAngle(), orbitArray, null); + orbitType.mapOrbitToArray(newOrbit, getPositionAngleType(), orbitArray, null); // Update all the orbital drivers, selected or unselected // Reset values and reference values @@ -366,33 +387,13 @@ public void resetOrbit(final Orbit newOrbit) { int i = 0; for (DelegatingDriver driver : orbitalDriversList) { driver.setReferenceValue(orbitArray[i]); - driver.setValue(orbitArray[i++]); + driver.setValue(orbitArray[i++], newOrbit.getDate()); } // Change the initial orbit date in the builder this.initialOrbitDate = newOrbit.getDate(); } - /** Add a set of user-specified equations to be integrated along with the orbit propagation (author Shiva Iyer). - * @param additional additional equations - * @since 10.1 - * @deprecated as of 11.1, replaced by {@link #addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} - */ - @Deprecated - public void addAdditionalEquations(final org.orekit.propagation.integration.AdditionalEquations additional) { - additionalEquations.add(additional); - } - - /** Get the list of additional equations. - * @return the list of additional equations - * @since 10.1 - * @deprecated as of 11.1, replaced by {@link #addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} - */ - @Deprecated - protected List getAdditionalEquations() { - return additionalEquations; - } - /** Add a set of user-specified equations to be integrated along with the orbit propagation (author Shiva Iyer). * @param provider provider for additional derivatives * @since 11.1 diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorConverter.java b/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorConverter.java index d0d518284b..1c28c9af46 100644 --- a/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorConverter.java +++ b/src/main/java/org/orekit/propagation/conversion/AbstractPropagatorConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java new file mode 100644 index 0000000000..f8b3e4344d --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AbstractVariableStepFieldIntegratorBuilder.java @@ -0,0 +1,62 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Abstract class for integrator builder using variable step size. + * + * @param type of the field elements + * + * @author Vincent Cucchietti + */ +public abstract class AbstractVariableStepFieldIntegratorBuilder> + extends AbstractFieldIntegratorBuilder { + + // CHECKSTYLE: stop VisibilityModifier check + /** Minimum step size (s). */ + protected final double minStep; + + /** Maximum step size (s). */ + protected final double maxStep; + + /** Position error (m). */ + protected final double dP; + // CHECKSTYLE: resume VisibilityModifier check + + /** + * Constructor. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + */ + AbstractVariableStepFieldIntegratorBuilder(final double minStep, final double maxStep, final double dP) { + this.minStep = minStep; + this.maxStep = maxStep; + this.dP = dP; + } + + /** {@inheritDoc} */ + @Override + public abstract AbstractFieldIntegrator buildIntegrator(Field field, Orbit orbit, OrbitType orbitType); +} diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java new file mode 100644 index 0000000000..b7b6c912d8 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthFieldIntegratorBuilder.java @@ -0,0 +1,60 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.AdamsBashforthFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Builder for AdamsBashforthFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class AdamsBashforthFieldIntegratorBuilder> + extends AbstractLimitedVariableStepFieldIntegratorBuilder { + + /** + * Build a new instance. + * + * @param nSteps number of steps + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * + * @see AdamsBashforthFieldIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public AdamsBashforthFieldIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP) { + super(nSteps, minStep, maxStep, dP); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + return new AdamsBashforthFieldIntegrator<>(field, nSteps, minStep, maxStep, tol[0], tol[1]); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java index 919c68ebc8..b2b8b4a697 100644 --- a/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AdamsBashforthIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java new file mode 100644 index 0000000000..8d3a2965b1 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonFieldIntegratorBuilder.java @@ -0,0 +1,60 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.AdamsMoultonFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Builder for AdamsMoultonFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class AdamsMoultonFieldIntegratorBuilder> + extends AbstractLimitedVariableStepFieldIntegratorBuilder { + + /** + * Build a new instance. + * + * @param nSteps number of steps + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * + * @see AdamsMoultonFieldIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public AdamsMoultonFieldIntegratorBuilder(final int nSteps, final double minStep, + final double maxStep, final double dP) { + super(nSteps, minStep, maxStep, dP); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + return new AdamsMoultonFieldIntegrator<>(field, nSteps, minStep, maxStep, tol[0], tol[1]); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java index 64afb3295f..88cffb5e3b 100644 --- a/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AdamsMoultonIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java index 4364d2b50d..f93651e83d 100644 --- a/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,25 +16,21 @@ */ package org.orekit.propagation.conversion; - +import java.util.Collections; import java.util.List; - import org.hipparchus.util.FastMath; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.leastsquares.AbstractBatchLSModel; import org.orekit.estimation.leastsquares.BatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.estimation.sequential.KalmanModel; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.TideSystem; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.analytical.BrouwerLyddanePropagator; import org.orekit.propagation.analytical.tle.TLE; import org.orekit.utils.ParameterDriver; @@ -69,7 +65,7 @@ * @author Bryan Cazabonne * @since 11.1 */ -public class BrouwerLyddanePropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { +public class BrouwerLyddanePropagatorBuilder extends AbstractPropagatorBuilder { /** Parameters scaling factor. *

        @@ -88,28 +84,30 @@ public class BrouwerLyddanePropagatorBuilder extends AbstractPropagatorBuilder i * #createInitialOrbit() create initial orbit}. It defines the * inertial frame, the central attraction coefficient, the orbit type, and is also * used together with the {@code positionScale} to convert from the {@link - * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters used by the - * callers of this builder to the real orbital parameters. + * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters + * used by the callers of this builder to the real orbital parameters. + * The default attitude provider is aligned with the orbit's inertial frame. *

        * * @param templateOrbit reference orbit from which real orbits will be built * (note that the mu from this orbit will be overridden with the mu from the * {@code provider}) * @param provider for un-normalized zonal coefficients - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param M2 value of empirical drag coefficient in rad/s². * If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed * @see #BrouwerLyddanePropagatorBuilder(Orbit, - * UnnormalizedSphericalHarmonicsProvider, PositionAngle, double, AttitudeProvider, double) + * UnnormalizedSphericalHarmonicsProvider, PositionAngleType, double, AttitudeProvider, double) */ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, final UnnormalizedSphericalHarmonicsProvider provider, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final double M2) { - this(templateOrbit, provider, positionAngle, positionScale, InertialProvider.of(templateOrbit.getFrame()), M2); + this(templateOrbit, provider, positionAngleType, positionScale, + FrameAlignedProvider.of(templateOrbit.getFrame()), M2); } /** Build a new instance. @@ -133,13 +131,13 @@ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth) * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth) * @param orbitType orbit type to use - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param M2 value of empirical drag coefficient in rad/s². * If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed * @see #BrouwerLyddanePropagatorBuilder(Orbit, - * UnnormalizedSphericalHarmonicsProvider, PositionAngle, double, AttitudeProvider, double) + * UnnormalizedSphericalHarmonicsProvider, PositionAngleType, double, AttitudeProvider, double) */ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, final double referenceRadius, @@ -150,7 +148,7 @@ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, final double c40, final double c50, final OrbitType orbitType, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final double M2) { this(templateOrbit, @@ -184,7 +182,7 @@ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, 0 } }), - positionAngle, positionScale, M2); + positionAngleType, positionScale, M2); } /** Build a new instance. @@ -200,46 +198,64 @@ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, * (note that the mu from this orbit will be overridden with the mu from the * {@code provider}) * @param provider for un-normalized zonal coefficients - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) + * @param attitudeProvider attitude law to use * @param M2 value of empirical drag coefficient in rad/s². * If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed - * @param attitudeProvider attitude law to use */ public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit, final UnnormalizedSphericalHarmonicsProvider provider, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final AttitudeProvider attitudeProvider, final double M2) { - super(overrideMu(templateOrbit, provider, positionAngle), positionAngle, positionScale, true, attitudeProvider); + super(overrideMu(templateOrbit, provider, positionAngleType), positionAngleType, positionScale, true, attitudeProvider); this.provider = provider; // initialize M2 driver final ParameterDriver M2Driver = new ParameterDriver(BrouwerLyddanePropagator.M2_NAME, M2, SCALE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); - addSupportedParameter(M2Driver); + addSupportedParameters(Collections.singletonList(M2Driver)); } /** Override central attraction coefficient. * @param templateOrbit template orbit * @param provider gravity field provider - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @return orbit with overridden central attraction coefficient */ private static Orbit overrideMu(final Orbit templateOrbit, final UnnormalizedSphericalHarmonicsProvider provider, - final PositionAngle positionAngle) { + final PositionAngleType positionAngleType) { final double[] parameters = new double[6]; final double[] parametersDot = templateOrbit.hasDerivatives() ? new double[6] : null; - templateOrbit.getType().mapOrbitToArray(templateOrbit, positionAngle, parameters, parametersDot); - return templateOrbit.getType().mapArrayToOrbit(parameters, parametersDot, positionAngle, + templateOrbit.getType().mapOrbitToArray(templateOrbit, positionAngleType, parameters, parametersDot); + return templateOrbit.getType().mapArrayToOrbit(parameters, parametersDot, positionAngleType, templateOrbit.getDate(), provider.getMu(), templateOrbit.getFrame()); } + /** {@inheritDoc} */ + @Override + public BrouwerLyddanePropagatorBuilder copy() { + + // Find M2 value + double m2 = 0.0; + for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) { + if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) { + // it is OK as BL m2 parameterDriver has 1 value estimated from -INF to +INF, and + // setPeriod method should not be called on this driver (to have several values estimated) + m2 = driver.getValue(); + } + } + + return new BrouwerLyddanePropagatorBuilder(createInitialOrbit(), provider, getPositionAngleType(), + getPositionScale(), getAttitudeProvider(), m2); + } + /** {@inheritDoc} */ public BrouwerLyddanePropagator buildPropagator(final double[] normalizedParameters) { setParameters(normalizedParameters); @@ -249,6 +265,8 @@ public BrouwerLyddanePropagator buildPropagator(final double[] normalizedParamet boolean isSelected = false; for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) { if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) { + // it is OK as BL m2 parameterDriver has 1 value estimated from -INF to +INF, and + // setPeriod method should not be called on this driver (to have several values estimated) newM2 = driver.getValue(); isSelected = driver.isSelected(); } @@ -265,20 +283,11 @@ public BrouwerLyddanePropagator buildPropagator(final double[] normalizedParamet /** {@inheritDoc} */ @Override - public AbstractBatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { + public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } - /** {@inheritDoc} */ - @Override - public AbstractKalmanModel buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - return new KalmanModel(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementsParameters, measurementProcessNoiseMatrix); - } - } diff --git a/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaFieldIntegratorBuilder.java new file mode 100644 index 0000000000..0f5e68dd65 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaFieldIntegratorBuilder.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Builder for ClassicalRungeKuttaFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class ClassicalRungeKuttaFieldIntegratorBuilder> extends + AbstractFixedStepFieldIntegratorBuilder { + + /** + * Constructor. + * + * @param step step size (s) + * + * @see ClassicalRungeKuttaFieldIntegrator + */ + public ClassicalRungeKuttaFieldIntegratorBuilder(final double step) { + super(step); + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + * + * @see ClassicalRungeKuttaFieldIntegrator + */ + public ClassicalRungeKuttaFieldIntegratorBuilder(final T step) { + super(step); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + return new ClassicalRungeKuttaFieldIntegrator<>(field, getFieldStep(field)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilder.java index 1d6d0685e6..578dfa0020 100644 --- a/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/ClassicalRungeKuttaIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java index 2d5a18739c..05e118bab4 100644 --- a/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,19 +19,16 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; - import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.leastsquares.DSSTBatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -46,7 +43,7 @@ * @author Bryan Cazabonne * @since 10.0 */ -public class DSSTPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { +public class DSSTPropagatorBuilder extends AbstractPropagatorBuilder { /** First order integrator builder for propagation. */ private final ODEIntegratorBuilder builder; @@ -71,6 +68,7 @@ public class DSSTPropagatorBuilder extends AbstractPropagatorBuilder implements * with the {@code positionScale} to convert from the {@link * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the * callers of this builder to the real orbital parameters. + * The default attitude provider is aligned with the orbit's inertial frame. *

        * * @param referenceOrbit reference orbit from which real orbits will be built @@ -88,7 +86,7 @@ public DSSTPropagatorBuilder(final Orbit referenceOrbit, final PropagationType propagationType, final PropagationType stateType) { this(referenceOrbit, builder, positionScale, propagationType, stateType, - InertialProvider.of(referenceOrbit.getFrame())); + FrameAlignedProvider.of(referenceOrbit.getFrame())); } /** Build a new instance. @@ -115,7 +113,7 @@ public DSSTPropagatorBuilder(final Orbit referenceOrbit, final PropagationType propagationType, final PropagationType stateType, final AttitudeProvider attitudeProvider) { - super(referenceOrbit, PositionAngle.MEAN, positionScale, true, attitudeProvider); + super(referenceOrbit, PositionAngleType.MEAN, positionScale, true, attitudeProvider); this.builder = builder; this.forceModels = new ArrayList(); this.mass = Propagator.DEFAULT_MASS; @@ -142,7 +140,7 @@ public PropagationType getStateType() { */ public DSSTPropagatorBuilder copy() { final DSSTPropagatorBuilder copyBuilder = - new DSSTPropagatorBuilder((EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(createInitialOrbit()), + new DSSTPropagatorBuilder(createInitialOrbit(), builder, getPositionScale(), propagationType, @@ -213,9 +211,7 @@ public void addForceModel(final DSSTForceModel model) { } } - for (final ParameterDriver driver : model.getParametersDrivers()) { - addSupportedParameter(driver); - } + addSupportedParameters(model.getParametersDrivers()); } /** Reset the orbit in the propagator builder. @@ -228,7 +224,6 @@ public void resetOrbit(final Orbit newOrbit, final PropagationType orbitType) { } /** {@inheritDoc} */ - @SuppressWarnings("deprecation") public DSSTPropagator buildPropagator(final double[] normalizedParameters) { setParameters(normalizedParameters); @@ -257,41 +252,21 @@ public DSSTPropagator buildPropagator(final double[] normalizedParameters) { propagator.addAdditionalDerivativesProvider(provider); } - // FIXME: remove in 12.0 when AdditionalEquations is removed - for (org.orekit.propagation.integration.AdditionalEquations equations : getAdditionalEquations()) { - propagator.addAdditionalDerivativesProvider(new org.orekit.propagation.integration.AdditionalEquationsAdapter(equations, propagator::getInitialState)); - } - return propagator; } /** {@inheritDoc} */ @Override - public DSSTBatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { + public DSSTBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { return new DSSTBatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer, - propagationType, stateType); - } - - /** {@inheritDoc} */ - @Override - @SuppressWarnings("deprecation") - public AbstractKalmanModel buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - // FIXME: remove in 12.0 when DSSTKalmanModel is removed - return new org.orekit.estimation.sequential.DSSTKalmanModel(propagatorBuilders, - covarianceMatricesProviders, - estimatedMeasurementsParameters, - measurementProcessNoiseMatrix, - propagationType, stateType); + propagationType); } /** Check if Newtonian attraction force model is available. diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java new file mode 100644 index 0000000000..488a57c8b0 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince54FieldIntegratorBuilder.java @@ -0,0 +1,58 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.DormandPrince54FieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Builder for DormandPrince54FieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class DormandPrince54FieldIntegratorBuilder> + extends AbstractVariableStepFieldIntegratorBuilder { + + /** + * Build a new instance. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * + * @see DormandPrince54FieldIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public DormandPrince54FieldIntegratorBuilder(final double minStep, final double maxStep, final double dP) { + super(minStep, maxStep, dP); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + return new DormandPrince54FieldIntegrator<>(field, minStep, maxStep, tol[0], tol[1]); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java index 49bde8649f..3e04df44d4 100644 --- a/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince54IntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java new file mode 100644 index 0000000000..8232fbbbff --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince853FieldIntegratorBuilder.java @@ -0,0 +1,58 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Builder for DormandPrince853FieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class DormandPrince853FieldIntegratorBuilder> + extends AbstractVariableStepFieldIntegratorBuilder { + + /** + * Build a new instance. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * + * @see DormandPrince853FieldIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public DormandPrince853FieldIntegratorBuilder(final double minStep, final double maxStep, final double dP) { + super(minStep, maxStep, dP); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + return new DormandPrince853FieldIntegrator<>(field, minStep, maxStep, tol[0], tol[1]); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java index 67d3329af6..f1a859ad97 100644 --- a/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DormandPrince853IntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java index 93fd9f5b20..9f23966605 100644 --- a/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/EcksteinHechlerPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,20 +19,17 @@ import java.util.List; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.leastsquares.AbstractBatchLSModel; import org.orekit.estimation.leastsquares.BatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.estimation.sequential.KalmanModel; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.TideSystem; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.analytical.EcksteinHechlerPropagator; import org.orekit.utils.ParameterDriversList; @@ -41,7 +38,7 @@ * @author Pascal Parraud * @since 6.0 */ -public class EcksteinHechlerPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { +public class EcksteinHechlerPropagatorBuilder extends AbstractPropagatorBuilder { /** Provider for un-normalized coefficients. */ private final UnnormalizedSphericalHarmonicsProvider provider; @@ -54,25 +51,26 @@ public class EcksteinHechlerPropagatorBuilder extends AbstractPropagatorBuilder * used together with the {@code positionScale} to convert from the {@link * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters used by the * callers of this builder to the real orbital parameters. + * The default attitude provider is aligned with the orbit's inertial frame. *

        * * @param templateOrbit reference orbit from which real orbits will be built * (note that the mu from this orbit will be overridden with the mu from the * {@code provider}) * @param provider for un-normalized zonal coefficients - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @since 8.0 * @see #EcksteinHechlerPropagatorBuilder(Orbit, - * UnnormalizedSphericalHarmonicsProvider, PositionAngle, double, AttitudeProvider) + * UnnormalizedSphericalHarmonicsProvider, PositionAngleType, double, AttitudeProvider) */ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, final UnnormalizedSphericalHarmonicsProvider provider, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale) { - this(templateOrbit, provider, positionAngle, positionScale, - InertialProvider.of(templateOrbit.getFrame())); + this(templateOrbit, provider, positionAngleType, positionScale, + FrameAlignedProvider.of(templateOrbit.getFrame())); } /** Build a new instance. @@ -88,7 +86,7 @@ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, * (note that the mu from this orbit will be overridden with the mu from the * {@code provider}) * @param provider for un-normalized zonal coefficients - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param attitudeProvider attitude law to use. @@ -96,11 +94,11 @@ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, */ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, final UnnormalizedSphericalHarmonicsProvider provider, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final AttitudeProvider attitudeProvider) { - super(overrideMu(templateOrbit, provider, positionAngle), positionAngle, - positionScale, true, attitudeProvider); + super(overrideMu(templateOrbit, provider, positionAngleType), positionAngleType, + positionScale, true, attitudeProvider); this.provider = provider; } @@ -126,12 +124,12 @@ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth) * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth) * @param orbitType orbit type to use - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @since 8.0 * @see #EcksteinHechlerPropagatorBuilder(Orbit, - * UnnormalizedSphericalHarmonicsProvider, PositionAngle, double, AttitudeProvider) + * UnnormalizedSphericalHarmonicsProvider, PositionAngleType, double, AttitudeProvider) */ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, final double referenceRadius, @@ -143,7 +141,7 @@ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, final double c50, final double c60, final OrbitType orbitType, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale) { this(templateOrbit, GravityFieldFactory.getUnnormalizedProvider(referenceRadius, mu, tideSystem, @@ -180,22 +178,22 @@ public EcksteinHechlerPropagatorBuilder(final Orbit templateOrbit, 0 } }), - positionAngle, positionScale); + positionAngleType, positionScale); } /** Override central attraction coefficient. * @param templateOrbit template orbit * @param provider gravity field provider - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @return orbit with overridden central attraction coefficient */ private static Orbit overrideMu(final Orbit templateOrbit, final UnnormalizedSphericalHarmonicsProvider provider, - final PositionAngle positionAngle) { + final PositionAngleType positionAngleType) { final double[] parameters = new double[6]; final double[] parametersDot = templateOrbit.hasDerivatives() ? new double[6] : null; - templateOrbit.getType().mapOrbitToArray(templateOrbit, positionAngle, parameters, parametersDot); - return templateOrbit.getType().mapArrayToOrbit(parameters, parametersDot, positionAngle, + templateOrbit.getType().mapOrbitToArray(templateOrbit, positionAngleType, parameters, parametersDot); + return templateOrbit.getType().mapArrayToOrbit(parameters, parametersDot, positionAngleType, templateOrbit.getDate(), provider.getMu(), templateOrbit.getFrame()); @@ -210,20 +208,17 @@ public Propagator buildPropagator(final double[] normalizedParameters) { /** {@inheritDoc} */ @Override - public AbstractBatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { + public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } /** {@inheritDoc} */ @Override - public AbstractKalmanModel buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - return new KalmanModel(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementsParameters, measurementProcessNoiseMatrix); + public EcksteinHechlerPropagatorBuilder copy() { + return new EcksteinHechlerPropagatorBuilder(createInitialOrbit(), provider, getPositionAngleType(), + getPositionScale(), getAttitudeProvider()); } - } diff --git a/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java index 913aeb2aa3..3642442ff4 100644 --- a/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java @@ -16,27 +16,34 @@ */ package org.orekit.propagation.conversion; -import java.util.List; - import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.leastsquares.AbstractBatchLSModel; import org.orekit.estimation.leastsquares.BatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.estimation.sequential.KalmanModel; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.StateCovariance; import org.orekit.propagation.analytical.Ephemeris; +import org.orekit.time.TimeInterpolator; +import org.orekit.time.TimeStampedPair; import org.orekit.utils.ParameterDriversList; -/** Builder for Ephemeris propagator. +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Builder for Ephemeris propagator. + * * @author Bryan Cazabonne + * @author Vincent Cucchietti * @since 11.3 */ -public class EphemerisPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { +public class EphemerisPropagatorBuilder extends AbstractPropagatorBuilder { /** Default position scale (not used for ephemeris based estimation). */ private static final double DEFAULT_SCALE = 10.0; @@ -44,55 +51,119 @@ public class EphemerisPropagatorBuilder extends AbstractPropagatorBuilder implem /** List of spacecraft states. */ private final List states; - /** The extrapolation threshold beyond which the propagation will fail. **/ - private final double extrapolationThreshold; + /** List of covariances. **/ + private final Optional> covariances; - /** Number of points to use in interpolation. */ - private final int interpolationPoints; + /** Spacecraft state interpolator. */ + private final TimeInterpolator stateInterpolator; + + /** State covariance interpolator. */ + private final Optional>> covarianceInterpolator; /** Attitude provider. */ private final AttitudeProvider provider; - /** Constructor. + /** + * Constructor using the default attitude provider. + *

        + * The default attitude provider is an {@link org.orekit.attitudes.FrameAlignedProvider inertial provider} built from the frame of the first + * spacecraft state instance in given list + * * @param states list of spacecraft states - * @param interpolationPoints number of points to use in interpolation - * @param extrapolationThreshold the extrapolation threshold beyond which the propagation will fail - * @param attitudeProvider attitude provider + * @param stateInterpolator spacecraft state interpolator */ public EphemerisPropagatorBuilder(final List states, - final int interpolationPoints, - final double extrapolationThreshold, + final TimeInterpolator stateInterpolator) { + this(states, stateInterpolator, states.isEmpty() ? null : new FrameAlignedProvider(states.get(0).getFrame())); + } + + /** + * Constructor. + * + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * @param attitudeProvider attitude law to use + */ + public EphemerisPropagatorBuilder(final List states, + final TimeInterpolator stateInterpolator, final AttitudeProvider attitudeProvider) { - super(states.get(0).getOrbit(), PositionAngle.TRUE, DEFAULT_SCALE, false, attitudeProvider); + this(states, stateInterpolator, new ArrayList<>(), null, attitudeProvider); + } + + /** + * Constructor with covariances and default attitude provider. + *

        + * The default attitude provider is an {@link FrameAlignedProvider inertial provider} built from the frame of the first + * spacecraft state instance in given list + * + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * @param covariances tabulated covariances associated to tabulated states + * @param covarianceInterpolator covariance interpolator + * + * @see StateCovariance + * @see FrameAlignedProvider + */ + public EphemerisPropagatorBuilder(final List states, + final TimeInterpolator stateInterpolator, + final List covariances, + final TimeInterpolator> covarianceInterpolator) { + this(states, stateInterpolator, covariances, covarianceInterpolator, + states.isEmpty() ? null : new FrameAlignedProvider(states.get(0).getFrame())); + } + + /** + * Constructor. + * + * @param states list of spacecraft states + * @param stateInterpolator spacecraft state interpolator + * @param covariances tabulated covariances associated to tabulated states + * @param covarianceInterpolator covariance interpolator + * @param attitudeProvider attitude law to use + */ + public EphemerisPropagatorBuilder(final List states, + final TimeInterpolator stateInterpolator, + final List covariances, + final TimeInterpolator> covarianceInterpolator, + final AttitudeProvider attitudeProvider) { + super(states.get(0).getOrbit(), PositionAngleType.TRUE, DEFAULT_SCALE, false, attitudeProvider); deselectDynamicParameters(); + + // Check input consistency the same way Ephemeris is checking consistency + Ephemeris.checkInputConsistency(states, stateInterpolator, covariances, covarianceInterpolator); + this.states = states; - this.interpolationPoints = interpolationPoints; - this.extrapolationThreshold = extrapolationThreshold; + this.stateInterpolator = stateInterpolator; + this.covariances = Optional.ofNullable(covariances); + this.covarianceInterpolator = Optional.ofNullable(covarianceInterpolator); this.provider = attitudeProvider; } - /** {@inheritDoc}. */ + /** {@inheritDoc} */ @Override - public Propagator buildPropagator(final double[] normalizedParameters) { - return new Ephemeris(states, interpolationPoints, extrapolationThreshold, provider); + public EphemerisPropagatorBuilder copy() { + return new EphemerisPropagatorBuilder(states, stateInterpolator, + covariances.orElse(null), covarianceInterpolator.orElse(null), + provider); } - /** {@inheritDoc} */ + /** {@inheritDoc}. */ @Override - public AbstractBatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); + public Propagator buildPropagator(final double[] normalizedParameters) { + if (covariances.isPresent() && covarianceInterpolator.isPresent()) { + return new Ephemeris(states, stateInterpolator, covariances.get(), covarianceInterpolator.get(), provider); + } + return new Ephemeris(states, stateInterpolator, provider); + } /** {@inheritDoc} */ @Override - public AbstractKalmanModel buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - return new KalmanModel(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementsParameters, measurementProcessNoiseMatrix); + public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { + return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } } diff --git a/src/main/java/org/orekit/propagation/conversion/EulerFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EulerFieldIntegratorBuilder.java new file mode 100644 index 0000000000..7b6e8afcfb --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/EulerFieldIntegratorBuilder.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.EulerFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Builder for EulerFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class EulerFieldIntegratorBuilder> extends + AbstractFixedStepFieldIntegratorBuilder { + + /** + * Constructor. + * + * @param step step size (s) + * + * @see EulerFieldIntegrator + */ + public EulerFieldIntegratorBuilder(final double step) { + super(step); + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + * + * @see EulerFieldIntegrator + */ + public EulerFieldIntegratorBuilder(final T step) { + super(step); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + return new EulerFieldIntegrator<>(field, getFieldStep(field)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/EulerIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EulerIntegratorBuilder.java index d63a9ff585..8dc836e803 100644 --- a/src/main/java/org/orekit/propagation/conversion/EulerIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/EulerIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java new file mode 100644 index 0000000000..44329cc649 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/FieldODEIntegratorBuilder.java @@ -0,0 +1,59 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * This interface is the top-level abstraction to build first order integrators for propagators conversion. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public interface FieldODEIntegratorBuilder> { + + /** + * Build a first order integrator. + * + * @param field field to which the elements belong + * @param orbit reference orbit + * @param orbitType orbit type to use + * + * @return a first order integrator ready to use + */ + AbstractFieldIntegrator buildIntegrator(Field field, + Orbit orbit, + OrbitType orbitType); + + /** + * Build a first order integrator. + * + * @param orbit reference orbit + * @param orbitType orbit type to use + * + * @return a first order integrator ready to use + */ + AbstractFieldIntegrator buildIntegrator(FieldOrbit orbit, + OrbitType orbitType); +} diff --git a/src/main/java/org/orekit/propagation/conversion/FiniteDifferencePropagatorConverter.java b/src/main/java/org/orekit/propagation/conversion/FiniteDifferencePropagatorConverter.java index 053d246881..d0e554c9dc 100644 --- a/src/main/java/org/orekit/propagation/conversion/FiniteDifferencePropagatorConverter.java +++ b/src/main/java/org/orekit/propagation/conversion/FiniteDifferencePropagatorConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/GillFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/GillFieldIntegratorBuilder.java new file mode 100644 index 0000000000..ca4989f714 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/GillFieldIntegratorBuilder.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.GillFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Builder for GillFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class GillFieldIntegratorBuilder> + extends AbstractFixedStepFieldIntegratorBuilder { + + /** + * Constructor. + * + * @param step step size (s) + * + * @see GillFieldIntegrator + */ + public GillFieldIntegratorBuilder(final double step) { + super(step); + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + * + * @see GillFieldIntegrator + */ + public GillFieldIntegratorBuilder(final T step) { + super(step); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + return new GillFieldIntegrator<>(field, getFieldStep(field)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/GillIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/GillIntegratorBuilder.java index cbb58c9ed7..d1e35dcf1a 100644 --- a/src/main/java/org/orekit/propagation/conversion/GillIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/GillIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java index d568f6753f..af5b6bc2dd 100644 --- a/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/GraggBulirschStoerIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java new file mode 100644 index 0000000000..59572dba1b --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/HighamHall54FieldIntegratorBuilder.java @@ -0,0 +1,58 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.HighamHall54FieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.numerical.NumericalPropagator; + +/** + * Builder for HighamHall54Integrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class HighamHall54FieldIntegratorBuilder> + extends AbstractVariableStepFieldIntegratorBuilder { + + /** + * Build a new instance. + * + * @param minStep minimum step size (s) + * @param maxStep maximum step size (s) + * @param dP position error (m) + * + * @see HighamHall54FieldIntegrator + * @see NumericalPropagator#tolerances(double, Orbit, OrbitType) + */ + public HighamHall54FieldIntegratorBuilder(final double minStep, final double maxStep, final double dP) { + super(minStep, maxStep, dP); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + final double[][] tol = NumericalPropagator.tolerances(dP, orbit, orbitType); + return new HighamHall54FieldIntegrator<>(field, minStep, maxStep, tol[0], tol[1]); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java index 98e3bc72ca..e6625d6c51 100644 --- a/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/HighamHall54IntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/JacobianPropagatorConverter.java b/src/main/java/org/orekit/propagation/conversion/JacobianPropagatorConverter.java index 034a7581c4..5d7ab189e4 100644 --- a/src/main/java/org/orekit/propagation/conversion/JacobianPropagatorConverter.java +++ b/src/main/java/org/orekit/propagation/conversion/JacobianPropagatorConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -37,6 +37,7 @@ import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; +import org.orekit.utils.TimeSpanMap.Span; /** Propagator converter using the real Jacobian. * @author Pascal Parraud @@ -257,14 +258,16 @@ private void fillRows(final int row, for (int j = 0; j < dYdP.getColumnDimension(); ++j) { final String name = harvester.getJacobiansColumnsNames().get(j); for (final ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) { - if (name.equals(driver.getName())) { - jacobian.setEntry(row + k, column++, dYdP.getEntry(k, j) * driver.getScale()); + + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (name.equals(span.getData())) { + jacobian.setEntry(row + k, column++, dYdP.getEntry(k, j) * driver.getScale()); + } } } } } } - } } diff --git a/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java index 58017f09ca..37ad64d69a 100644 --- a/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/KeplerianPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,16 +19,13 @@ import java.util.List; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.leastsquares.AbstractBatchLSModel; import org.orekit.estimation.leastsquares.BatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.estimation.sequential.KalmanModel; import org.orekit.orbits.Orbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.analytical.KeplerianPropagator; import org.orekit.utils.ParameterDriversList; @@ -37,7 +34,7 @@ * @author Pascal Parraud * @since 6.0 */ -public class KeplerianPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { +public class KeplerianPropagatorBuilder extends AbstractPropagatorBuilder { /** Build a new instance. *

        @@ -47,19 +44,20 @@ public class KeplerianPropagatorBuilder extends AbstractPropagatorBuilder implem * used together with the {@code positionScale} to convert from the {@link * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters used by the * callers of this builder to the real orbital parameters. + * The default attitude provider is aligned with the orbit's inertial frame. *

        * * @param templateOrbit reference orbit from which real orbits will be built - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @since 8.0 - * @see #KeplerianPropagatorBuilder(Orbit, PositionAngle, double, AttitudeProvider) + * @see #KeplerianPropagatorBuilder(Orbit, PositionAngleType, double, AttitudeProvider) */ - public KeplerianPropagatorBuilder(final Orbit templateOrbit, final PositionAngle positionAngle, + public KeplerianPropagatorBuilder(final Orbit templateOrbit, final PositionAngleType positionAngleType, final double positionScale) { - this(templateOrbit, positionAngle, positionScale, - InertialProvider.of(templateOrbit.getFrame())); + this(templateOrbit, positionAngleType, positionScale, + FrameAlignedProvider.of(templateOrbit.getFrame())); } /** Build a new instance. @@ -72,17 +70,24 @@ public KeplerianPropagatorBuilder(final Orbit templateOrbit, final PositionAngle * callers of this builder to the real orbital parameters. *

        * @param templateOrbit reference orbit from which real orbits will be built - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param attitudeProvider attitude law to use. * @since 10.1 */ public KeplerianPropagatorBuilder(final Orbit templateOrbit, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final AttitudeProvider attitudeProvider) { - super(templateOrbit, positionAngle, positionScale, true, attitudeProvider); + super(templateOrbit, positionAngleType, positionScale, true, attitudeProvider); + } + + /** {@inheritDoc} */ + @Override + public KeplerianPropagatorBuilder copy() { + return new KeplerianPropagatorBuilder(createInitialOrbit(), getPositionAngleType(), + getPositionScale(), getAttitudeProvider()); } /** {@inheritDoc} */ @@ -93,20 +98,11 @@ public Propagator buildPropagator(final double[] normalizedParameters) { /** {@inheritDoc} */ @Override - public AbstractBatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { + public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } - /** {@inheritDoc} */ - @Override - public AbstractKalmanModel buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - return new KalmanModel(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementsParameters, measurementProcessNoiseMatrix); - } - } diff --git a/src/main/java/org/orekit/propagation/conversion/LutherFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/LutherFieldIntegratorBuilder.java new file mode 100644 index 0000000000..c1c5fdda65 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/LutherFieldIntegratorBuilder.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.LutherFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Builder for LutherFieldIntegrator. + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class LutherFieldIntegratorBuilder> + extends AbstractFixedStepFieldIntegratorBuilder { + + /** + * Constructor. + * + * @param step step size (s) + * + * @see LutherFieldIntegrator + */ + public LutherFieldIntegratorBuilder(final double step) { + super(step); + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + * + * @see LutherFieldIntegrator + */ + public LutherFieldIntegratorBuilder(final T step) { + super(step); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + return new LutherFieldIntegrator<>(field, getFieldStep(field)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/LutherIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/LutherIntegratorBuilder.java index 5b7afcc626..a1e45f3c4a 100644 --- a/src/main/java/org/orekit/propagation/conversion/LutherIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/LutherIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/MidpointFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/MidpointFieldIntegratorBuilder.java new file mode 100644 index 0000000000..40e416b618 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/MidpointFieldIntegratorBuilder.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.MidpointFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Builder for MidpointFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class MidpointFieldIntegratorBuilder> + extends AbstractFixedStepFieldIntegratorBuilder { + + /** + * Constructor. + * + * @param step step size (s) + * + * @see MidpointFieldIntegrator + */ + public MidpointFieldIntegratorBuilder(final double step) { + super(step); + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + * + * @see MidpointFieldIntegrator + */ + public MidpointFieldIntegratorBuilder(final T step) { + super(step); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + return new MidpointFieldIntegrator<>(field, getFieldStep(field)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/MidpointIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/MidpointIntegratorBuilder.java index 6be27eb279..1849059f60 100644 --- a/src/main/java/org/orekit/propagation/conversion/MidpointIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/MidpointIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java index 03f7547a5b..aae4b8e6c2 100644 --- a/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/NumericalPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,16 +22,14 @@ import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.estimation.leastsquares.BatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.estimation.sequential.KalmanModel; import org.orekit.forces.ForceModel; import org.orekit.forces.gravity.NewtonianAttraction; import org.orekit.orbits.Orbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AdditionalDerivativesProvider; @@ -43,7 +41,7 @@ * @author Pascal Parraud * @since 6.0 */ -public class NumericalPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { +public class NumericalPropagatorBuilder extends AbstractPropagatorBuilder { /** First order integrator builder for propagation. */ private final ODEIntegratorBuilder builder; @@ -62,23 +60,24 @@ public class NumericalPropagatorBuilder extends AbstractPropagatorBuilder implem * with the {@code positionScale} to convert from the {@link * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the * callers of this builder to the real orbital parameters. + * The default attitude provider is aligned with the orbit's inertial frame. *

        * * @param referenceOrbit reference orbit from which real orbits will be built * @param builder first order integrator builder - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @since 8.0 - * @see #NumericalPropagatorBuilder(Orbit, ODEIntegratorBuilder, PositionAngle, + * @see #NumericalPropagatorBuilder(Orbit, ODEIntegratorBuilder, PositionAngleType, * double, AttitudeProvider) */ public NumericalPropagatorBuilder(final Orbit referenceOrbit, final ODEIntegratorBuilder builder, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale) { - this(referenceOrbit, builder, positionAngle, positionScale, - InertialProvider.of(referenceOrbit.getFrame())); + this(referenceOrbit, builder, positionAngleType, positionScale, + FrameAlignedProvider.of(referenceOrbit.getFrame())); } /** Build a new instance. @@ -92,7 +91,7 @@ public NumericalPropagatorBuilder(final Orbit referenceOrbit, *

        * @param referenceOrbit reference orbit from which real orbits will be built * @param builder first order integrator builder - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param attitudeProvider attitude law. @@ -100,10 +99,10 @@ public NumericalPropagatorBuilder(final Orbit referenceOrbit, */ public NumericalPropagatorBuilder(final Orbit referenceOrbit, final ODEIntegratorBuilder builder, - final PositionAngle positionAngle, + final PositionAngleType positionAngleType, final double positionScale, final AttitudeProvider attitudeProvider) { - super(referenceOrbit, positionAngle, positionScale, true, attitudeProvider); + super(referenceOrbit, positionAngleType, positionScale, true, attitudeProvider); this.builder = builder; this.forceModels = new ArrayList(); this.mass = Propagator.DEFAULT_MASS; @@ -116,7 +115,7 @@ public NumericalPropagatorBuilder copy() { final NumericalPropagatorBuilder copyBuilder = new NumericalPropagatorBuilder(createInitialOrbit(), builder, - getPositionAngle(), + getPositionAngleType(), getPositionScale(), getAttitudeProvider()); copyBuilder.setMass(mass); @@ -171,9 +170,7 @@ public void addForceModel(final ForceModel model) { } } - for (final ParameterDriver driver : model.getParametersDrivers()) { - addSupportedParameter(driver); - } + addSupportedParameters(model.getParametersDrivers()); } /** Get the mass. @@ -193,7 +190,6 @@ public void setMass(final double mass) { } /** {@inheritDoc} */ - @SuppressWarnings("deprecation") public NumericalPropagator buildPropagator(final double[] normalizedParameters) { setParameters(normalizedParameters); @@ -206,7 +202,7 @@ public NumericalPropagator buildPropagator(final double[] normalizedParameters) builder.buildIntegrator(orbit, getOrbitType()), getAttitudeProvider()); propagator.setOrbitType(getOrbitType()); - propagator.setPositionAngleType(getPositionAngle()); + propagator.setPositionAngleType(getPositionAngleType()); // Configure force models if (!hasNewtonianAttraction()) { @@ -224,30 +220,17 @@ public NumericalPropagator buildPropagator(final double[] normalizedParameters) propagator.addAdditionalDerivativesProvider(provider); } - // FIXME: remove in 12.0 when AdditionalEquations is removed - for (org.orekit.propagation.integration.AdditionalEquations equations : getAdditionalEquations()) { - propagator.addAdditionalDerivativesProvider(new org.orekit.propagation.integration.AdditionalEquationsAdapter(equations, propagator::getInitialState)); - } - return propagator; - } - /** {@inheritDoc} */ - public BatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } /** {@inheritDoc} */ @Override - public KalmanModel buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - return new KalmanModel(propagatorBuilders, covarianceMatricesProviders, - estimatedMeasurementsParameters, measurementProcessNoiseMatrix); + public BatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { + return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } /** Check if Newtonian attraction force model is available. diff --git a/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java index 8225c69f16..fa6c83b3c7 100644 --- a/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/ODEIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/OrbitDeterminationPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/OrbitDeterminationPropagatorBuilder.java deleted file mode 100644 index aba9368102..0000000000 --- a/src/main/java/org/orekit/propagation/conversion/OrbitDeterminationPropagatorBuilder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.conversion; - -import java.util.List; - -import org.orekit.estimation.leastsquares.AbstractBatchLSModel; -import org.orekit.estimation.leastsquares.ModelObserver; -import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.orbits.Orbit; -import org.orekit.utils.ParameterDriversList; - -/** Base class for orbit determination model builders. - * @author Bryan Cazabonne - * @since 11.0 - */ -public interface OrbitDeterminationPropagatorBuilder extends PropagatorBuilder { - - /** Build a new batch least squares model. - * @param builders builders to use for propagation - * @param measurements measurements - * @param estimatedMeasurementsParameters estimated measurements parameters - * @param observer observer to be notified at model calls - * @return a new model for the Batch Least Squares orbit determination - */ - AbstractBatchLSModel buildLSModel(OrbitDeterminationPropagatorBuilder[] builders, - List> measurements, - ParameterDriversList estimatedMeasurementsParameters, - ModelObserver observer); - - /** Build a new Kalman model. - * @param propagatorBuilders propagators builders used to evaluate the orbits. - * @param covarianceMatricesProviders providers for covariance matrices - * @param estimatedMeasurementsParameters measurement parameters to estimate - * @param measurementProcessNoiseMatrix provider for measurement process noise matrix - * @return a new model for Kalman Filter orbit determination - */ - AbstractKalmanModel buildKalmanModel(List propagatorBuilders, - List covarianceMatricesProviders, - ParameterDriversList estimatedMeasurementsParameters, - CovarianceMatrixProvider measurementProcessNoiseMatrix); - - /** Reset the orbit in the propagator builder. - * @param newOrbit New orbit to set in the propagator builder - */ - void resetOrbit(Orbit newOrbit); - -} diff --git a/src/main/java/org/orekit/propagation/conversion/OsculatingToMeanElementsConverter.java b/src/main/java/org/orekit/propagation/conversion/OsculatingToMeanElementsConverter.java index 597d1e52de..89ff8eb34a 100644 --- a/src/main/java/org/orekit/propagation/conversion/OsculatingToMeanElementsConverter.java +++ b/src/main/java/org/orekit/propagation/conversion/OsculatingToMeanElementsConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,7 @@ */ package org.orekit.propagation.conversion; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -80,7 +80,7 @@ public final SpacecraftState convert() { propagator.resetInitialState(state); final FiniteDifferencePropagatorConverter converter = new FiniteDifferencePropagatorConverter(new KeplerianPropagatorBuilder(state.getOrbit(), - PositionAngle.MEAN, + PositionAngleType.MEAN, positionScale, propagator.getAttitudeProvider()), 1.e-6, MAX_EVALUATION); diff --git a/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java index 12d3de29b6..4df68a9ff4 100644 --- a/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,9 +16,15 @@ */ package org.orekit.propagation.conversion; +import java.util.List; + +import org.orekit.estimation.leastsquares.AbstractBatchLSModel; +import org.orekit.estimation.leastsquares.ModelObserver; +import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.frames.Frame; +import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriversList; @@ -29,12 +35,30 @@ */ public interface PropagatorBuilder { + /** Create a new instance identical to this one. + * @return new instance identical to this one + */ + PropagatorBuilder copy(); + /** Build a propagator. * @param normalizedParameters normalized values for the selected parameters * @return an initialized propagator */ Propagator buildPropagator(double[] normalizedParameters); + /** Build a new batch least squares model. + * @param builders builders to use for propagation + * @param measurements measurements + * @param estimatedMeasurementsParameters estimated measurements parameters + * @param observer observer to be notified at model calls + * @return a new model for the Batch Least Squares orbit determination + * @since 12.0 + */ + AbstractBatchLSModel buildLeastSquaresModel(PropagatorBuilder[] builders, + List> measurements, + ParameterDriversList estimatedMeasurementsParameters, + ModelObserver observer); + /** Get the current value of selected normalized parameters. * @return current value of selected normalized parameters */ @@ -44,7 +68,7 @@ public interface PropagatorBuilder { * {@link #buildPropagator(double[])}. * @return orbit type to use in {@link #buildPropagator(double[])} * @see #buildPropagator(double[]) - * @see #getPositionAngle() + * @see #getPositionAngleType() * @since 7.1 */ OrbitType getOrbitType(); @@ -56,7 +80,7 @@ public interface PropagatorBuilder { * @see #getOrbitType() * @since 7.1 */ - PositionAngle getPositionAngle(); + PositionAngleType getPositionAngleType(); /** Get the date of the initial orbit. * @return date of the initial orbit @@ -68,7 +92,14 @@ public interface PropagatorBuilder { */ Frame getFrame(); + /** Get the central attraction coefficient (µ - m³/s²) value. + * @return the central attraction coefficient (µ - m³/s²) value + * @since 12.0 + */ + double getMu(); + /** Get the drivers for the configurable orbital parameters. + * Orbital drivers should have only 1 value estimated (1 span) * @return drivers for the configurable orbital parameters * @since 8.0 */ @@ -83,4 +114,10 @@ public interface PropagatorBuilder { */ ParameterDriversList getPropagationParametersDrivers(); + /** Reset the orbit in the propagator builder. + * @param newOrbit New orbit to set in the propagator builder + * @since 12.0 + */ + void resetOrbit(Orbit newOrbit); + } diff --git a/src/main/java/org/orekit/propagation/conversion/PropagatorConverter.java b/src/main/java/org/orekit/propagation/conversion/PropagatorConverter.java index 791ffed5ef..3da9c1f23a 100644 --- a/src/main/java/org/orekit/propagation/conversion/PropagatorConverter.java +++ b/src/main/java/org/orekit/propagation/conversion/PropagatorConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java index d70cfa89e1..201f8d55cd 100644 --- a/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/TLEPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,23 +19,20 @@ import java.util.List; import org.orekit.annotation.DefaultDataContext; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; import org.orekit.estimation.leastsquares.AbstractBatchLSModel; import org.orekit.estimation.leastsquares.BatchLSModel; import org.orekit.estimation.leastsquares.ModelObserver; import org.orekit.estimation.measurements.ObservedMeasurement; -import org.orekit.estimation.sequential.AbstractKalmanModel; -import org.orekit.estimation.sequential.CovarianceMatrixProvider; -import org.orekit.estimation.sequential.KalmanModel; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.tle.TLE; import org.orekit.propagation.analytical.tle.TLEPropagator; -import org.orekit.time.TimeScale; +import org.orekit.propagation.analytical.tle.generation.TleGenerationAlgorithm; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; @@ -44,13 +41,7 @@ * @author Thomas Paulet * @since 6.0 */ -public class TLEPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder { - - /** Default value for epsilon. */ - private static final double EPSILON_DEFAULT = 1.0e-10; - - /** Default value for maxIterations. */ - private static final int MAX_ITERATIONS_DEFAULT = 100; +public class TLEPropagatorBuilder extends AbstractPropagatorBuilder { /** Data context used to access frames and time scales. */ private final DataContext dataContext; @@ -58,11 +49,8 @@ public class TLEPropagatorBuilder extends AbstractPropagatorBuilder implements O /** Template TLE. */ private final TLE templateTLE; - /** Threshold for convergence used in TLE generation. */ - private final double epsilon; - - /** Maximum number of iterations for convergence used in TLE generation. */ - private final int maxIterations; + /** TLE generation algorithm. */ + private final TleGenerationAlgorithm generationAlgorithm; /** Build a new instance. This constructor uses the {@link DataContext#getDefault() * default data context}. @@ -73,21 +61,19 @@ public class TLEPropagatorBuilder extends AbstractPropagatorBuilder implements O * classification, .... and is also used together with the {@code positionScale} to * convert from the {@link ParameterDriver#setNormalizedValue(double) normalized} * parameters used by the callers of this builder to the real orbital parameters. - *

        - * Using this constructor, {@link #EPSILON_DEFAULT} and {@link #MAX_ITERATIONS_DEFAULT} - * are used for spacecraft's state to TLE transformation *

        * @param templateTLE reference TLE from which real orbits will be built - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) - * @since 7.1 - * @see #TLEPropagatorBuilder(TLE, PositionAngle, double, DataContext) + * @param generationAlgorithm TLE generation algorithm + * @since 12.0 + * @see #TLEPropagatorBuilder(TLE, PositionAngleType, double, DataContext, TleGenerationAlgorithm) */ @DefaultDataContext - public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngle positionAngle, - final double positionScale) { - this(templateTLE, positionAngle, positionScale, DataContext.getDefault()); + public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType positionAngleType, + final double positionScale, final TleGenerationAlgorithm generationAlgorithm) { + this(templateTLE, positionAngleType, positionScale, DataContext.getDefault(), generationAlgorithm); } /** Build a new instance. @@ -98,86 +84,35 @@ public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngle positionA * classification, .... and is also used together with the {@code positionScale} to * convert from the {@link ParameterDriver#setNormalizedValue(double) normalized} * parameters used by the callers of this builder to the real orbital parameters. - *

        - * Using this constructor, {@link #EPSILON_DEFAULT} and {@link #MAX_ITERATIONS_DEFAULT} - * are used for spacecraft's state to TLE transformation + * The default attitude provider is aligned with the orbit's inertial frame. *

        * @param templateTLE reference TLE from which real orbits will be built - * @param positionAngle position angle type to use + * @param positionAngleType position angle type to use * @param positionScale scaling factor used for orbital parameters normalization * (typically set to the expected standard deviation of the position) * @param dataContext used to access frames and time scales. - * @since 10.1 - * @see #TLEPropagatorBuilder(TLE, PositionAngle, double, DataContext, double, int) + * @param generationAlgorithm TLE generation algorithm + * @since 12.0 */ - public TLEPropagatorBuilder(final TLE templateTLE, - final PositionAngle positionAngle, - final double positionScale, - final DataContext dataContext) { - this(templateTLE, positionAngle, positionScale, dataContext, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT); + public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngleType positionAngleType, + final double positionScale, final DataContext dataContext, + final TleGenerationAlgorithm generationAlgorithm) { + super(TLEPropagator.selectExtrapolator(templateTLE, dataContext.getFrames().getTEME()).getInitialState().getOrbit(), + positionAngleType, positionScale, false, FrameAlignedProvider.of(dataContext.getFrames().getTEME())); + + // Supported parameters: Bstar + addSupportedParameters(templateTLE.getParametersDrivers()); + + this.templateTLE = templateTLE; + this.dataContext = dataContext; + this.generationAlgorithm = generationAlgorithm; } - /** Build a new instance. This constructor uses the {@link DataContext#getDefault() - * default data context}. - *

        - * The template TLE is used as a model to {@link - * #createInitialOrbit() create initial orbit}. It defines the - * inertial frame, the central attraction coefficient, orbit type, satellite number, - * classification, .... and is also used together with the {@code positionScale} to - * convert from the {@link ParameterDriver#setNormalizedValue(double) normalized} - * parameters used by the callers of this builder to the real orbital parameters. - *

        - * @param templateTLE reference TLE from which real orbits will be built - * @param positionAngle position angle type to use - * @param positionScale scaling factor used for orbital parameters normalization - * (typically set to the expected standard deviation of the position) - * @param epsilon used to compute threshold for convergence check - * @param maxIterations maximum number of iterations for convergence - * @since 11.0.2 - * @see #TLEPropagatorBuilder(TLE, PositionAngle, double, DataContext, double, int) - */ - @DefaultDataContext - public TLEPropagatorBuilder(final TLE templateTLE, final PositionAngle positionAngle, - final double positionScale, final double epsilon, - final int maxIterations) { - this(templateTLE, positionAngle, positionScale, DataContext.getDefault(), epsilon, maxIterations); - } - - /** Build a new instance. - *

        - * The template TLE is used as a model to {@link - * #createInitialOrbit() create initial orbit}. It defines the - * inertial frame, the central attraction coefficient, orbit type, satellite number, - * classification, .... and is also used together with the {@code positionScale} to - * convert from the {@link ParameterDriver#setNormalizedValue(double) normalized} - * parameters used by the callers of this builder to the real orbital parameters. - *

        - * @param templateTLE reference TLE from which real orbits will be built - * @param positionAngle position angle type to use - * @param positionScale scaling factor used for orbital parameters normalization - * (typically set to the expected standard deviation of the position) - * @param dataContext used to access frames and time scales. - * @param epsilon used to compute threshold for convergence check - * @param maxIterations maximum number of iterations for convergence - * @since 11.0.2 - */ - public TLEPropagatorBuilder(final TLE templateTLE, - final PositionAngle positionAngle, - final double positionScale, - final DataContext dataContext, - final double epsilon, - final int maxIterations) { - super(TLEPropagator.selectExtrapolator(templateTLE, dataContext.getFrames()) - .getInitialState().getOrbit(), - positionAngle, positionScale, false, - InertialProvider.of(dataContext.getFrames().getTEME())); - for (final ParameterDriver driver : templateTLE.getParametersDrivers()) { - addSupportedParameter(driver); - } - this.templateTLE = templateTLE; - this.dataContext = dataContext; - this.epsilon = epsilon; - this.maxIterations = maxIterations; + /** {@inheritDoc} */ + @Override + public TLEPropagatorBuilder copy() { + return new TLEPropagatorBuilder(templateTLE, getPositionAngleType(), getPositionScale(), + dataContext, generationAlgorithm); } /** {@inheritDoc} */ @@ -189,10 +124,9 @@ public TLEPropagator buildPropagator(final double[] normalizedParameters) { final Orbit orbit = createInitialOrbit(); final SpacecraftState state = new SpacecraftState(orbit); final Frame teme = dataContext.getFrames().getTEME(); - final TimeScale utc = dataContext.getTimeScales().getUTC(); // TLE related to the orbit - final TLE tle = TLE.stateToTLE(state, templateTLE, utc, teme, epsilon, maxIterations); + final TLE tle = generationAlgorithm.generate(state, templateTLE); final List drivers = templateTLE.getParametersDrivers(); for (int index = 0; index < drivers.size(); index++) { if (drivers.get(index).isSelected()) { @@ -201,10 +135,7 @@ public TLEPropagator buildPropagator(final double[] normalizedParameters) { } // propagator - return TLEPropagator.selectExtrapolator(tle, - getAttitudeProvider(), - Propagator.DEFAULT_MASS, - teme); + return TLEPropagator.selectExtrapolator(tle, getAttitudeProvider(), Propagator.DEFAULT_MASS, teme); } @@ -216,20 +147,12 @@ public TLE getTemplateTLE() { } /** {@inheritDoc} */ - public AbstractBatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders, - final List> measurements, - final ParameterDriversList estimatedMeasurementsParameters, - final ModelObserver observer) { - return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); - } - @Override - public AbstractKalmanModel - buildKalmanModel(final List propagatorBuilders, - final List covarianceMatricesProviders, - final ParameterDriversList estimatedMeasurementsParameters, - final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - return new KalmanModel(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementsParameters, measurementProcessNoiseMatrix); + public AbstractBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders, + final List> measurements, + final ParameterDriversList estimatedMeasurementsParameters, + final ModelObserver observer) { + return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer); } } diff --git a/src/main/java/org/orekit/propagation/conversion/ThreeEighthesFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/ThreeEighthesFieldIntegratorBuilder.java new file mode 100644 index 0000000000..d9ebcd0aae --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/ThreeEighthesFieldIntegratorBuilder.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.conversion; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.AbstractFieldIntegrator; +import org.hipparchus.ode.nonstiff.ThreeEighthesFieldIntegrator; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; + +/** + * Builder for ThreeEighthesFieldIntegrator. + * + * @author Pascal Parraud + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class ThreeEighthesFieldIntegratorBuilder> + extends AbstractFixedStepFieldIntegratorBuilder { + + /** + * Constructor. + * + * @param step step size (s) + * + * @see ThreeEighthesFieldIntegrator + */ + public ThreeEighthesFieldIntegratorBuilder(final double step) { + super(step); + } + + /** + * Constructor using a "fielded" step. + *

        + * WARNING : Given "fielded" step must be using the same field as the one that will be used when calling + * {@link #buildIntegrator} + * + * @param step step size (s) + * + * @see ThreeEighthesFieldIntegrator + */ + public ThreeEighthesFieldIntegratorBuilder(final T step) { + super(step); + } + + /** {@inheritDoc} */ + @Override + public AbstractFieldIntegrator buildIntegrator(final Field field, final Orbit orbit, final OrbitType orbitType) { + return new ThreeEighthesFieldIntegrator<>(field, getFieldStep(field)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/ThreeEighthesIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/ThreeEighthesIntegratorBuilder.java index 99e0d01c73..e546da808a 100644 --- a/src/main/java/org/orekit/propagation/conversion/ThreeEighthesIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/ThreeEighthesIntegratorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/conversion/package-info.java b/src/main/java/org/orekit/propagation/conversion/package-info.java index 6df88b0f06..09a2680f89 100644 --- a/src/main/java/org/orekit/propagation/conversion/package-info.java +++ b/src/main/java/org/orekit/propagation/conversion/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/events/AbstractDetector.java b/src/main/java/org/orekit/propagation/events/AbstractDetector.java index e93cd4c0bc..887b27ddc1 100644 --- a/src/main/java/org/orekit/propagation/events/AbstractDetector.java +++ b/src/main/java/org/orekit/propagation/events/AbstractDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,7 +16,6 @@ */ package org.orekit.propagation.events; -import org.hipparchus.ode.events.Action; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.propagation.SpacecraftState; @@ -24,6 +23,7 @@ import org.orekit.time.AbsoluteDate; /** Common parts shared by several orbital events finders. + * @param type of the detector * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) * @author Luc Maisonobe */ @@ -39,7 +39,7 @@ public abstract class AbstractDetector> implements public static final int DEFAULT_MAX_ITER = 100; /** Max check interval. */ - private final double maxCheck; + private final AdaptableInterval maxCheck; /** Convergence threshold. */ private final double threshold; @@ -48,7 +48,7 @@ public abstract class AbstractDetector> implements private final int maxIter; /** Default handler for event overrides. */ - private final EventHandler handler; + private final EventHandler handler; /** Propagation direction. */ private boolean forward; @@ -60,8 +60,19 @@ public abstract class AbstractDetector> implements * @param handler event handler to call at event occurrences */ protected AbstractDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler) { - checkStrictlyPositive(maxCheck); + final EventHandler handler) { + this(s -> maxCheck, threshold, maxIter, handler); + } + + /** Build a new instance. + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @since 12.0 + */ + protected AbstractDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler) { checkStrictlyPositive(threshold); this.maxCheck = maxCheck; this.threshold = threshold; @@ -88,18 +99,17 @@ private void checkStrictlyPositive(final double value) throws OrekitException { * handler. If a subclass overrides this method it should call {@code * super.init(s0, t)}. */ - @SuppressWarnings("unchecked") public void init(final SpacecraftState s0, final AbsoluteDate t) { forward = t.durationFrom(s0.getDate()) >= 0.0; - getHandler().init(s0, t, (T) this); + getHandler().init(s0, t, this); } /** {@inheritDoc} */ public abstract double g(SpacecraftState s); /** {@inheritDoc} */ - public double getMaxCheckInterval() { + public AdaptableInterval getMaxCheckInterval() { return maxCheck; } @@ -123,6 +133,19 @@ public double getThreshold() { * @since 6.1 */ public T withMaxCheck(final double newMaxCheck) { + return withMaxCheck(s -> newMaxCheck); + } + + /** + * Setup the maximum checking interval. + *

        + * This will override a maximum checking interval if it has been configured previously. + *

        + * @param newMaxCheck maximum checking interval (s) + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.0 + */ + public T withMaxCheck(final AdaptableInterval newMaxCheck) { return create(newMaxCheck, getThreshold(), getMaxIterationCount(), getHandler()); } @@ -161,29 +184,14 @@ public T withThreshold(final double newThreshold) { * @return a new detector with updated configuration (the instance is not changed) * @since 6.1 */ - public T withHandler(final EventHandler newHandler) { + public T withHandler(final EventHandler newHandler) { return create(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), newHandler); } - /** Get the handler. - * @return event handler to call at event occurrences - */ - public EventHandler getHandler() { - return handler; - } - - /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final boolean increasing) { - @SuppressWarnings("unchecked") - final Action whatNext = getHandler().eventOccurred(s, (T) this, increasing); - return whatNext; - } - /** {@inheritDoc} */ - public SpacecraftState resetState(final SpacecraftState oldState) { - @SuppressWarnings("unchecked") - final SpacecraftState newState = getHandler().resetState((T) this, oldState); - return newState; + @Override + public EventHandler getHandler() { + return handler; } /** Build a new instance. @@ -193,8 +201,8 @@ public SpacecraftState resetState(final SpacecraftState oldState) { * @param newHandler event handler to call at event occurrences * @return a new instance of the appropriate sub-type */ - protected abstract T create(double newMaxCheck, double newThreshold, - int newMaxIter, EventHandler newHandler); + protected abstract T create(AdaptableInterval newMaxCheck, double newThreshold, + int newMaxIter, EventHandler newHandler); /** Check if the current propagation is forward or backward. * @return true if the current propagation is forward diff --git a/src/main/java/org/orekit/propagation/events/AdaptableInterval.java b/src/main/java/org/orekit/propagation/events/AdaptableInterval.java new file mode 100644 index 0000000000..927ccde8f8 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/AdaptableInterval.java @@ -0,0 +1,38 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.propagation.events; + +import org.orekit.propagation.SpacecraftState; + +/** This interface represents an event checking interval that depends on state. +* +* @see EventDetector +* @author Luc Maisonobe +* @since 12.0 +* +*/ +@FunctionalInterface +public interface AdaptableInterval { + + /** Get the current value of maximal time interval between events handler checks. + * @param state current state + * @return current value of maximal time interval between events handler checks + */ + double currentInterval(SpacecraftState state); + +} diff --git a/src/main/java/org/orekit/propagation/events/AdapterDetector.java b/src/main/java/org/orekit/propagation/events/AdapterDetector.java index dd8131a405..b13a729b80 100644 --- a/src/main/java/org/orekit/propagation/events/AdapterDetector.java +++ b/src/main/java/org/orekit/propagation/events/AdapterDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,8 @@ */ package org.orekit.propagation.events; -import org.hipparchus.ode.events.Action; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; /** Base class for adapting an existing detector. @@ -69,7 +69,7 @@ public double getThreshold() { /** {@inheritDoc} */ @Override - public double getMaxCheckInterval() { + public AdaptableInterval getMaxCheckInterval() { return detector.getMaxCheckInterval(); } @@ -81,14 +81,8 @@ public int getMaxIterationCount() { /** {@inheritDoc} */ @Override - public Action eventOccurred(final SpacecraftState s, final boolean increasing) { - return detector.eventOccurred(s, increasing); - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState resetState(final SpacecraftState oldState) { - return detector.resetState(oldState); + public EventHandler getHandler() { + return detector.getHandler(); } } diff --git a/src/main/java/org/orekit/propagation/events/AlignmentDetector.java b/src/main/java/org/orekit/propagation/events/AlignmentDetector.java index d3e86e8bba..47b14dc240 100644 --- a/src/main/java/org/orekit/propagation/events/AlignmentDetector.java +++ b/src/main/java/org/orekit/propagation/events/AlignmentDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -73,8 +73,8 @@ public AlignmentDetector(final Orbit orbit, public AlignmentDetector(final double maxCheck, final double threshold, final PVCoordinatesProvider body, final double alignAngle) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, - new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, + new StopOnIncreasing(), body, alignAngle); } @@ -93,9 +93,9 @@ public AlignmentDetector(final double threshold, this(orbit.getKeplerianPeriod() / 3, threshold, body, alignAngle); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        @@ -106,10 +106,10 @@ public AlignmentDetector(final double threshold, * @param body the body to align * @param alignAngle the alignment angle (rad) */ - private AlignmentDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final PVCoordinatesProvider body, - final double alignAngle) { + protected AlignmentDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final PVCoordinatesProvider body, + final double alignAngle) { super(maxCheck, threshold, maxIter, handler); final SinCos sc = FastMath.sinCos(alignAngle); this.body = body; @@ -120,8 +120,8 @@ private AlignmentDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected AlignmentDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected AlignmentDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new AlignmentDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, body, alignAngle); } @@ -153,7 +153,7 @@ public double g(final SpacecraftState s) { final Vector3D b = Vector3D.crossProduct(pv.getMomentum(), a).normalize(); final Vector3D x = new Vector3D(cosAlignAngle, a, sinAlignAngle, b); final Vector3D y = new Vector3D(sinAlignAngle, a, -cosAlignAngle, b); - final Vector3D pb = body.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D pb = body.getPosition(s.getDate(), s.getFrame()); final double beta = FastMath.atan2(Vector3D.dotProduct(pb, y), Vector3D.dotProduct(pb, x)); final double betm = -FastMath.PI - beta; final double betp = FastMath.PI - beta; diff --git a/src/main/java/org/orekit/propagation/events/AltitudeDetector.java b/src/main/java/org/orekit/propagation/events/AltitudeDetector.java index 9f296747e6..d59bc413a4 100644 --- a/src/main/java/org/orekit/propagation/events/AltitudeDetector.java +++ b/src/main/java/org/orekit/propagation/events/AltitudeDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,6 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnDecreasing; -import org.orekit.utils.PVCoordinates; /** Finder for satellite altitude crossing events. *

        This class finds altitude events (i.e. satellite crossing @@ -86,17 +85,17 @@ public AltitudeDetector(final double maxCheck, final double threshold, final double altitude, final BodyShape bodyShape) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), altitude, bodyShape); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -104,10 +103,10 @@ public AltitudeDetector(final double maxCheck, * @param bodyShape body shape with respect to which altitude should be evaluated * @since 6.1 */ - private AltitudeDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final double altitude, - final BodyShape bodyShape) { + protected AltitudeDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final double altitude, + final BodyShape bodyShape) { super(maxCheck, threshold, maxIter, handler); this.altitude = altitude; this.bodyShape = bodyShape; @@ -115,8 +114,8 @@ private AltitudeDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected AltitudeDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected AltitudeDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new AltitudeDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, altitude, bodyShape); } @@ -143,9 +142,7 @@ public BodyShape getBodyShape() { */ public double g(final SpacecraftState s) { final Frame bodyFrame = bodyShape.getBodyFrame(); - final PVCoordinates pvBody = s.getPVCoordinates(bodyFrame); - final GeodeticPoint point = bodyShape.transform(pvBody.getPosition(), - bodyFrame, s.getDate()); + final GeodeticPoint point = bodyShape.transform(s.getPosition(bodyFrame), bodyFrame, s.getDate()); return point.getAltitude() - altitude; } diff --git a/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java b/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java index 74831ab3b4..f0c5740952 100644 --- a/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java +++ b/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -58,17 +58,17 @@ public class AngularSeparationDetector extends AbstractDetector(), + this(s -> 60.0, 1.0e-3, 100, new StopOnDecreasing(), beacon, observer, proximityAngle); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -77,12 +77,12 @@ public AngularSeparationDetector(final PVCoordinatesProvider beacon, * the beacon at the same time if they are too close to each other * @param proximityAngle proximity angle as seen from observer, at which events are triggered (rad) */ - private AngularSeparationDetector(final double maxCheck, final double threshold, - final int maxIter, - final EventHandler handler, - final PVCoordinatesProvider beacon, - final PVCoordinatesProvider observer, - final double proximityAngle) { + protected AngularSeparationDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, + final EventHandler handler, + final PVCoordinatesProvider beacon, + final PVCoordinatesProvider observer, + final double proximityAngle) { super(maxCheck, threshold, maxIter, handler); this.beacon = beacon; this.observer = observer; @@ -91,8 +91,8 @@ private AngularSeparationDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected AngularSeparationDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected AngularSeparationDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new AngularSeparationDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, beacon, observer, proximityAngle); } @@ -137,10 +137,10 @@ public double getProximityAngle() { */ public double g(final SpacecraftState s) { final PVCoordinates sPV = s.getPVCoordinates(); - final PVCoordinates bPV = beacon.getPVCoordinates(s.getDate(), s.getFrame()); - final PVCoordinates oPV = observer.getPVCoordinates(s.getDate(), s.getFrame()); - final double separation = Vector3D.angle(sPV.getPosition().subtract(oPV.getPosition()), - bPV.getPosition().subtract(oPV.getPosition())); + final Vector3D bP = beacon.getPosition(s.getDate(), s.getFrame()); + final Vector3D oP = observer.getPosition(s.getDate(), s.getFrame()); + final double separation = Vector3D.angle(sPV.getPosition().subtract(oP), + bP.subtract(oP)); return separation - proximityAngle; } diff --git a/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java b/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java index c1ce4421d3..eca6b9d8dd 100644 --- a/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java +++ b/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java @@ -58,17 +58,17 @@ public class AngularSeparationFromSatelliteDetector extends AbstractDetector(), + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnDecreasing(), primaryObject, secondaryObject, proximityAngle); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -77,12 +77,12 @@ public AngularSeparationFromSatelliteDetector(final PVCoordinatesProvider primar * the primaryObject as seen from the spacecraft * @param proximityAngle proximity angle as seen from secondaryObject, at which events are triggered (rad) */ - private AngularSeparationFromSatelliteDetector(final double maxCheck, final double threshold, - final int maxIter, - final EventHandler handler, - final PVCoordinatesProvider primaryObject, - final PVCoordinatesProvider secondaryObject, - final double proximityAngle) { + protected AngularSeparationFromSatelliteDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, + final EventHandler handler, + final PVCoordinatesProvider primaryObject, + final PVCoordinatesProvider secondaryObject, + final double proximityAngle) { super(maxCheck, threshold, maxIter, handler); this.primaryObject = primaryObject; this.secondaryObject = secondaryObject; @@ -91,8 +91,8 @@ private AngularSeparationFromSatelliteDetector(final double maxCheck, final doub /** {@inheritDoc} */ @Override - protected AngularSeparationFromSatelliteDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected AngularSeparationFromSatelliteDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new AngularSeparationFromSatelliteDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, primaryObject, secondaryObject, proximityAngle); } @@ -137,10 +137,10 @@ public double getProximityAngle() { */ public double g(final SpacecraftState s) { final PVCoordinates sPV = s.getPVCoordinates(); - final PVCoordinates primaryPV = primaryObject .getPVCoordinates(s.getDate(), s.getFrame()); - final PVCoordinates secondaryPV = secondaryObject.getPVCoordinates(s.getDate(), s.getFrame()); - final double separation = Vector3D.angle(primaryPV .getPosition().subtract(sPV.getPosition()), - secondaryPV.getPosition().subtract(sPV.getPosition())); + final Vector3D primaryPos = primaryObject .getPosition(s.getDate(), s.getFrame()); + final Vector3D secondaryPos = secondaryObject.getPosition(s.getDate(), s.getFrame()); + final double separation = Vector3D.angle(primaryPos.subtract(sPV.getPosition()), + secondaryPos.subtract(sPV.getPosition())); return separation - proximityAngle; } diff --git a/src/main/java/org/orekit/propagation/events/ApsideDetector.java b/src/main/java/org/orekit/propagation/events/ApsideDetector.java index e4b230fbad..da20b45a90 100644 --- a/src/main/java/org/orekit/propagation/events/ApsideDetector.java +++ b/src/main/java/org/orekit/propagation/events/ApsideDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -58,30 +58,30 @@ public ApsideDetector(final Orbit orbit) { */ public ApsideDetector(final double threshold, final Orbit orbit) { super(orbit.getKeplerianPeriod() / 3, threshold, - DEFAULT_MAX_ITER, new StopOnIncreasing()); + DEFAULT_MAX_ITER, new StopOnIncreasing()); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @since 6.1 */ - private ApsideDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler) { + protected ApsideDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler) { super(maxCheck, threshold, maxIter, handler); } /** {@inheritDoc} */ @Override - protected ApsideDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected ApsideDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new ApsideDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); } diff --git a/src/main/java/org/orekit/propagation/events/BooleanDetector.java b/src/main/java/org/orekit/propagation/events/BooleanDetector.java index 03795046f9..40c0b968d5 100644 --- a/src/main/java/org/orekit/propagation/events/BooleanDetector.java +++ b/src/main/java/org/orekit/propagation/events/BooleanDetector.java @@ -76,12 +76,12 @@ public class BooleanDetector extends AbstractDetector { * @param newMaxIter max iterations. * @param newHandler event handler. */ - private BooleanDetector(final List detectors, - final Operator operator, - final double newMaxCheck, - final double newThreshold, - final int newMaxIter, - final EventHandler newHandler) { + protected BooleanDetector(final List detectors, + final Operator operator, + final AdaptableInterval newMaxCheck, + final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { super(newMaxCheck, newThreshold, newMaxIter, newHandler); this.detectors = detectors; this.operator = operator; @@ -133,10 +133,16 @@ public static BooleanDetector andCombine(final Collection(detectors), // copy for immutability Operator.AND, - detectors.stream().map(EventDetector::getMaxCheckInterval).min(Double::compareTo).get(), + s -> { + double minInterval = Double.POSITIVE_INFINITY; + for (final EventDetector detector : detectors) { + minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s)); + } + return minInterval; + }, detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(), detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get(), - new ContinueOnEvent<>()); + new ContinueOnEvent()); } /** @@ -185,10 +191,16 @@ public static BooleanDetector orCombine(final Collection(detectors), // copy for immutability Operator.OR, - detectors.stream().map(EventDetector::getMaxCheckInterval).min(Double::compareTo).get(), + s -> { + double minInterval = Double.POSITIVE_INFINITY; + for (final EventDetector detector : detectors) { + minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s)); + } + return minInterval; + }, detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(), detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get(), - new ContinueOnEvent<>()); + new ContinueOnEvent()); } /** @@ -230,10 +242,10 @@ public double g(final SpacecraftState s) { } @Override - protected BooleanDetector create(final double newMaxCheck, + protected BooleanDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new BooleanDetector(detectors, operator, newMaxCheck, newThreshold, newMaxIter, newHandler); } diff --git a/src/main/java/org/orekit/propagation/events/DateDetector.java b/src/main/java/org/orekit/propagation/events/DateDetector.java index b8b38f3102..d872bc596e 100644 --- a/src/main/java/org/orekit/propagation/events/DateDetector.java +++ b/src/main/java/org/orekit/propagation/events/DateDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,10 +33,10 @@ *

        This class finds date events (i.e. occurrence of some predefined dates).

        *

        As of version 5.1, it is an enhanced date detector:

        *
          - *
        • it can be defined without prior date ({@link #DateDetector(double, double, TimeStamped...)})
        • + *
        • it can be defined without prior date ({@link #DateDetector(TimeStamped...)})
        • *
        • several dates can be added ({@link #addEventDate(AbsoluteDate)})
        • *
        - *

        The gap between the added dates must be more than the maxCheck.

        + *

        The gap between the added dates must be more than the minGap.

        *

        The default implementation behavior is to {@link Action#STOP stop} * propagation at the first event date occurrence. This can be changed by calling * {@link #withHandler(EventHandler)} after construction.

        @@ -46,6 +46,26 @@ */ public class DateDetector extends AbstractDetector implements TimeStamped { + /** Default value for max check. + * @since 12.0 + */ + public static final double DEFAULT_MAX_CHECK = 1.0e10; + + /** Default value for minimum gap between added dates. + * @since 12.0 + */ + public static final double DEFAULT_MIN_GAP = 1.0; + + /** Default value for convergence threshold. + * @since 12.0 + */ + public static final double DEFAULT_THRESHOLD = 1.0e-13; + + /** Minimum gap between added dates. + * @since 12.0 + */ + private final double minGap; + /** Last date for g computation. */ private AbsoluteDate gDate; @@ -58,42 +78,30 @@ public class DateDetector extends AbstractDetector implements Time /** Build a new instance. *

        First event dates are set here, but others can be * added later with {@link #addEventDate(AbsoluteDate)}.

        - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) * @param dates list of event dates * @see #addEventDate(AbsoluteDate) + * @since 12.0 */ - public DateDetector(final double maxCheck, final double threshold, final TimeStamped... dates) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnEvent(), dates); + public DateDetector(final TimeStamped... dates) { + this(s-> DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), DEFAULT_MIN_GAP, dates); } - /** Build a new instance. - *

        This constructor is dedicated to single date detection. - * {@link #getMaxCheckInterval() max check interval} is set to 1.0e10, so almost - * no other date can be added. Tolerance is set to 1.0e-9.

        - * @param target target date - * @see #addEventDate(AbsoluteDate) - */ - public DateDetector(final AbsoluteDate target) { - this(1.0e10, 1.e-9, target); - } - - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences + * @param minGap minimum gap between added dates (s) * @param dates list of event dates - * @since 6.1 + * @since 12.0 */ - private DateDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final TimeStamped... dates) { + protected DateDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler, final double minGap, final TimeStamped... dates) { super(maxCheck, threshold, maxIter, handler); this.currentIndex = -1; this.gDate = null; @@ -101,13 +109,26 @@ private DateDetector(final double maxCheck, final double threshold, for (final TimeStamped ts : dates) { addEventDate(ts.getDate()); } + this.minGap = minGap; + } + + /** + * Setup minimum gap between added dates. + * @param newMinGap new minimum gap between added dates + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.0 + */ + public DateDetector withMinGap(final double newMinGap) { + return new DateDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), newMinGap, + eventDateList.toArray(new EventDate[eventDateList.size()])); } /** {@inheritDoc} */ @Override - protected DateDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { - return new DateDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, + protected DateDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new DateDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, minGap, eventDateList.toArray(new EventDate[eventDateList.size()])); } @@ -149,7 +170,7 @@ public AbsoluteDate getDate() { * * @param target target date * @throws IllegalArgumentException if the date is too close from already defined interval - * @see #DateDetector(double, double, TimeStamped...) + * @see #DateDetector(TimeStamped...) */ public void addEventDate(final AbsoluteDate target) throws IllegalArgumentException { final boolean increasing; @@ -158,14 +179,14 @@ public void addEventDate(final AbsoluteDate target) throws IllegalArgumentExcept currentIndex = 0; eventDateList.add(new EventDate(target, increasing)); } else { - final int lastIndex = eventDateList.size() - 1; + final int lastIndex = eventDateList.size() - 1; final AbsoluteDate firstDate = eventDateList.get(0).getDate(); - final AbsoluteDate lastDate = eventDateList.get(lastIndex).getDate(); - if (firstDate.durationFrom(target) > getMaxCheckInterval()) { + final AbsoluteDate lastDate = eventDateList.get(lastIndex).getDate(); + if (firstDate.durationFrom(target) > minGap) { increasing = !eventDateList.get(0).isgIncrease(); eventDateList.add(0, new EventDate(target, increasing)); currentIndex++; - } else if (target.durationFrom(lastDate) > getMaxCheckInterval()) { + } else if (target.durationFrom(lastDate) > minGap) { increasing = !eventDateList.get(lastIndex).isgIncrease(); eventDateList.add(new EventDate(target, increasing)); } else { @@ -173,7 +194,7 @@ public void addEventDate(final AbsoluteDate target) throws IllegalArgumentExcept target, firstDate, lastDate, - getMaxCheckInterval(), + minGap, firstDate.durationFrom(target), target.durationFrom(lastDate)); } diff --git a/src/main/java/org/orekit/propagation/events/EclipseDetector.java b/src/main/java/org/orekit/propagation/events/EclipseDetector.java index d65804af4b..f9cba44eb4 100644 --- a/src/main/java/org/orekit/propagation/events/EclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/EclipseDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,13 +16,13 @@ */ package org.orekit.propagation.events; -import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.events.Action; -import org.hipparchus.util.FastMath; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnIncreasing; +import org.orekit.utils.ExtendedPVCoordinatesProvider; +import org.orekit.utils.OccultationEngine; import org.orekit.utils.PVCoordinatesProvider; /** Finder for satellite eclipse related events. @@ -50,18 +50,17 @@ */ public class EclipseDetector extends AbstractDetector { - /** Occulting body. */ - private final OneAxisEllipsoid occulting; - - /** Occulted body. */ - private final PVCoordinatesProvider occulted; - - /** Occulted body radius (m). */ - private final double occultedRadius; + /** Occultation engine. + * @since 12.0 + */ + private final OccultationEngine occultationEngine; /** Umbra, if true, or penumbra, if false, detection flag. */ private final boolean totalEclipse; + /** Margin to apply to eclipse angle. */ + private final double margin; + /** Build a new eclipse detector. *

        The new instance is a total eclipse (umbra) detector with default * values for maximal checking interval ({@link #DEFAULT_MAXCHECK}) @@ -69,48 +68,56 @@ public class EclipseDetector extends AbstractDetector { * @param occulted the body to be occulted * @param occultedRadius the radius of the body to be occulted (m) * @param occulting the occulting body - * @since 10.0 + * @since 12.0 */ - public EclipseDetector(final PVCoordinatesProvider occulted, final double occultedRadius, + public EclipseDetector(final ExtendedPVCoordinatesProvider occulted, final double occultedRadius, final OneAxisEllipsoid occulting) { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, - new StopOnIncreasing(), - occulted, occultedRadius, occulting, true); + this(new OccultationEngine(occulted, occultedRadius, occulting)); } - /** Private constructor with full parameters. + /** Build a new eclipse detector. + *

        The new instance is a total eclipse (umbra) detector with default + * values for maximal checking interval ({@link #DEFAULT_MAXCHECK}) + * and convergence threshold ({@link #DEFAULT_THRESHOLD}).

        + * @param occultationEngine occultation engine + * @since 12.0 + */ + public EclipseDetector(final OccultationEngine occultationEngine) { + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new StopOnIncreasing(), + occultationEngine, 0.0, true); + } + + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences - * @param occulted the body to be occulted - * @param occultedRadius the radius of the body to be occulted in meters - * @param occulting the occulting body + * @param occultationEngine occultation engine + * @param margin to apply to eclipse angle (rad) * @param totalEclipse umbra (true) or penumbra (false) detection flag - * @since 10.0 + * @since 12.0 */ - private EclipseDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final PVCoordinatesProvider occulted, final double occultedRadius, - final OneAxisEllipsoid occulting, final boolean totalEclipse) { + protected EclipseDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final OccultationEngine occultationEngine, final double margin, final boolean totalEclipse) { super(maxCheck, threshold, maxIter, handler); - this.occulted = occulted; - this.occultedRadius = FastMath.abs(occultedRadius); - this.occulting = occulting; - this.totalEclipse = totalEclipse; + this.occultationEngine = occultationEngine; + this.margin = margin; + this.totalEclipse = totalEclipse; } /** {@inheritDoc} */ @Override - protected EclipseDetector create(final double newMaxCheck, final double newThreshold, - final int nawMaxIter, final EventHandler newHandler) { + protected EclipseDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int nawMaxIter, final EventHandler newHandler) { return new EclipseDetector(newMaxCheck, newThreshold, nawMaxIter, newHandler, - occulted, occultedRadius, occulting, totalEclipse); + occultationEngine, margin, totalEclipse); } /** @@ -124,7 +131,7 @@ protected EclipseDetector create(final double newMaxCheck, final double newThres */ public EclipseDetector withUmbra() { return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occulted, occultedRadius, occulting, true); + occultationEngine, margin, true); } /** @@ -138,28 +145,38 @@ public EclipseDetector withUmbra() { */ public EclipseDetector withPenumbra() { return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occulted, occultedRadius, occulting, false); + occultationEngine, margin, false); } - /** Getter for the occulting body. - * @return the occulting body + /** + * Setup a margin to angle detection. + *

        + * A positive margin implies eclipses are "larger" hence entry occurs earlier and exit occurs later + * than a detector with 0 margin. + *

        + * @param newMargin angular margin to apply to eclipse detection (rad) + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.0 */ - public OneAxisEllipsoid getOcculting() { - return occulting; + public EclipseDetector withMargin(final double newMargin) { + return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), + occultationEngine, newMargin, totalEclipse); } - /** Getter for the occulted body. - * @return the occulted body + /** Get the angular margin used for eclipse detection. + * @return angular margin used for eclipse detection (rad) + * @since 12.0 */ - public PVCoordinatesProvider getOcculted() { - return occulted; + public double getMargin() { + return margin; } - /** Getter for the occultedRadius. - * @return the occultedRadius + /** Get the occultation engine. + * @return occultation engine + * @since 12.0 */ - public double getOccultedRadius() { - return occultedRadius; + public OccultationEngine getOccultationEngine() { + return occultationEngine; } /** Get the total eclipse detection flag. @@ -177,17 +194,10 @@ public boolean getTotalEclipse() { * @return value of the switching function */ public double g(final SpacecraftState s) { - final Vector3D pted = occulted.getPVCoordinates(s.getDate(), occulting.getBodyFrame()).getPosition(); - final Vector3D psat = s.getPVCoordinates(occulting.getBodyFrame()).getPosition(); - final Vector3D plimb = occulting.pointOnLimb(psat, pted); - final Vector3D ps = psat.subtract(pted); - final Vector3D pi = psat.subtract(plimb); - final double angle = Vector3D.angle(ps, psat); - final double rs = FastMath.asin(occultedRadius / ps.getNorm()); - if (Double.isNaN(rs)) { - return FastMath.PI; - } - final double ro = Vector3D.angle(pi, psat); - return totalEclipse ? (angle - ro + rs) : (angle - ro - rs); + final OccultationEngine.OccultationAngles angles = occultationEngine.angles(s); + return totalEclipse ? + (angles.getSeparation() - angles.getLimbRadius() + angles.getOccultedApparentRadius() + margin) : + (angles.getSeparation() - angles.getLimbRadius() - angles.getOccultedApparentRadius() + margin); } + } diff --git a/src/main/java/org/orekit/propagation/events/ElevationDetector.java b/src/main/java/org/orekit/propagation/events/ElevationDetector.java index 66f8b876e2..2034cf6d66 100644 --- a/src/main/java/org/orekit/propagation/events/ElevationDetector.java +++ b/src/main/java/org/orekit/propagation/events/ElevationDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,6 +23,7 @@ import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnDecreasing; import org.orekit.utils.ElevationMask; +import org.orekit.utils.TrackingCoordinates; /** @@ -78,18 +79,18 @@ public ElevationDetector(final TopocentricFrame topo) { */ public ElevationDetector(final double maxCheck, final double threshold, final TopocentricFrame topo) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, - new StopOnDecreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, + new StopOnDecreasing(), 0.0, null, null, topo); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -98,11 +99,11 @@ public ElevationDetector(final double maxCheck, final double threshold, * @param refractionModel reference to refraction model * @param topo reference to a topocentric model */ - private ElevationDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final double minElevation, final ElevationMask mask, - final AtmosphericRefractionModel refractionModel, - final TopocentricFrame topo) { + protected ElevationDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final double minElevation, final ElevationMask mask, + final AtmosphericRefractionModel refractionModel, + final TopocentricFrame topo) { super(maxCheck, threshold, maxIter, handler); this.minElevation = minElevation; this.elevationMask = mask; @@ -112,8 +113,8 @@ private ElevationDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected ElevationDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected ElevationDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new ElevationDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, minElevation, elevationMask, refractionModel, topo); } @@ -164,19 +165,17 @@ public TopocentricFrame getTopocentricFrame() { @Override public double g(final SpacecraftState s) { - final double trueElevation = topo.getElevation(s.getPVCoordinates().getPosition(), - s.getFrame(), s.getDate()); + final TrackingCoordinates tc = topo.getTrackingCoordinates(s.getPosition(), s.getFrame(), s.getDate()); final double calculatedElevation; if (refractionModel != null) { - calculatedElevation = trueElevation + refractionModel.getRefraction(trueElevation); + calculatedElevation = tc.getElevation() + refractionModel.getRefraction(tc.getElevation()); } else { - calculatedElevation = trueElevation; + calculatedElevation = tc.getElevation(); } if (elevationMask != null) { - final double azimuth = topo.getAzimuth(s.getPVCoordinates().getPosition(), s.getFrame(), s.getDate()); - return calculatedElevation - elevationMask.getElevation(azimuth); + return calculatedElevation - elevationMask.getElevation(tc.getAzimuth()); } else { return calculatedElevation - minElevation; } diff --git a/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java b/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java index 7dfe687f75..3f873f01e2 100644 --- a/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -63,34 +63,34 @@ public ElevationExtremumDetector(final TopocentricFrame topo) { */ public ElevationExtremumDetector(final double maxCheck, final double threshold, final TopocentricFrame topo) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), topo); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param topo topocentric frame centered on ground point */ - private ElevationExtremumDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final TopocentricFrame topo) { + protected ElevationExtremumDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final TopocentricFrame topo) { super(maxCheck, threshold, maxIter, handler); this.topo = topo; } /** {@inheritDoc} */ @Override - protected ElevationExtremumDetector create(final double newMaxCheck, final double newThreshold, + protected ElevationExtremumDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new ElevationExtremumDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, topo); } @@ -107,7 +107,7 @@ public TopocentricFrame getTopocentricFrame() { * @return spacecraft elevation */ public double getElevation(final SpacecraftState s) { - return topo.getElevation(s.getPVCoordinates().getPosition(), s.getFrame(), s.getDate()); + return topo.getElevation(s.getPosition(), s.getFrame(), s.getDate()); } /** Compute the value of the detection function. diff --git a/src/main/java/org/orekit/propagation/events/EnablingPredicate.java b/src/main/java/org/orekit/propagation/events/EnablingPredicate.java index 992787411d..e042e2b815 100644 --- a/src/main/java/org/orekit/propagation/events/EnablingPredicate.java +++ b/src/main/java/org/orekit/propagation/events/EnablingPredicate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,15 +22,15 @@ * @author Luc Maisonobe * @since 7.1 */ -public interface EnablingPredicate { +public interface EnablingPredicate { /** Compute an event enabling function of state. * @param state current state - * @param eventDetector underlying detector + * @param detector underlying detector * @param g value of the underlying detector for the current state * @return true if the event is enabled (i.e. it can be * triggered), false if it should be ignored */ - boolean eventIsEnabled(SpacecraftState state, S eventDetector, double g); + boolean eventIsEnabled(SpacecraftState state, EventDetector detector, double g); } diff --git a/src/main/java/org/orekit/propagation/events/EventDetector.java b/src/main/java/org/orekit/propagation/events/EventDetector.java index 05d97b91c6..ebc06c1fa2 100644 --- a/src/main/java/org/orekit/propagation/events/EventDetector.java +++ b/src/main/java/org/orekit/propagation/events/EventDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,8 @@ */ package org.orekit.propagation.events; -import org.hipparchus.ode.events.Action; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; /** This interface represents space-dynamics aware events detectors. @@ -29,16 +29,34 @@ * *

        Events detectors are a useful solution to meet the requirements * of propagators concerning discrete conditions. The state of each - * event detector is queried by the integrator at each step. When the - * sign of the underlying g switching function changes, the step is rejected - * and reduced, in order to make sure the sign changes occur only at steps - * boundaries.

        + * event detector is queried by the propagator from time to time, at least + * once every {@link #getMaxCheckInterval() max check interval} but it may + * be more frequent. When the sign of the underlying g switching function + * changes, a root-finding algorithm is run to precisely locate the event, + * down to a configured {@link #getThreshold() convergence threshold}. The + * {@link #getMaxCheckInterval() max check interval} is therefore devoted to + * separate roots and is often much larger than the {@link #getThreshold() + * convergence threshold}.

        * - *

        When step ends exactly at a switching function sign change, the corresponding - * event is triggered, by calling the {@link #eventOccurred(SpacecraftState, boolean)} - * method. The method can do whatever it needs with the event (logging it, performing + *

        The physical meaning of the g switching function is not really used + * by the event detection algorithms. Its varies from event detector to + * event detector. One example would be a visibility detector that could use the + * angular elevation of the satellite above horizon as a g switching function. + * In this case, the function would switch from negative to positive when the + * satellite raises above horizon and it would switch from positive to negative + * when it sets backs below horizon. Another example would be an apside detector + * that could use the dot product of position and velocity. In this case, the + * function would switch from negative to positive when the satellite crosses + * periapsis and it would switch from positive to negative when the satellite + * crosses apoapsis.

        + * + *

        When the precise state at which the g switching function changes has been + * located, the corresponding event is triggered, by calling the {@link + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} + * method from the associated {@link #getHandler() handler}. + * The method can do whatever it needs with the event (logging it, performing * some processing, ignore it ...). The return value of the method will be used by - * the propagator to stop or resume propagation, possibly changing the state vector.

        + * the propagator to stop or resume propagation, possibly changing the state vector.

        * * @author Luc Maisonobe * @author Véronique Pommier-Maurussane @@ -78,38 +96,17 @@ default void init(SpacecraftState s0, AbsoluteDate t) { /** Get maximal time interval between switching function checks. * @return maximal time interval (s) between switching function checks */ - double getMaxCheckInterval(); + AdaptableInterval getMaxCheckInterval(); /** Get maximal number of iterations in the event time search. * @return maximal number of iterations in the event time search */ int getMaxIterationCount(); - /** Handle the event. - * @param s SpaceCraft state to be used in the evaluation - * @param increasing with the event occurred in an "increasing" or "decreasing" slope direction - * @return the Action that the calling detector should pass back to the evaluation system - * @since 7.0 - */ - Action eventOccurred(SpacecraftState s, boolean increasing); - - /** Reset the state prior to continue propagation. - *

        This method is called after the step handler has returned and - * before the next step is started, but only when {@link - * #eventOccurred} has itself returned the {@link Action#RESET_STATE} - * indicator. It allows the user to reset the state for the next step, - * without perturbing the step handler of the finishing step. If the - * {@link #eventOccurred} never returns the {@link Action#RESET_STATE} - * indicator, this function will never be called, and it is safe to simply return null.

        - *

        - * The default implementation simply returns its argument. - *

        - * @param oldState old state - * @return new state - * @since 7.0 + /** Get the handler. + * @return event handler to call at event occurrences + * @since 12.0 */ - default SpacecraftState resetState(SpacecraftState oldState) { - return oldState; - } + EventHandler getHandler(); } diff --git a/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java b/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java new file mode 100644 index 0000000000..854402fee8 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/EventDetectorsProvider.java @@ -0,0 +1,177 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.FastMath; +import org.orekit.forces.ForceModel; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeStamped; +import org.orekit.time.TimeStamped; +import org.orekit.utils.ParameterDriver; + +/** Interface for building event detectors for force models and maneuver parameters. + * + *

        + * Objects implementing this interface are mainly {@link ForceModel} and {@link DSSTForceModel}. + * + * @author Luc Maisonobe + * @author Melina Vanel + * @author Maxime Journot + * @since 12.0 + */ +public interface EventDetectorsProvider { + + /** Accuracy of switching events dates (s). */ + double DATATION_ACCURACY = 1.0e-10; + + /** Get the discrete events related to the model. + * + *

        This method is not intended to be called several time, only once by a propagator, + * as it has the side effect of rebuilding the events detectors when called + * + * @return stream of event detectors + */ + Stream getEventDetectors(); + + /** Get the discrete events related to the model. + * + *

        This method is not intended to be called several time, only once by a propagator, + * as it has the side effect of rebuilding the events detectors when called + * + * @param field field to which the state belongs + * @param extends CalculusFieldElement<T> + * @return stream of event detectors + */ + > Stream> getFieldEventDetectors(Field field); + + /** Get the discrete events related to the model from a list of {@link ParameterDriver} + * + *

        Date detectors are used to cleanly stop the propagator and reset + * the state derivatives at transition dates (if any) of the parameter drivers. + * + *

        This method is not intended to be called several times, only once by a propagator, + * as it has the side effect of rebuilding the events detectors when called. + * + * @param parameterDrivers list of parameter drivers + * @return stream of event detectors + */ + default Stream getEventDetectors(List parameterDrivers) { + // If force model does not have parameter Driver, an empty stream is given as results + final ArrayList transitionDates = new ArrayList<>(); + for (final ParameterDriver driver : parameterDrivers) { + // Get the transitions' dates from the TimeSpanMap + for (AbsoluteDate date : driver.getTransitionDates()) { + transitionDates.add(date); + } + } + // Either force model does not have any parameter driver or only contains parameter driver with only 1 span + if (transitionDates.size() == 0) { + return Stream.empty(); + + } else { + // Sort transition dates chronologically + transitionDates.sort(null); + + // Find shortest duration between 2 consecutive dates + double shortestDuration = AbstractDetector.DEFAULT_MAXCHECK; + for (int i = 1; i < transitionDates.size(); i++) { + // Duration from current to previous date + shortestDuration = FastMath.min(shortestDuration, + transitionDates.get(i).durationFrom(transitionDates.get(i - 1))); + } + + // Create the date detector containing all transition dates and return it + // Max check set to half the shortest duration between 2 consecutive dates + final DateDetector datesDetector = new DateDetector(transitionDates.toArray(new TimeStamped[0])). + withMaxCheck(0.5 * shortestDuration). + withMinGap(0.5 * shortestDuration). + withThreshold(DATATION_ACCURACY). + withHandler((state, d, increasing) -> { + return Action.RESET_DERIVATIVES; + }); + return Stream.of(datesDetector); + } + } + + /** Get the discrete events related to the model from a list of {@link ParameterDriver} + * + *

        Date detectors are used to cleanly stop the propagator and reset + * the state derivatives at transition dates (if any) of the parameter drivers. + * + *

        This method is not intended to be called several times, only once by a propagator, + * as it has the side effect of rebuilding the events detectors when called. + * + * @param parameterDrivers list of parameter drivers + * @param field field to which the state belongs + * @param extends CalculusFieldElement<T> + * @return stream of event detectors + */ + default > Stream> getFieldEventDetectors(Field field, List parameterDrivers) { + // If force model does not have parameter Driver, an empty stream is given as results + final ArrayList transitionDates = new ArrayList<>(); + for (ParameterDriver driver : parameterDrivers) { + // Get the transitions' dates from the TimeSpanMap + for (AbsoluteDate date : driver.getTransitionDates()) { + transitionDates.add(date); + } + } + // Either force model does not have any parameter driver or only contains parameter driver with only 1 span + if (transitionDates.size() == 0) { + return Stream.empty(); + + } else { + // Sort transition dates chronologically + transitionDates.sort(null); + + // Find shortest duration between 2 consecutive dates + double shortestDuration = AbstractDetector.DEFAULT_MAXCHECK; + for (int i = 1; i < transitionDates.size(); i++) { + // Duration from current to previous date + shortestDuration = FastMath.min(shortestDuration, + transitionDates.get(i).durationFrom(transitionDates.get(i - 1))); + } + + // Initialize the date detector + // Max check set to half the shortest duration between 2 consecutive dates + @SuppressWarnings("unchecked") + final FieldDateDetector datesDetector = + new FieldDateDetector<>(field, (FieldTimeStamped[]) Array.newInstance(FieldTimeStamped.class, 0)). + withMaxCheck(0.5 * shortestDuration). + withMinGap(0.5 * shortestDuration). + withThreshold(field.getZero().newInstance(DATATION_ACCURACY)). + withHandler(( state, d, increasing) -> { + return Action.RESET_DERIVATIVES; + }); + // Add all transitions' dates to the date detector + for (int i = 0; i < transitionDates.size(); i++) { + datesDetector.addEventDate(new FieldAbsoluteDate<>(field, transitionDates.get(i))); + } + // Return the detectors + return Stream.of(datesDetector); + } + } +} diff --git a/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java b/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java index fb2cab25be..4becc19d2c 100644 --- a/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java +++ b/src/main/java/org/orekit/propagation/events/EventEnablingPredicateFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,7 +44,7 @@ * in order to avoid wasting time looking for uninteresting events. * The wrapper will intercept the calls to the {@link * EventDetector#g(SpacecraftState) g function} and to the {@link - * EventDetector#eventOccurred(SpacecraftState, boolean) + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) * eventOccurred} method in order to ignore uninteresting events. The * wrapped regular {@link EventDetector event detector} will the see only * the interesting events, i.e. either only events that occur when a @@ -56,17 +56,17 @@ * @since 7.1 */ -public class EventEnablingPredicateFilter - extends AbstractDetector> { +public class EventEnablingPredicateFilter + extends AbstractDetector { /** Number of past transformers updates stored. */ private static final int HISTORY_SIZE = 100; /** Wrapped event detector. */ - private final T rawDetector; + private final EventDetector rawDetector; /** Enabling predicate function. */ - private final EnablingPredicate enabler; + private final EnablingPredicate enabler; /** Transformers of the g function. */ private final Transformer[] transformers; @@ -87,30 +87,30 @@ public class EventEnablingPredicateFilter * @param rawDetector event detector to wrap * @param enabler event enabling predicate function to use */ - public EventEnablingPredicateFilter(final T rawDetector, - final EnablingPredicate enabler) { + public EventEnablingPredicateFilter(final EventDetector rawDetector, + final EnablingPredicate enabler) { this(rawDetector.getMaxCheckInterval(), rawDetector.getThreshold(), - rawDetector.getMaxIterationCount(), new LocalHandler(), + rawDetector.getMaxIterationCount(), new LocalHandler(), rawDetector, enabler); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param rawDetector event detector to wrap * @param enabler event enabling function to use */ - private EventEnablingPredicateFilter(final double maxCheck, final double threshold, - final int maxIter, final EventHandler> handler, - final T rawDetector, - final EnablingPredicate enabler) { + protected EventEnablingPredicateFilter(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final EventDetector rawDetector, + final EnablingPredicate enabler) { super(maxCheck, threshold, maxIter, handler); this.rawDetector = rawDetector; this.enabler = enabler; @@ -120,10 +120,10 @@ private EventEnablingPredicateFilter(final double maxCheck, final double thresho /** {@inheritDoc} */ @Override - protected EventEnablingPredicateFilter create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, - final EventHandler> newHandler) { - return new EventEnablingPredicateFilter(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, enabler); + protected EventEnablingPredicateFilter create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { + return new EventEnablingPredicateFilter(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, enabler); } /** @@ -285,18 +285,20 @@ private Transformer selectTransformer(final Transformer previous, final double p } /** Local handler. */ - private static class LocalHandler implements EventHandler> { + private static class LocalHandler implements EventHandler { /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final EventEnablingPredicateFilter ef, final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { + final EventEnablingPredicateFilter ef = (EventEnablingPredicateFilter) detector; final Transformer transformer = ef.forward ? ef.transformers[ef.transformers.length - 1] : ef.transformers[0]; - return ef.rawDetector.eventOccurred(s, transformer == Transformer.PLUS ? increasing : !increasing); + return ef.rawDetector.getHandler().eventOccurred(s, ef.rawDetector, transformer == Transformer.PLUS ? increasing : !increasing); } /** {@inheritDoc} */ @Override - public SpacecraftState resetState(final EventEnablingPredicateFilter ef, final SpacecraftState oldState) { - return ef.rawDetector.resetState(oldState); + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { + final EventEnablingPredicateFilter ef = (EventEnablingPredicateFilter) detector; + return ef.rawDetector.getHandler().resetState(ef.rawDetector, oldState); } } diff --git a/src/main/java/org/orekit/propagation/events/EventShifter.java b/src/main/java/org/orekit/propagation/events/EventShifter.java index 93bde36c92..cb068fa442 100644 --- a/src/main/java/org/orekit/propagation/events/EventShifter.java +++ b/src/main/java/org/orekit/propagation/events/EventShifter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,13 +34,12 @@ * and a negative times shift for decreasing events (eclipse entry).

        * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) * @see EventDetector - * @param class type for the generic version * @author Luc Maisonobe */ -public class EventShifter extends AbstractDetector> { +public class EventShifter extends AbstractDetector { /** Event detector for the raw unshifted event. */ - private final T detector; + private final EventDetector detector; /** Indicator for using shifted or unshifted states at event occurrence. */ private final boolean useShiftedStates; @@ -55,48 +54,48 @@ public class EventShifter extends AbstractDetectorThe {@link #getMaxCheckInterval() max check interval}, the * {@link #getThreshold() convergence threshold} of the raw unshifted * events will be used for the shifted event. When an event occurs, - * the {@link #eventOccurred(SpacecraftState, boolean) eventOccurred} + * the {@link EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} * method of the raw unshifted events will be called (with spacecraft * state at either the shifted or the unshifted event date depending * on the useShiftedStates parameter).

        * @param detector event detector for the raw unshifted event * @param useShiftedStates if true, the state provided to {@link - * #eventOccurred(SpacecraftState, boolean) eventOccurred} method of - * the detector will remain shifted, otherwise it will + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} method of + * the associated {@code handler} will remain shifted, otherwise it will * be unshifted to correspond to the underlying raw event. * @param increasingTimeShift increasing events time shift. * @param decreasingTimeShift decreasing events time shift. */ - public EventShifter(final T detector, final boolean useShiftedStates, + public EventShifter(final EventDetector detector, final boolean useShiftedStates, final double increasingTimeShift, final double decreasingTimeShift) { this(detector.getMaxCheckInterval(), detector.getThreshold(), - detector.getMaxIterationCount(), new LocalHandler(), + detector.getMaxIterationCount(), new LocalHandler(), detector, useShiftedStates, increasingTimeShift, decreasingTimeShift); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param detector event detector for the raw unshifted event * @param useShiftedStates if true, the state provided to {@link - * #eventOccurred(SpacecraftState, boolean) eventOccurred} method of + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} method of * the detector will remain shifted, otherwise it will * be unshifted to correspond to the underlying raw event. * @param increasingTimeShift increasing events time shift. * @param decreasingTimeShift decreasing events time shift. * @since 6.1 */ - private EventShifter(final double maxCheck, final double threshold, - final int maxIter, final EventHandler> handler, - final T detector, final boolean useShiftedStates, - final double increasingTimeShift, final double decreasingTimeShift) { + protected EventShifter(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final EventDetector detector, final boolean useShiftedStates, + final double increasingTimeShift, final double decreasingTimeShift) { super(maxCheck, threshold, maxIter, handler); this.detector = detector; this.useShiftedStates = useShiftedStates; @@ -106,10 +105,10 @@ private EventShifter(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected EventShifter create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler> newHandler) { - return new EventShifter(newMaxCheck, newThreshold, newMaxIter, newHandler, - detector, useShiftedStates, -increasingOffset, -decreasingOffset); + protected EventShifter create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new EventShifter(newMaxCheck, newThreshold, newMaxIter, newHandler, + detector, useShiftedStates, -increasingOffset, -decreasingOffset); } /** @@ -151,14 +150,15 @@ public double g(final SpacecraftState s) { } /** Local class for handling events. */ - private static class LocalHandler implements EventHandler> { + private static class LocalHandler implements EventHandler { /** Shifted state at even occurrence. */ private SpacecraftState shiftedState; /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final EventShifter shifter, final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { + final EventShifter shifter = (EventShifter) detector; if (shifter.useShiftedStates) { // the state provided by the caller already includes the time shift shiftedState = s; @@ -168,14 +168,15 @@ public Action eventOccurred(final SpacecraftState s, final EventShifter shift shiftedState = s.shiftedBy(offset); } - return shifter.detector.eventOccurred(shiftedState, increasing); + return shifter.detector.getHandler().eventOccurred(shiftedState, shifter.detector, increasing); } /** {@inheritDoc} */ @Override - public SpacecraftState resetState(final EventShifter shifter, final SpacecraftState oldState) { - return shifter.detector.resetState(shiftedState); + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { + final EventShifter shifter = (EventShifter) detector; + return shifter.detector.getHandler().resetState(shifter.detector, shiftedState); } } diff --git a/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java b/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java index 8ee9be21ab..36e8da6e0d 100644 --- a/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java +++ b/src/main/java/org/orekit/propagation/events/EventSlopeFilter.java @@ -52,13 +52,14 @@ * in order to avoid wasting time looking for uninteresting events. * The wrapper will intercept the calls to the {@link * EventDetector#g(SpacecraftState) g function} and to the {@link - * EventDetector#eventOccurred(SpacecraftState, boolean) + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) * eventOccurred} method in order to ignore uninteresting events. The * wrapped regular {@link EventDetector event detector} will then see only * the interesting events, i.e. either only {@code increasing} events or * only {@code decreasing} events. The number of calls to the {@link * EventDetector#g(SpacecraftState) g function} will also be reduced.

        * @see EventEnablingPredicateFilter + * @param type of the detector */ public class EventSlopeFilter extends AbstractDetector> { @@ -90,17 +91,17 @@ public class EventSlopeFilter extends AbstractDetector< */ public EventSlopeFilter(final T rawDetector, final FilterType filter) { this(rawDetector.getMaxCheckInterval(), rawDetector.getThreshold(), - rawDetector.getMaxIterationCount(), new LocalHandler(), + rawDetector.getMaxIterationCount(), new LocalHandler<>(), rawDetector, filter); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -108,9 +109,9 @@ public EventSlopeFilter(final T rawDetector, final FilterType filter) { * @param filter filter to use * @since 6.1 */ - private EventSlopeFilter(final double maxCheck, final double threshold, - final int maxIter, final EventHandler> handler, - final T rawDetector, final FilterType filter) { + protected EventSlopeFilter(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final T rawDetector, final FilterType filter) { super(maxCheck, threshold, maxIter, handler); this.rawDetector = rawDetector; this.filter = filter; @@ -120,8 +121,8 @@ private EventSlopeFilter(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected EventSlopeFilter create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler> newHandler) { + protected EventSlopeFilter create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new EventSlopeFilter(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, filter); } @@ -130,10 +131,17 @@ protected EventSlopeFilter create(final double newMaxCheck, final double newT * @return the wrapped raw detector * @since 11.1 */ - public EventDetector getDetector() { + public T getDetector() { return rawDetector; } + /** Get filter type. + * @return filter type + */ + public FilterType getFilter() { + return filter; + } + /** {@inheritDoc} */ public void init(final SpacecraftState s0, final AbsoluteDate t) { @@ -240,17 +248,21 @@ public double g(final SpacecraftState s) { } /** Local handler. */ - private static class LocalHandler implements EventHandler> { + private static class LocalHandler implements EventHandler { /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final EventSlopeFilter ef, final boolean increasing) { - return ef.rawDetector.eventOccurred(s, ef.filter.getTriggeredIncreasing()); + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { + @SuppressWarnings("unchecked") + final EventSlopeFilter esf = (EventSlopeFilter) detector; + return esf.rawDetector.getHandler().eventOccurred(s, esf.rawDetector, esf.filter.getTriggeredIncreasing()); } /** {@inheritDoc} */ @Override - public SpacecraftState resetState(final EventSlopeFilter ef, final SpacecraftState oldState) { - return ef.rawDetector.resetState(oldState); + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { + @SuppressWarnings("unchecked") + final EventSlopeFilter esf = (EventSlopeFilter) detector; + return esf.rawDetector.getHandler().resetState(esf.rawDetector, oldState); } } diff --git a/src/main/java/org/orekit/propagation/events/EventState.java b/src/main/java/org/orekit/propagation/events/EventState.java index fdcd311df1..09f8f9796f 100644 --- a/src/main/java/org/orekit/propagation/events/EventState.java +++ b/src/main/java/org/orekit/propagation/events/EventState.java @@ -30,6 +30,7 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; @@ -55,6 +56,9 @@ public class EventState { /** Event detector. */ private T detector; + /** Event handler. */ + private EventHandler handler; + /** Time of the previous call to g. */ private AbsoluteDate lastT; @@ -104,6 +108,7 @@ public class EventState { */ public EventState(final T detector) { this.detector = detector; + this.handler = detector.getHandler(); // some dummy values ... lastT = AbsoluteDate.PAST_INFINITY; @@ -196,28 +201,30 @@ public boolean evaluateStep(final OrekitStepInterpolator interpolator) throws MathRuntimeException { forward = interpolator.isForward(); + final SpacecraftState s0 = interpolator.getPreviousState(); final SpacecraftState s1 = interpolator.getCurrentState(); final AbsoluteDate t1 = s1.getDate(); final double dt = t1.durationFrom(t0); if (FastMath.abs(dt) < detector.getThreshold()) { // we cannot do anything on such a small step, don't trigger any events + pendingEvent = false; + pendingEventTime = null; return false; } - // number of points to check in the current step - final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt) / detector.getMaxCheckInterval())); - final double h = dt / n; AbsoluteDate ta = t0; double ga = g0; - for (int i = 0; i < n; ++i) { + for (SpacecraftState sb = nextCheck(s0, s1, interpolator); + sb != null; + sb = nextCheck(sb, s1, interpolator)) { // evaluate handler value at the end of the substep - final AbsoluteDate tb = (i == n - 1) ? t1 : t0.shiftedBy((i + 1) * h); - final double gb = g(interpolator.getInterpolatedState(tb)); + final AbsoluteDate tb = sb.getDate(); + final double gb = g(sb); // check events occurrence - if (gb == 0.0 || (g0Positive ^ (gb > 0))) { + if (gb == 0.0 || (g0Positive ^ gb > 0)) { // there is a sign change: an event is expected during this step if (findRoot(interpolator, ta, ga, tb, gb)) { return true; @@ -237,6 +244,29 @@ public boolean evaluateStep(final OrekitStepInterpolator interpolator) } + /** Estimate next state to check. + * @param done state already checked + * @param target target state towards which we are checking + * @param interpolator step interpolator for the proposed step + * @return intermediate state to check, or exactly {@code null} + * if we already have {@code done == target} + * @since 12.0 + */ + private SpacecraftState nextCheck(final SpacecraftState done, final SpacecraftState target, + final OrekitStepInterpolator interpolator) { + if (done == target) { + // we have already reached target + return null; + } else { + // we have to select some intermediate state + // attempting to split the remaining time in an integer number of checks + final double dt = target.getDate().durationFrom(done.getDate()); + final double maxCheck = detector.getMaxCheckInterval().currentInterval(done); + final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt) / maxCheck)); + return n == 1 ? target : interpolator.getInterpolatedState(done.getDate().shiftedBy(dt / n)); + } + } + /** * Find a root in a bracketing interval. * @@ -484,7 +514,7 @@ public boolean tryAdvance(final SpacecraftState state, /** * Notify the user's listener of the event. The event occurs wholly within this method - * call including a call to {@link EventDetector#resetState(SpacecraftState)} + * call including a call to {@link EventHandler#resetState(EventDetector, SpacecraftState)} * if necessary. * * @param state the state at the time of the event. This must be at the same time as @@ -501,10 +531,10 @@ public EventOccurrence doEvent(final SpacecraftState state) { check(pendingEvent); check(state.getDate().equals(this.pendingEventTime)); - final Action action = detector.eventOccurred(state, increasing == forward); + final Action action = handler.eventOccurred(state, detector, increasing == forward); final SpacecraftState newState; if (action == Action.RESET_STATE) { - newState = detector.resetState(state); + newState = handler.resetState(detector, state); } else { newState = state; } @@ -556,7 +586,7 @@ private AbsoluteDate shiftedBy(final AbsoluteDate t, final double delta) { * @return min(a, b) if forward, else max (a, b) */ private AbsoluteDate minTime(final AbsoluteDate a, final AbsoluteDate b) { - return (forward ^ (a.compareTo(b) > 0)) ? a : b; + return (forward ^ a.compareTo(b) > 0) ? a : b; } /** diff --git a/src/main/java/org/orekit/propagation/events/EventsLogger.java b/src/main/java/org/orekit/propagation/events/EventsLogger.java index 719d2ca3fe..3c7d7091f9 100644 --- a/src/main/java/org/orekit/propagation/events/EventsLogger.java +++ b/src/main/java/org/orekit/propagation/events/EventsLogger.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,7 +29,7 @@ * *

        As {@link EventDetector events detectors} are triggered during * orbit propagation, an event specific {@link - * EventDetector#eventOccurred(SpacecraftState, boolean) eventOccurred} + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} * method is called. This class can be used to add a global logging * feature registering all events with their corresponding states in * a chronological sequence (or reverse-chronological if propagation @@ -37,7 +37,7 @@ *

        This class works by wrapping user-provided {@link EventDetector * events detectors} before they are registered to the propagator. The * wrapper monitor the calls to {@link - * EventDetector#eventOccurred(SpacecraftState, boolean) eventOccurred} + * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} * and store the corresponding events as {@link LoggedEvent} instances. * After propagation is complete, the user can retrieve all the events * that have occurred at once by calling method {@link #getLoggedEvents()}.

        @@ -81,7 +81,7 @@ public EventsLogger() { * @param class type for the generic version */ public EventDetector monitorDetector(final T monitoredDetector) { - return new LoggingWrapper(monitoredDetector); + return new LoggingWrapper(monitoredDetector); } /** Clear the logged events. @@ -141,7 +141,7 @@ public AbsoluteDate getDate() { /** Get the triggering state. * @return triggering state - * @see EventDetector#eventOccurred(SpacecraftState, boolean) + * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) */ public SpacecraftState getState() { return state; @@ -149,7 +149,7 @@ public SpacecraftState getState() { /** Get the Increasing/decreasing status of the event. * @return increasing/decreasing status of the event - * @see EventDetector#eventOccurred(SpacecraftState, boolean) + * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) */ public boolean isIncreasing() { return increasing; @@ -157,20 +157,18 @@ public boolean isIncreasing() { } - /** Internal wrapper for events detectors. - * @param class type for the generic version - */ - private class LoggingWrapper extends AbstractDetector> { + /** Internal wrapper for events detectors. */ + private class LoggingWrapper extends AbstractDetector { /** Wrapped events detector. */ - private final T detector; + private final EventDetector detector; /** Simple constructor. * @param detector events detector to wrap */ - LoggingWrapper(final T detector) { + LoggingWrapper(final EventDetector detector) { this(detector.getMaxCheckInterval(), detector.getThreshold(), - detector.getMaxIterationCount(), new LocalHandler(), + detector.getMaxIterationCount(), null, detector); } @@ -180,25 +178,25 @@ private class LoggingWrapper extends AbstractDetector - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param detector events detector to wrap * @since 6.1 */ - private LoggingWrapper(final double maxCheck, final double threshold, - final int maxIter, final EventHandler> handler, - final T detector) { + private LoggingWrapper(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final EventDetector detector) { super(maxCheck, threshold, maxIter, handler); this.detector = detector; } /** {@inheritDoc} */ @Override - protected LoggingWrapper create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler> newHandler) { - return new LoggingWrapper(newMaxCheck, newThreshold, newMaxIter, newHandler, detector); + protected LoggingWrapper create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new LoggingWrapper(newMaxCheck, newThreshold, newMaxIter, newHandler, detector); } /** Log an event. @@ -221,23 +219,26 @@ public double g(final SpacecraftState s) { return detector.g(s); } - } + /** {@inheritDoc} */ + public EventHandler getHandler() { - /** Local class for handling events. - * @param class type for the generic version - */ - private static class LocalHandler implements EventHandler> { + final EventHandler handler = detector.getHandler(); - /** {@inheritDoc} */ - public Action eventOccurred(final SpacecraftState s, final LoggingWrapper wrapper, final boolean increasing) { - wrapper.logEvent(s, increasing); - return wrapper.detector.eventOccurred(s, increasing); - } + return new EventHandler() { - /** {@inheritDoc} */ - @Override - public SpacecraftState resetState(final LoggingWrapper wrapper, final SpacecraftState oldState) { - return wrapper.detector.resetState(oldState); + /** {@inheritDoc} */ + public Action eventOccurred(final SpacecraftState s, final EventDetector d, final boolean increasing) { + logEvent(s, increasing); + return handler.eventOccurred(s, detector, increasing); + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState resetState(final EventDetector d, final SpacecraftState oldState) { + return handler.resetState(detector, oldState); + } + + }; } } diff --git a/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java b/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java index a9a6a96cd1..86de06a905 100644 --- a/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java +++ b/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -89,7 +89,7 @@ public class ExtremumApproachDetector extends AbstractDetector(), secondaryPVProvider); + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), secondaryPVProvider); } /** @@ -98,18 +98,16 @@ public ExtremumApproachDetector(final PVCoordinatesProvider secondaryPVProvider) * This constructor is to be used if the user wants to change the default behavior of the detector. *

        * - * @param maxCheck Maximum checking interval (s). + * @param maxCheck Maximum checking interval. * @param threshold Convergence threshold (s). * @param maxIter Maximum number of iterations in the event time search. * @param handler Event handler to call at event occurrences. * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum - * * approach. + * approach. * @see EventHandler */ - public ExtremumApproachDetector( - final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler, - final PVCoordinatesProvider secondaryPVProvider) { + protected ExtremumApproachDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler, final PVCoordinatesProvider secondaryPVProvider) { super(maxCheck, threshold, maxIter, handler); this.secondaryPVProvider = secondaryPVProvider; } @@ -132,15 +130,24 @@ public double g(final SpacecraftState s) { * @param s Spacecraft state. * @return Relative position between primary (=s) and secondaryPVProvider. */ - protected PVCoordinates computeDeltaPV(final SpacecraftState s) { + public PVCoordinates computeDeltaPV(final SpacecraftState s) { return new PVCoordinates(s.getPVCoordinates(), secondaryPVProvider.getPVCoordinates(s.getDate(), s.getFrame())); } /** {@inheritDoc} */ @Override - protected ExtremumApproachDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + protected ExtremumApproachDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, + final EventHandler newHandler) { return new ExtremumApproachDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, secondaryPVProvider); } + + /** + * Get the secondary position-velocity provider stored in this instance. + * + * @return the secondary position-velocity provider stored in this instance + */ + public PVCoordinatesProvider getSecondaryPVProvider() { + return secondaryPVProvider; + } } diff --git a/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java b/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java index ce66f31b42..97f55058d2 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,7 +17,6 @@ package org.orekit.propagation.events; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.ode.events.Action; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.propagation.FieldSpacecraftState; @@ -25,11 +24,13 @@ import org.orekit.time.FieldAbsoluteDate; /** Common parts shared by several orbital events finders. + * @param type of the detector + * @param type of the field element * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) * @author Luc Maisonobe */ -public abstract class FieldAbstractDetector, - T extends CalculusFieldElement> implements FieldEventDetector { +public abstract class FieldAbstractDetector, T extends CalculusFieldElement> + implements FieldEventDetector { /** Default maximum checking interval (s). */ public static final double DEFAULT_MAXCHECK = 600; @@ -41,7 +42,7 @@ public abstract class FieldAbstractDetector, public static final int DEFAULT_MAX_ITER = 100; /** Max check interval. */ - private final T maxCheck; + private final FieldAdaptableInterval maxCheck; /** Convergence threshold. */ private final T threshold; @@ -50,20 +51,19 @@ public abstract class FieldAbstractDetector, private final int maxIter; /** Default handler for event overrides. */ - private final FieldEventHandler handler; + private final FieldEventHandler handler; /** Propagation direction. */ private boolean forward; /** Build a new instance. - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences */ - protected FieldAbstractDetector(final T maxCheck, final T threshold, final int maxIter, - final FieldEventHandler handler) { - checkStrictlyPositive(maxCheck.getReal()); + protected FieldAbstractDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, + final FieldEventHandler handler) { checkStrictlyPositive(threshold.getReal()); this.maxCheck = maxCheck; this.threshold = threshold; @@ -84,18 +84,17 @@ private void checkStrictlyPositive(final double value) throws OrekitException { } /** {@inheritDoc} */ - @SuppressWarnings("unchecked") public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { forward = t.durationFrom(s0.getDate()).getReal() >= 0.0; - getHandler().init(s0, t, (D) this); + getHandler().init(s0, t, this); } /** {@inheritDoc} */ public abstract T g(FieldSpacecraftState s); /** {@inheritDoc} */ - public T getMaxCheckInterval() { + public FieldAdaptableInterval getMaxCheckInterval() { return maxCheck; } @@ -116,9 +115,22 @@ public T getThreshold() { *

        * @param newMaxCheck maximum checking interval (s) * @return a new detector with updated configuration (the instance is not changed) - * @since 6.1 + * @since 12.0 + */ + public D withMaxCheck(final double newMaxCheck) { + return withMaxCheck(s -> newMaxCheck); + } + + /** + * Setup the maximum checking interval. + *

        + * This will override a maximum checking interval if it has been configured previously. + *

        + * @param newMaxCheck maximum checking interval (s) + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.0 */ - public D withMaxCheck(final T newMaxCheck) { + public D withMaxCheck(final FieldAdaptableInterval newMaxCheck) { return create(newMaxCheck, getThreshold(), getMaxIterationCount(), getHandler()); } @@ -157,40 +169,24 @@ public D withThreshold(final T newThreshold) { * @return a new detector with updated configuration (the instance is not changed) * @since 6.1 */ - public D withHandler(final FieldEventHandler newHandler) { + public D withHandler(final FieldEventHandler newHandler) { return create(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), newHandler); } - /** Get the handler. - * @return event handler to call at event occurrences - */ - public FieldEventHandler getHandler() { - return handler; - } - /** {@inheritDoc} */ - public Action eventOccurred(final FieldSpacecraftState s, final boolean increasing) { - @SuppressWarnings("unchecked") - final Action whatNext = getHandler().eventOccurred(s, (D) this, increasing); - return whatNext; - } - - /** {@inheritDoc} */ - public FieldSpacecraftState resetState(final FieldSpacecraftState oldState) { - @SuppressWarnings("unchecked") - final FieldSpacecraftState newState = getHandler().resetState((D) this, oldState); - return newState; + public FieldEventHandler getHandler() { + return handler; } /** Build a new instance. - * @param newMaxCheck maximum checking interval (s) + * @param newMaxCheck maximum checking interval * @param newThreshold convergence threshold (s) * @param newMaxIter maximum number of iterations in the event time search * @param newHandler event handler to call at event occurrences * @return a new instance of the appropriate sub-type */ - protected abstract D create(T newMaxCheck, T newThreshold, - int newMaxIter, FieldEventHandler newHandler); + protected abstract D create(FieldAdaptableInterval newMaxCheck, T newThreshold, + int newMaxIter, FieldEventHandler newHandler); /** Check if the current propagation is forward or backward. * @return true if the current propagation is forward diff --git a/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java b/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java new file mode 100644 index 0000000000..8c88cfe96a --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java @@ -0,0 +1,39 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.propagation.FieldSpacecraftState; + +/** This interface represents an event checking interval that depends on state. +* +* @see FieldEventDetector +* @author Luc Maisonobe +* @since 12.0 +* @param the type of the field elements +*/ +@FunctionalInterface +public interface FieldAdaptableInterval> { + + /** Get the current value of maximal time interval between events handler checks. + * @param state current state + * @return current value of maximal time interval between events handler checks (only as a double) + */ + double currentInterval(FieldSpacecraftState state); + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java b/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java new file mode 100644 index 0000000000..d3cbbfa1a9 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldAdapterDetector.java @@ -0,0 +1,90 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.time.FieldAbsoluteDate; + +/** Base class for adapting an existing detector. + *

        + * This class is intended to be a base class for changing behaviour + * of a wrapped existing detector. This base class delegates all + * its methods to the wrapped detector. Classes extending it can + * therefore override only the methods they want to change. + *

        + * @author Luc Maisonobe + * @since 12.0 + * @param type of the field element + */ +public class FieldAdapterDetector> implements FieldEventDetector { + + /** Wrapped detector. */ + private final FieldEventDetector detector; + + /** Build an adaptor wrapping an existing detector. + * @param detector detector to wrap + */ + public FieldAdapterDetector(final FieldEventDetector detector) { + this.detector = detector; + } + + /** Get the wrapped detector. + * @return wrapped detector + */ + public FieldEventDetector getDetector() { + return detector; + } + + /** {@inheritDoc} */ + @Override + public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { + detector.init(s0, t); + } + + /** {@inheritDoc} */ + @Override + public T g(final FieldSpacecraftState s) { + return detector.g(s); + } + + /** {@inheritDoc} */ + @Override + public T getThreshold() { + return detector.getThreshold(); + } + + /** {@inheritDoc} */ + @Override + public FieldAdaptableInterval getMaxCheckInterval() { + return detector.getMaxCheckInterval(); + } + + /** {@inheritDoc} */ + @Override + public int getMaxIterationCount() { + return detector.getMaxIterationCount(); + } + + /** {@inheritDoc} */ + @Override + public FieldEventHandler getHandler() { + return detector.getHandler(); + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java b/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java index 20e75a693a..1d30234c09 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,6 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.events.handlers.FieldStopOnDecreasing; -import org.orekit.utils.FieldPVCoordinates; /** Finder for satellite altitude crossing events. *

        This class finds altitude events (i.e. satellite crossing @@ -36,6 +35,7 @@ * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) * @author Luc Maisonobe * @since 9.0 + * @param type of the field elements */ public class FieldAltitudeDetector> extends FieldAbstractDetector, T> { @@ -90,17 +90,17 @@ public FieldAltitudeDetector(final T maxCheck, final T threshold, final T altitude, final BodyShape bodyShape) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new FieldStopOnDecreasing, T>(), + this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnDecreasing(), altitude, bodyShape); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -108,10 +108,10 @@ public FieldAltitudeDetector(final T maxCheck, * @param bodyShape body shape with respect to which altitude should be evaluated * @since 6.1 */ - private FieldAltitudeDetector(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler, - final T altitude, - final BodyShape bodyShape) { + protected FieldAltitudeDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final T altitude, + final BodyShape bodyShape) { super(maxCheck, threshold, maxIter, handler); this.altitude = altitude; this.bodyShape = bodyShape; @@ -119,9 +119,9 @@ private FieldAltitudeDetector(final T maxCheck, final T threshold, /** {@inheritDoc} */ @Override - protected FieldAltitudeDetector create(final T newMaxCheck, final T newThreshold, + protected FieldAltitudeDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { + final FieldEventHandler newHandler) { return new FieldAltitudeDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, altitude, bodyShape); } @@ -148,8 +148,7 @@ public BodyShape getBodyShape() { */ public T g(final FieldSpacecraftState s) { final Frame bodyFrame = bodyShape.getBodyFrame(); - final FieldPVCoordinates pvBody = s.getPVCoordinates(bodyFrame); - final FieldGeodeticPoint point = bodyShape.transform(pvBody.getPosition(), + final FieldGeodeticPoint point = bodyShape.transform(s.getPosition(bodyFrame), bodyFrame, s.getDate()); return point.getAltitude().subtract(altitude); } diff --git a/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java b/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java index 0252bf8dae..501443028e 100644 --- a/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -38,6 +38,7 @@ * after the maneuver has been performed!

        * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) * @author Luc Maisonobe + * @param type of the field elements */ public class FieldApsideDetector> extends FieldAbstractDetector, T> { @@ -58,31 +59,31 @@ public FieldApsideDetector(final FieldOrbit orbit) { * @param orbit initial orbit */ public FieldApsideDetector(final T threshold, final FieldOrbit orbit) { - super(orbit.getKeplerianPeriod().divide(3), threshold, - DEFAULT_MAX_ITER, new FieldStopOnIncreasing, T>()); + super(s -> orbit.getKeplerianPeriod().divide(3).getReal(), threshold, + DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>()); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences */ - private FieldApsideDetector(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler) { + protected FieldApsideDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler) { super(maxCheck, threshold, maxIter, handler); } /** {@inheritDoc} */ @Override - protected FieldApsideDetector create(final T newMaxCheck, final T newThreshold, + protected FieldApsideDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { + final FieldEventHandler newHandler) { return new FieldApsideDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); } diff --git a/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java b/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java new file mode 100644 index 0000000000..8ee9f9bc69 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldBooleanDetector.java @@ -0,0 +1,325 @@ +/* Contributed in the public domain. + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.time.FieldAbsoluteDate; + +/** + * This class provides AND and OR operations for event detectors. This class treats + * positive values of the g function as true and negative values as false. + * + *

        One example for an imaging satellite might be to only detect events when a + * satellite is overhead (elevation > 0) AND when the ground point is sunlit (Sun + * elevation > 0). Another slightly contrived example using the OR operator would be to + * detect access to a set of ground stations and only report events when the satellite + * enters or leaves the field of view of the set, but not hand-offs between the ground + * stations. + * + *

        For the FieldBooleanDetector is important that the sign of the g function of the + * underlying event detector is not arbitrary, but has a semantic meaning, e.g. in or out, + * true or false. This class works well with event detectors that detect entry to or exit + * from a region, e.g. {@link FieldEclipseDetector}, {@link FieldElevationDetector}, {@link + * FieldLatitudeCrossingDetector}. Using this detector with detectors that are not based on + * entry to or exit from a region, e.g. {@link FieldDateDetector}, will likely lead to + * unexpected results. To apply conditions to this latter type of event detectors a + * {@link FieldEventEnablingPredicateFilter} is usually more appropriate. + * + * @param type of the field elements + * @since 12.0 + * @author Evan Ward + * @author luc Luc Maisonobe + * @see #andCombine(Collection) + * @see #orCombine(Collection) + * @see #notCombine(FieldEventDetector) + * @see EventEnablingPredicateFilter + * @see EventSlopeFilter + */ +public class FieldBooleanDetector> extends FieldAbstractDetector, T> { + + /** Original detectors: the operands. */ + private final List> detectors; + + /** The composition function. Should be associative for predictable behavior. */ + private final Operator operator; + + /** + * Private constructor with all the parameters. + * + * @param detectors the operands. + * @param operator reduction operator to apply to value of the g function of the + * operands. + * @param newMaxCheck max check interval. + * @param newThreshold convergence threshold in seconds. + * @param newMaxIter max iterations. + * @param newHandler event handler. + */ + protected FieldBooleanDetector(final List> detectors, + final Operator operator, + final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + super(newMaxCheck, newThreshold, newMaxIter, newHandler); + this.detectors = detectors; + this.operator = operator; + } + + /** + * Create a new event detector that is the logical AND of the given event detectors. + * + *

        The created event detector's g function is positive if and only if the g + * functions of all detectors in {@code detectors} are positive. + * + *

        The starting interval, threshold, and iteration count are set to the most + * stringent (minimum) of all the {@code detectors}. The event handlers of the + * underlying {@code detectors} are not used, instead the default handler is {@link + * FieldContinueOnEvent}. + * + * @param type of the field elements + * @param detectors the operands. Must contain at least one detector. + * @return a new event detector that is the logical AND of the operands. + * @throws NoSuchElementException if {@code detectors} is empty. + * @see FieldBooleanDetector + * @see #andCombine(Collection) + * @see #orCombine(FieldEventDetector...) + * @see #notCombine(FieldEventDetector) + */ + @SafeVarargs + public static > FieldBooleanDetector andCombine(final FieldEventDetector... detectors) { + return andCombine(Arrays.asList(detectors)); + } + + /** + * Create a new event detector that is the logical AND of the given event detectors. + * + *

        The created event detector's g function is positive if and only if the g + * functions of all detectors in {@code detectors} are positive. + * + *

        The starting interval, threshold, and iteration count are set to the most + * stringent (minimum) of the {@code detectors}. The event handlers of the + * underlying {@code detectors} are not used, instead the default handler is {@link + * FieldContinueOnEvent}. + * + * @param type of the field elements + * @param detectors the operands. Must contain at least one detector. + * @return a new event detector that is the logical AND of the operands. + * @throws NoSuchElementException if {@code detectors} is empty. + * @see FieldBooleanDetector + * @see #andCombine(FieldEventDetector...) + * @see #orCombine(Collection) + * @see #notCombine(FieldEventDetector) + */ + public static > FieldBooleanDetector andCombine(final Collection> detectors) { + + return new FieldBooleanDetector<>(new ArrayList<>(detectors), // copy for immutability + Operator.AND, + s -> { + double minInterval = Double.POSITIVE_INFINITY; + for (final FieldEventDetector detector : detectors) { + minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s)); + } + return minInterval; + }, + detectors.stream().map(FieldEventDetector::getThreshold).min(new FieldComparator<>()).get(), + detectors.stream().map(FieldEventDetector::getMaxIterationCount).min(Integer::compareTo).get(), + new FieldContinueOnEvent<>()); + } + + /** + * Create a new event detector that is the logical OR of the given event detectors. + * + *

        The created event detector's g function is positive if and only if at least + * one of g functions of the event detectors in {@code detectors} is positive. + * + *

        The starting interval, threshold, and iteration count are set to the most + * stringent (minimum) of the {@code detectors}. The event handlers of the + * underlying EventDetectors are not used, instead the default handler is {@link + * FieldContinueOnEvent}. + * + * @param type of the field elements + * @param detectors the operands. Must contain at least one detector. + * @return a new event detector that is the logical OR of the operands. + * @throws NoSuchElementException if {@code detectors} is empty. + * @see FieldBooleanDetector + * @see #orCombine(Collection) + * @see #andCombine(FieldEventDetector...) + * @see #notCombine(FieldEventDetector) + */ + @SafeVarargs + public static > FieldBooleanDetector orCombine(final FieldEventDetector... detectors) { + return orCombine(Arrays.asList(detectors)); + } + + /** + * Create a new event detector that is the logical OR of the given event detectors. + * + *

        The created event detector's g function is positive if and only if at least + * one of g functions of the event detectors in {@code detectors} is positive. + * + *

        The starting interval, threshold, and iteration count are set to the most + * stringent (minimum) of the {@code detectors}. The event handlers of the + * underlying EventDetectors are not used, instead the default handler is {@link + * FieldContinueOnEvent}. + * + * @param type of the field elements + * @param detectors the operands. Must contain at least one detector. + * @return a new event detector that is the logical OR of the operands. + * @throws NoSuchElementException if {@code detectors} is empty. + * @see FieldBooleanDetector + * @see #orCombine(FieldEventDetector...) + * @see #andCombine(Collection) + * @see #notCombine(FieldEventDetector) + */ + public static > FieldBooleanDetector orCombine(final Collection> detectors) { + + return new FieldBooleanDetector<>(new ArrayList<>(detectors), // copy for immutability + Operator.OR, + s -> { + double minInterval = Double.POSITIVE_INFINITY; + for (final FieldEventDetector detector : detectors) { + minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s)); + } + return minInterval; + }, + detectors.stream().map(FieldEventDetector::getThreshold).min(new FieldComparator<>()).get(), + detectors.stream().map(FieldEventDetector::getMaxIterationCount).min(Integer::compareTo).get(), + new FieldContinueOnEvent<>()); + } + + /** + * Create a new event detector that negates the g function of another detector. + * + *

        This detector will be initialized with the same {@link + * FieldEventDetector#getMaxCheckInterval()}, {@link FieldEventDetector#getThreshold()}, and + * {@link FieldEventDetector#getMaxIterationCount()} as {@code detector}. The event handler + * of the underlying detector is not used, instead the default handler is {@link + * FieldContinueOnEvent}. + * + * @param type of the field elements + * @param detector to negate. + * @return an new event detector whose g function is the same magnitude but opposite + * sign of {@code detector}. + * @see #andCombine(Collection) + * @see #orCombine(Collection) + * @see FieldBooleanDetector + */ + public static > FieldNegateDetector notCombine(final FieldEventDetector detector) { + return new FieldNegateDetector<>(detector); + } + + @Override + public T g(final FieldSpacecraftState s) { + // can't use stream/lambda here because g(s) throws a checked exception + // so write out and combine the map and reduce loops + T ret = s.getDate().getField().getZero().newInstance(Double.NaN); // return value + boolean first = true; + for (final FieldEventDetector detector : detectors) { + if (first) { + ret = detector.g(s); + first = false; + } else { + ret = operator.combine(ret, detector.g(s)); + } + } + // return the result of applying the operator to all operands + return ret; + } + + @Override + protected FieldBooleanDetector create(final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldBooleanDetector<>(detectors, operator, newMaxCheck, newThreshold, + newMaxIter, newHandler); + } + + @Override + public void init(final FieldSpacecraftState s0, + final FieldAbsoluteDate t) { + super.init(s0, t); + for (final FieldEventDetector detector : detectors) { + detector.init(s0, t); + } + } + + /** + * Get the list of original detectors. + * @return the list of original detectors + */ + public List> getDetectors() { + return new ArrayList<>(detectors); + } + + /** Local class for operator. */ + private enum Operator { + + /** And operator. */ + AND() { + + @Override + /** {@inheritDoc} */ + public > T combine(final T g1, final T g2) { + return FastMath.min(g1, g2); + } + + }, + + /** Or operator. */ + OR() { + + @Override + /** {@inheritDoc} */ + public > T combine(final T g1, final T g2) { + return FastMath.max(g1, g2); + } + + }; + + /** Combine two g functions evaluations. + * @param type of the field elements + * @param g1 first evaluation + * @param g2 second evaluation + * @return combined evaluation + */ + public abstract > T combine(T g1, T g2); + + }; + + /** Comparator for field elements. + * @param type of the field elements + */ + private static class FieldComparator> implements Comparator { + public int compare(final T t1, final T t2) { + return Double.compare(t1.getReal(), t2.getReal()); + } + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldDateDetector.java b/src/main/java/org/orekit/propagation/events/FieldDateDetector.java index 7b3149ae6a..ac06c057d0 100644 --- a/src/main/java/org/orekit/propagation/events/FieldDateDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldDateDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,10 +17,13 @@ package org.orekit.propagation.events; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.ode.events.Action; import org.orekit.errors.OrekitIllegalArgumentException; -import org.hipparchus.CalculusFieldElement; import org.orekit.errors.OrekitMessages; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.handlers.FieldEventHandler; @@ -32,20 +35,41 @@ *

        This class finds date events (i.e. occurrence of some predefined dates).

        *

        As of version 5.1, it is an enhanced date detector:

        *
          - *
        • it can be defined without prior date ({@link #FieldDateDetector(CalculusFieldElement, CalculusFieldElement, FieldTimeStamped...)})
        • + *
        • it can be defined without prior date ({@link #FieldDateDetector(Field, FieldTimeStamped...)})
        • *
        • several dates can be added ({@link #addEventDate(FieldAbsoluteDate)})
        • *
        - *

        The gap between the added dates must be more than the maxCheck.

        + *

        The gap between the added dates must be more than the minGap.

        *

        The default implementation behavior is to {@link Action#STOP stop} * propagation at the first event date occurrence. This can be changed by calling * {@link #withHandler(FieldEventHandler)} after construction.

        * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) * @author Luc Maisonobe * @author Pascal Parraud + * @param type of the field elements */ public class FieldDateDetector> extends FieldAbstractDetector, T> implements FieldTimeStamped { + /** Default value for max check. + * @since 12.0 + */ + public static final double DEFAULT_MAX_CHECK = 1.0e10; + + /** Default value for minimum gap between added dates. + * @since 12.0 + */ + public static final double DEFAULT_MIN_GAP = 1.0; + + /** Default value for convergence threshold. + * @since 12.0 + */ + public static final double DEFAULT_THRESHOLD = 1.0e-13; + + /** Minimum gap between added dates. + * @since 12.0 + */ + private final double minGap; + /** Last date for g computation. */ private FieldAbsoluteDate gDate; @@ -58,43 +82,34 @@ public class FieldDateDetector> extends FieldA /** Build a new instance. *

        First event dates are set here, but others can be * added later with {@link #addEventDate(FieldAbsoluteDate)}.

        - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) + * @param field field to which dates belong * @param dates list of event dates * @see #addEventDate(FieldAbsoluteDate) + * @since 12.0 */ @SafeVarargs - public FieldDateDetector(final T maxCheck, final T threshold, final FieldTimeStamped... dates) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new FieldStopOnEvent, T>(), dates); + public FieldDateDetector(final Field field, final FieldTimeStamped... dates) { + this(s-> DEFAULT_MAX_CHECK, field.getZero().newInstance(DEFAULT_THRESHOLD), + DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), DEFAULT_MIN_GAP, dates); } - /** Build a new instance. - *

        This constructor is dedicated to single date detection. - * {@link #getMaxCheckInterval() max check interval} is set to 1.0e10, so almost - * no other date can be added. Tolerance is set to 1.0e-9.

        - * @param target target date - * @see #addEventDate(FieldAbsoluteDate) - */ - public FieldDateDetector(final FieldAbsoluteDate target) { - this(target.getField().getZero().add(1.0e10), target.getField().getZero().add(1.e-9), target); - } - - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences + * @param minGap minimum gap between added dates (s) * @param dates list of event dates */ @SafeVarargs - private FieldDateDetector(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler, - final FieldTimeStamped... dates) { + protected FieldDateDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final double minGap, final FieldTimeStamped... dates) { super(maxCheck, threshold, maxIter, handler); this.currentIndex = -1; this.gDate = null; @@ -102,15 +117,37 @@ private FieldDateDetector(final T maxCheck, final T threshold, for (final FieldTimeStamped ts : dates) { addEventDate(ts.getDate()); } + this.minGap = minGap; + } + + /** + * Setup minimum gap between added dates. + * @param newMinGap new minimum gap between added dates + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.0 + */ + public FieldDateDetector withMinGap(final double newMinGap) { + @SuppressWarnings("unchecked") + final FieldTimeStamped[] dates = eventDateList.toArray(new FieldEventDate[eventDateList.size()]); + return new FieldDateDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), newMinGap, dates); } /** {@inheritDoc} */ @Override - protected FieldDateDetector create(final T newMaxCheck, final T newThreshold, - final int newMaxIter, final FieldEventHandler, T> newHandler) { + protected FieldDateDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { @SuppressWarnings("unchecked") final FieldTimeStamped[] dates = eventDateList.toArray(new FieldEventDate[eventDateList.size()]); - return new FieldDateDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, dates); + return new FieldDateDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, minGap, dates); + } + + /** Get all event field dates currently managed, in chronological order. + * @return all event field dates currently managed, in chronological order + * @since 12.0 + */ + public List> getDates() { + return Collections.unmodifiableList(eventDateList); } /** Compute the value of the switching function. @@ -143,7 +180,7 @@ public FieldAbsoluteDate getDate() { * * @param target target date * @throws IllegalArgumentException if the date is too close from already defined interval - * @see #FieldDateDetector(CalculusFieldElement, CalculusFieldElement, FieldTimeStamped...) + * @see #FieldDateDetector(Field, FieldTimeStamped...) */ public void addEventDate(final FieldAbsoluteDate target) throws IllegalArgumentException { final boolean increasing; @@ -152,20 +189,24 @@ public void addEventDate(final FieldAbsoluteDate target) throws IllegalArgume currentIndex = 0; eventDateList.add(new FieldEventDate<>(target, increasing)); } else { - final int lastIndex = eventDateList.size() - 1; - if (eventDateList.get(0).getDate().durationFrom(target).getReal() > getMaxCheckInterval().getReal()) { + final int lastIndex = eventDateList.size() - 1; + final FieldAbsoluteDate firstDate = eventDateList.get(0).getDate(); + final FieldAbsoluteDate lastDate = eventDateList.get(lastIndex).getDate(); + if (firstDate.durationFrom(target).getReal() > minGap) { increasing = !eventDateList.get(0).isgIncrease(); eventDateList.add(0, new FieldEventDate<>(target, increasing)); currentIndex++; - } else if (target.durationFrom(eventDateList.get(lastIndex).getDate()).getReal() > getMaxCheckInterval().getReal()) { + } else if (target.durationFrom(lastDate).getReal() > minGap) { increasing = !eventDateList.get(lastIndex).isgIncrease(); eventDateList.add(new FieldEventDate<>(target, increasing)); } else { throw new OrekitIllegalArgumentException(OrekitMessages.EVENT_DATE_TOO_CLOSE, target, - eventDateList.get(0).getDate(), - eventDateList.get(lastIndex).getDate(), - getMaxCheckInterval()); + firstDate, + lastDate, + minGap, + firstDate.durationFrom(target), + target.durationFrom(lastDate)); } } } diff --git a/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java b/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java index 4e356cc593..9d993169a0 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,15 +16,15 @@ */ package org.orekit.propagation.events; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.Field; import org.hipparchus.ode.events.Action; -import org.hipparchus.util.FastMath; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; -import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.ExtendedPVCoordinatesProvider; +import org.orekit.utils.OccultationEngine; /** Finder for satellite eclipse related events. *

        This class finds eclipse events, i.e. satellite within umbra (total @@ -33,117 +33,83 @@ * propagation when entering the eclipse and to {@link Action#STOP stop} propagation * when exiting the eclipse. This can be changed by calling {@link * #withHandler(FieldEventHandler)} after construction.

        + * @param the type of the field elements * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) * @author Pascal Parraud */ public class FieldEclipseDetector> extends FieldAbstractDetector, T> { - - /** Occulting body. */ - private final PVCoordinatesProvider occulting; - - /** Occulting body radius (m). */ - private final double occultingRadius; - - /** Occulted body. */ - private final PVCoordinatesProvider occulted; - - /** Occulted body radius (m). */ - private final double occultedRadius; + /** Occultation engine. + * @since 12.0 + */ + private final OccultationEngine occultationEngine; /** Umbra, if true, or penumbra, if false, detection flag. */ private boolean totalEclipse; + /** Margin to apply to eclipse angle. */ + private final T margin; + /** Build a new eclipse detector. *

        The new instance is a total eclipse (umbra) detector with default * values for maximal checking interval ({@link #DEFAULT_MAXCHECK}) * and convergence threshold ({@link #DEFAULT_THRESHOLD}).

        + * @param field field used by default * @param occulted the body to be occulted * @param occultedRadius the radius of the body to be occulted (m) * @param occulting the occulting body - * @param occultingRadius the occulting body radius (m) - * @param field field used by default + * @since 12.0 */ - public FieldEclipseDetector(final PVCoordinatesProvider occulted, final double occultedRadius, - final PVCoordinatesProvider occulting, final double occultingRadius, final Field field) { - this(field.getZero().add(DEFAULT_MAXCHECK), field.getZero().add(DEFAULT_THRESHOLD), - occulted, occultedRadius, occulting, occultingRadius); + public FieldEclipseDetector(final Field field, + final ExtendedPVCoordinatesProvider occulted, final double occultedRadius, + final OneAxisEllipsoid occulting) { + this(field, new OccultationEngine(occulted, occultedRadius, occulting)); } /** Build a new eclipse detector. *

        The new instance is a total eclipse (umbra) detector with default - * value for convergence threshold ({@link #DEFAULT_THRESHOLD}).

        - *

        The maximal interval between eclipse checks should be smaller than - * the half duration of the minimal pass to handle, otherwise some short - * passes could be missed.

        - * @param maxCheck maximal checking interval (s) - * @param occulted the body to be occulted - * @param occultedRadius the radius of the body to be occulted in meters - * @param occulting the occulting body - * @param occultingRadius the occulting body radius in meters - */ - public FieldEclipseDetector(final T maxCheck, - final PVCoordinatesProvider occulted, final double occultedRadius, - final PVCoordinatesProvider occulting, final double occultingRadius) { - this(maxCheck, maxCheck.getField().getZero().add(DEFAULT_THRESHOLD), - occulted, occultedRadius, occulting, occultingRadius); - } - - /** Build a new eclipse detector. - *

        The new instance is a total eclipse (umbra) detector.

        - *

        The maximal interval between eclipse checks should be smaller than - * the half duration of the minimal pass to handle, otherwise some short - * passes could be missed.

        - * @param maxCheck maximal checking interval (s) - * @param threshold convergence threshold (s) - * @param occulted the body to be occulted - * @param occultedRadius the radius of the body to be occulted in meters - * @param occulting the occulting body - * @param occultingRadius the occulting body radius in meters + * values for maximal checking interval ({@link #DEFAULT_MAXCHECK}) + * and convergence threshold ({@link #DEFAULT_THRESHOLD}).

        + * @param field field used by default + * @param occultationEngine occultation engine + * @since 12.0 */ - public FieldEclipseDetector(final T maxCheck, final T threshold, - final PVCoordinatesProvider occulted, final double occultedRadius, - final PVCoordinatesProvider occulting, final double occultingRadius) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing, T>(), - occulted, occultedRadius, occulting, occultingRadius, true); + public FieldEclipseDetector(final Field field, final OccultationEngine occultationEngine) { + this(s -> DEFAULT_MAXCHECK, field.getZero().newInstance(DEFAULT_THRESHOLD), + DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + occultationEngine, field.getZero(), true); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences - * @param occulted the body to be occulted - * @param occultedRadius the radius of the body to be occulted in meters - * @param occulting the occulting body - * @param occultingRadius the occulting body radius in meters + * @param occultationEngine occultation engine + * @param margin to apply to eclipse angle (rad) * @param totalEclipse umbra (true) or penumbra (false) detection flag - * @since 6.1 + * @since 12.0 */ - private FieldEclipseDetector(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler, - final PVCoordinatesProvider occulted, final double occultedRadius, - final PVCoordinatesProvider occulting, final double occultingRadius, - final boolean totalEclipse) { + protected FieldEclipseDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final OccultationEngine occultationEngine, final T margin, final boolean totalEclipse) { super(maxCheck, threshold, maxIter, handler); - this.occulted = occulted; - this.occultedRadius = FastMath.abs(occultedRadius); - this.occulting = occulting; - this.occultingRadius = FastMath.abs(occultingRadius); - this.totalEclipse = totalEclipse; + this.occultationEngine = occultationEngine; + this.margin = margin; + this.totalEclipse = totalEclipse; } /** {@inheritDoc} */ @Override - protected FieldEclipseDetector create(final T newMaxCheck, final T newThreshold, - final int nawMaxIter, final FieldEventHandler, T> newHandler) { + protected FieldEclipseDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int nawMaxIter, + final FieldEventHandler newHandler) { return new FieldEclipseDetector<>(newMaxCheck, newThreshold, nawMaxIter, newHandler, - occulted, occultedRadius, occulting, occultingRadius, totalEclipse); + occultationEngine, margin, totalEclipse); } /** @@ -157,8 +123,7 @@ protected FieldEclipseDetector create(final T newMaxCheck, final T newThresho */ public FieldEclipseDetector withUmbra() { return new FieldEclipseDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occulted, occultedRadius, occulting, occultingRadius, - true); + occultationEngine, margin, true); } /** @@ -172,36 +137,38 @@ public FieldEclipseDetector withUmbra() { */ public FieldEclipseDetector withPenumbra() { return new FieldEclipseDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), - occulted, occultedRadius, occulting, occultingRadius, - false); + occultationEngine, margin, false); } - /** Get the occulting body. - * @return the occulting body - */ - public PVCoordinatesProvider getOcculting() { - return occulting; - } - - /** Get the occulting body radius (m). - * @return the occulting body radius + /** + * Setup a margin to angle detection. + *

        + * A positive margin implies eclipses are "larger" hence entry occurs earlier and exit occurs later + * than a detector with 0 margin. + *

        + * @param newMargin angular margin to apply to eclipse detection (rad) + * @return a new detector with updated configuration (the instance is not changed) + * @since 12.0 */ - public double getOccultingRadius() { - return occultingRadius; + public FieldEclipseDetector withMargin(final T newMargin) { + return new FieldEclipseDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), + occultationEngine, newMargin, totalEclipse); } - /** Get the occulted body. - * @return the occulted body + /** Get the angular margin used for eclipse detection. + * @return angular margin used for eclipse detection (rad) + * @since 12.0 */ - public PVCoordinatesProvider getOcculted() { - return occulted; + public T getMargin() { + return margin; } - /** Get the occulted body radius (m). - * @return the occulted body radius + /** Get the occultation engine. + * @return occultation engine + * @since 12.0 */ - public double getOccultedRadius() { - return occultedRadius; + public OccultationEngine getOccultationEngine() { + return occultationEngine; } /** Get the total eclipse detection flag. @@ -219,22 +186,10 @@ public boolean getTotalEclipse() { * @return value of the switching function */ public T g(final FieldSpacecraftState s) { - final T zero = s.getOrbit().getA().getField().getZero(); - final Vector3D pted = occulted.getPVCoordinates(s.getDate().toAbsoluteDate(), s.getFrame()).getPosition(); - final Vector3D ping = occulting.getPVCoordinates(s.getDate().toAbsoluteDate(), s.getFrame()).getPosition(); - final Vector3D psat = s.toSpacecraftState().getPVCoordinates().getPosition(); - final Vector3D ps = pted.subtract(psat); - final Vector3D po = ping.subtract(psat); - final double angle = Vector3D.angle(ps, po); - final double rs = FastMath.asin(occultedRadius / ps.getNorm()); - if (Double.isNaN(rs)) { - return zero.getPi(); - } - final double ro = FastMath.asin(occultingRadius / po.getNorm()); - if (Double.isNaN(ro)) { - return zero.getPi().negate(); - } - return totalEclipse ? (zero.add(angle - ro + rs)) : (zero.add(angle - ro - rs)); + final OccultationEngine.FieldOccultationAngles angles = occultationEngine.angles(s); + return totalEclipse ? + angles.getSeparation().subtract(angles.getLimbRadius()).add(angles.getOccultedApparentRadius().add(margin)) : + angles.getSeparation().subtract(angles.getLimbRadius()).subtract(angles.getOccultedApparentRadius().add(margin)); } } diff --git a/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java b/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java index 7e644d129e..b9d3e4ca7d 100644 --- a/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.ode.events.Action; import org.hipparchus.util.FastMath; -import org.orekit.frames.StaticTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.TopocentricFrame; import org.orekit.models.AtmosphericRefractionModel; import org.orekit.propagation.FieldSpacecraftState; @@ -40,6 +40,7 @@ * at setting. This can be changed by calling * {@link #withHandler(FieldEventHandler)} after construction.

        * @author Hank Grabowski + * @param type of the field elements */ public class FieldElevationDetector> extends FieldAbstractDetector, T> { @@ -68,9 +69,10 @@ public class FieldElevationDetector> extends F * @see #withRefraction(AtmosphericRefractionModel) */ public FieldElevationDetector(final Field field, final TopocentricFrame topo) { - this(field.getZero().add(DEFAULT_MAXCHECK), - field.getZero().add(DEFAULT_THRESHOLD), - topo); + this(s -> DEFAULT_MAXCHECK, + field.getZero().add(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + new FieldStopOnDecreasing<>(), + 0.0, null, null, topo); } /** @@ -84,18 +86,18 @@ public FieldElevationDetector(final Field field, final TopocentricFrame topo) * @see #withRefraction(AtmosphericRefractionModel) */ public FieldElevationDetector(final T maxCheck, final T threshold, final TopocentricFrame topo) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, - new FieldStopOnDecreasing, T>(), + this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, + new FieldStopOnDecreasing<>(), 0.0, null, null, topo); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -104,10 +106,10 @@ public FieldElevationDetector(final T maxCheck, final T threshold, final Topocen * @param refractionModel reference to refraction model * @param topo reference to a topocentric model */ - private FieldElevationDetector(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler, - final double minElevation, final ElevationMask mask, - final AtmosphericRefractionModel refractionModel, + protected FieldElevationDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final double minElevation, final ElevationMask mask, + final AtmosphericRefractionModel refractionModel, final TopocentricFrame topo) { super(maxCheck, threshold, maxIter, handler); this.minElevation = minElevation; @@ -118,8 +120,8 @@ private FieldElevationDetector(final T maxCheck, final T threshold, /** {@inheritDoc} */ @Override - protected FieldElevationDetector create(final T newMaxCheck, final T newThreshold, - final int newMaxIter, final FieldEventHandler, T> newHandler) { + protected FieldElevationDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { return new FieldElevationDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, minElevation, elevationMask, refractionModel, topo); } @@ -170,9 +172,8 @@ public TopocentricFrame getTopocentricFrame() { @Override public T g(final FieldSpacecraftState s) { - final StaticTransform t = s.getFrame() - .getStaticTransformTo(topo, s.getDate().toAbsoluteDate()); - final FieldVector3D extPointTopo = t.transformPosition(s.getPVCoordinates().getPosition()); + final FieldStaticTransform t = s.getFrame().getStaticTransformTo(topo, s.getDate()); + final FieldVector3D extPointTopo = t.transformPosition(s.getPosition()); final T trueElevation = extPointTopo.getDelta(); final T calculatedElevation; diff --git a/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java b/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java new file mode 100644 index 0000000000..e4c81e8130 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java @@ -0,0 +1,148 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.ode.events.FieldEventSlopeFilter; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.TopocentricFrame; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +/** Detector for elevation extremum with respect to a ground point. + *

        This detector identifies when a spacecraft reaches its + * extremum elevation with respect to a ground point.

        + *

        + * As in most cases only the elevation maximum is needed and the + * minimum is often irrelevant, this detector is often wrapped into + * an {@link FieldEventSlopeFilter event slope filter} configured with + * {@link FilterType#TRIGGER_ONLY_DECREASING_EVENTS} (i.e. when the + * elevation derivative decreases from positive values to negative values, + * which correspond to a maximum). Setting up this filter saves some computation + * time as the elevation minimum occurrences are not even looked at. It is + * however still often necessary to do an additional filtering + *

        + * @param type of the field element + * @author Luc Maisonobe + * @since 12.0 + */ +public class FieldElevationExtremumDetector> + extends FieldAbstractDetector, T> { + + /** Topocentric frame in which elevation should be evaluated. */ + private final TopocentricFrame topo; + + /** Build a new detector. + *

        The new instance uses default values for maximal checking interval + * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link + * #DEFAULT_THRESHOLD}).

        + * @param field field to which elements belong + * @param topo topocentric frame centered on ground point + */ + public FieldElevationExtremumDetector(final Field field, final TopocentricFrame topo) { + this(field.getZero().newInstance(DEFAULT_MAXCHECK), + field.getZero().newInstance(DEFAULT_THRESHOLD), + topo); + } + + /** Build a detector. + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param topo topocentric frame centered on ground point + */ + public FieldElevationExtremumDetector(final T maxCheck, final T threshold, + final TopocentricFrame topo) { + this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + topo); + } + + /** Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

        + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @param topo topocentric frame centered on ground point + */ + protected FieldElevationExtremumDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final TopocentricFrame topo) { + super(maxCheck, threshold, maxIter, handler); + this.topo = topo; + } + + /** {@inheritDoc} */ + @Override + protected FieldElevationExtremumDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldElevationExtremumDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, topo); + } + + /** + * Returns the topocentric frame centered on ground point. + * @return topocentric frame centered on ground point + */ + public TopocentricFrame getTopocentricFrame() { + return this.topo; + } + + /** Get the elevation value. + * @param s the current state information: date, kinematics, attitude + * @return spacecraft elevation + */ + public T getElevation(final FieldSpacecraftState s) { + return topo.getElevation(s.getPosition(), s.getFrame(), s.getDate()); + } + + /** Compute the value of the detection function. + *

        + * The value is the spacecraft elevation first time derivative. + *

        + * @param s the current state information: date, kinematics, attitude + * @return spacecraft elevation first time derivative + */ + public T g(final FieldSpacecraftState s) { + + // get position, velocity acceleration of spacecraft in topocentric frame + final FieldTransform inertToTopo = s.getFrame().getTransformTo(topo, s.getDate()); + final TimeStampedFieldPVCoordinates pvTopo = inertToTopo.transformPVCoordinates(s.getPVCoordinates()); + + // convert the coordinates to UnivariateDerivative1 based vector + // instead of having vector position, then vector velocity then vector acceleration + // we get one vector and each coordinate is a DerivativeStructure containing + // value, first time derivative (we don't need second time derivative here) + final FieldVector3D> pvDS = pvTopo.toUnivariateDerivative1Vector(); + + // compute elevation and its first time derivative + final FieldUnivariateDerivative1 elevation = pvDS.getZ().divide(pvDS.getNorm()).asin(); + + // return elevation first time derivative + return elevation.getDerivative(1); + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldEnablingPredicate.java b/src/main/java/org/orekit/propagation/events/FieldEnablingPredicate.java new file mode 100644 index 0000000000..e11f8a96d4 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldEnablingPredicate.java @@ -0,0 +1,38 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.propagation.FieldSpacecraftState; + +/** This interface represents an event enabling predicate function. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.0 + */ +public interface FieldEnablingPredicate> { + + /** Compute an event enabling function of state. + * @param state current state + * @param detector underlying detector + * @param g value of the underlying detector for the current state + * @return true if the event is enabled (i.e. it can be + * triggered), false if it should be ignored + */ + boolean eventIsEnabled(FieldSpacecraftState state, FieldEventDetector detector, T g); + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldEventDetector.java b/src/main/java/org/orekit/propagation/events/FieldEventDetector.java index a93983aae3..d99cd97dee 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,8 @@ package org.orekit.propagation.events; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.ode.events.Action; import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.time.FieldAbsoluteDate; /** This interface represents space-dynamics aware events detectors. @@ -30,17 +30,36 @@ * *

        Events detectors are a useful solution to meet the requirements * of propagators concerning discrete conditions. The state of each - * event detector is queried by the integrator at each step. When the - * sign of the underlying g switching function changes, the step is rejected - * and reduced, in order to make sure the sign changes occur only at steps - * boundaries.

        + * event detector is queried by the propagator from time to time, at least + * once every {@link #getMaxCheckInterval() max check interval} but it may + * be more frequent. When the sign of the underlying g switching function + * changes, a root-finding algorithm is run to precisely locate the event, + * down to a configured {@link #getThreshold() convergence threshold}. The + * {@link #getMaxCheckInterval() max check interval} is therefore devoted to + * separate roots and is often much larger than the {@link #getThreshold() + * convergence threshold}.

        * - *

        When step ends exactly at a switching function sign change, the corresponding - * event is triggered, by calling the {@link #eventOccurred(FieldSpacecraftState, boolean)} - * method. The method can do whatever it needs with the event (logging it, performing + *

        The physical meaning of the g switching function is not really used + * by the event detection algorithms. Its varies from event detector to + * event detector. One example would be a visibility detector that could use the + * angular elevation of the satellite above horizon as a g switching function. + * In this case, the function would switch from negative to positive when the + * satellite raises above horizon and it would switch from positive to negative + * when it sets backs below horizon. Another example would be an apside detector + * that could use the dot product of position and velocity. In this case, the + * function would switch from negative to positive when the satellite crosses + * periapsis and it would switch from positive to negative when the satellite + * crosses apoapsis.

        + * + *

        When the precise state at which the g switching function changes has been + * located, the corresponding event is triggered, by calling the {@link + * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) + * eventOccurred} method from the associated {@link #getHandler() handler}. + * The method can do whatever it needs with the event (logging it, performing * some processing, ignore it ...). The return value of the method will be used by - * the propagator to stop or resume propagation, possibly changing the state vector.

        + * the propagator to stop or resume propagation, possibly changing the state vector.

        * + * @param type of the field element * @author Luc Maisonobe * @author Véronique Pommier-Maurussane */ @@ -80,38 +99,17 @@ default void init(FieldSpacecraftState s0, /** Get maximal time interval between switching function checks. * @return maximal time interval (s) between switching function checks */ - T getMaxCheckInterval(); + FieldAdaptableInterval getMaxCheckInterval(); /** Get maximal number of iterations in the event time search. * @return maximal number of iterations in the event time search */ int getMaxIterationCount(); - /** Handle the event. - * @param s SpaceCraft state to be used in the evaluation - * @param increasing with the event occurred in an "increasing" or "decreasing" slope direction - * @return the Action that the calling detector should pass back to the evaluation system - * @since 7.0 - */ - Action eventOccurred(FieldSpacecraftState s, boolean increasing); - - /** Reset the state prior to continue propagation. - *

        This method is called after the step handler has returned and - * before the next step is started, but only when {@link - * #eventOccurred} has itself returned the {@link Action#RESET_STATE} - * indicator. It allows the user to reset the state for the next step, - * without perturbing the step handler of the finishing step. If the - * {@link #eventOccurred} never returns the {@link Action#RESET_STATE} - * indicator, this function will never be called, and it is safe to simply return null.

        - *

        - * The default implementation simply returns its argument. - *

        - * @param oldState old state - * @return new state - * @since 7.0 + /** Get the handler. + * @return event handler to call at event occurrences + * @since 12.0 */ - default FieldSpacecraftState resetState(FieldSpacecraftState oldState) { - return oldState; - } + FieldEventHandler getHandler(); } diff --git a/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java b/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java new file mode 100644 index 0000000000..b274201189 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilter.java @@ -0,0 +1,313 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import java.lang.reflect.Array; +import java.util.Arrays; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.time.FieldAbsoluteDate; + +/** Wrapper used to detect events only when enabled by an external predicated function. + * + *

        General {@link FieldEventDetector events} are defined implicitly + * by a {@link FieldEventDetector#g(FieldSpacecraftState) g function} crossing + * zero. This implies that during an orbit propagation, events are + * triggered at all zero crossings. + *

        + * + *

        Sometimes, users would like to enable or disable events by themselves, + * for example to trigger them only for certain orbits, or to check elevation + * maximums only when elevation itself is positive (i.e. they want to + * discard elevation maximums below ground). In these cases, looking precisely + * for all events location and triggering events that will later be ignored + * is a waste of computing time.

        + * + *

        Users can wrap a regular {@link FieldEventDetector event detector} in + * an instance of this class and provide this wrapping instance to + * a {@link org.orekit.propagation.FieldPropagator} + * in order to avoid wasting time looking for uninteresting events. + * The wrapper will intercept the calls to the {@link + * FieldEventDetector#g(FieldSpacecraftState) g function} and to the {@link + * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) + * eventOccurred} method in order to ignore uninteresting events. The + * wrapped regular {@link FieldEventDetector event detector} will the see only + * the interesting events, i.e. either only events that occur when a + * user-provided event enabling predicate function is true, ignoring all events + * that occur when the event enabling predicate function is false. The number of + * calls to the {@link FieldEventDetector#g(FieldSpacecraftState) g function} will also be + * reduced.

        + * @param type of the field elements + * @see FieldEventSlopeFilter + * @since 12.0 + */ + +public class FieldEventEnablingPredicateFilter> + extends FieldAbstractDetector, T> { + + /** Number of past transformers updates stored. */ + private static final int HISTORY_SIZE = 100; + + /** Wrapped event detector. */ + private final FieldEventDetector rawDetector; + + /** Enabling predicate function. */ + private final FieldEnablingPredicate enabler; + + /** Transformers of the g function. */ + private final Transformer[] transformers; + + /** Update time of the transformers. */ + private final FieldAbsoluteDate[] updates; + + /** Indicator for forward integration. */ + private boolean forward; + + /** Extreme time encountered so far. */ + private FieldAbsoluteDate extremeT; + + /** Detector function value at extremeT. */ + private T extremeG; + + /** Wrap an {@link EventDetector event detector}. + * @param rawDetector event detector to wrap + * @param enabler event enabling predicate function to use + */ + public FieldEventEnablingPredicateFilter(final FieldEventDetector rawDetector, + final FieldEnablingPredicate enabler) { + this(rawDetector.getMaxCheckInterval(), rawDetector.getThreshold(), + rawDetector.getMaxIterationCount(), new LocalHandler<>(), + rawDetector, enabler); + } + + /** Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

        + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @param rawDetector event detector to wrap + * @param enabler event enabling function to use + */ + @SuppressWarnings("unchecked") + protected FieldEventEnablingPredicateFilter(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final FieldEventDetector rawDetector, + final FieldEnablingPredicate enabler) { + super(maxCheck, threshold, maxIter, handler); + this.rawDetector = rawDetector; + this.enabler = enabler; + this.transformers = new Transformer[HISTORY_SIZE]; + this.updates = (FieldAbsoluteDate[]) Array.newInstance(FieldAbsoluteDate.class, HISTORY_SIZE); + } + + /** {@inheritDoc} */ + @Override + protected FieldEventEnablingPredicateFilter create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldEventEnablingPredicateFilter<>(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, enabler); + } + + /** + * Get the wrapped raw detector. + * @return the wrapped raw detector + */ + public FieldEventDetector getDetector() { + return rawDetector; + } + + /** {@inheritDoc} */ + public void init(final FieldSpacecraftState s0, + final FieldAbsoluteDate t) { + super.init(s0, t); + + // delegate to raw detector + rawDetector.init(s0, t); + + // initialize events triggering logic + forward = t.compareTo(s0.getDate()) >= 0; + extremeT = forward ? + FieldAbsoluteDate.getPastInfinity(t.getField()) : + FieldAbsoluteDate.getFutureInfinity(t.getField()); + extremeG = t.getField().getZero().newInstance(Double.NaN); + Arrays.fill(transformers, Transformer.UNINITIALIZED); + Arrays.fill(updates, extremeT); + + } + + /** {@inheritDoc} */ + public T g(final FieldSpacecraftState s) { + + final T rawG = rawDetector.g(s); + final boolean isEnabled = enabler.eventIsEnabled(s, rawDetector, rawG); + if (extremeG.isNaN()) { + extremeG = rawG; + } + + // search which transformer should be applied to g + if (forward) { + final int last = transformers.length - 1; + if (extremeT.compareTo(s.getDate()) < 0) { + // we are at the forward end of the history + + // check if enabled status has changed + final Transformer previous = transformers[last]; + final Transformer next = selectTransformer(previous, extremeG, isEnabled); + if (next != previous) { + // there is a status change somewhere between extremeT and t. + // the new transformer is valid for t (this is how we have just computed + // it above), but it is in fact valid on both sides of the change, so + // it was already valid before t and even up to previous time. We store + // the switch at extremeT for safety, to ensure the previous transformer + // is not applied too close of the root + System.arraycopy(updates, 1, updates, 0, last); + System.arraycopy(transformers, 1, transformers, 0, last); + updates[last] = extremeT; + transformers[last] = next; + } + + extremeT = s.getDate(); + extremeG = rawG; + + // apply the transform + return next.transformed(rawG); + + } else { + // we are in the middle of the history + + // select the transformer + for (int i = last; i > 0; --i) { + if (updates[i].compareTo(s.getDate()) <= 0) { + // apply the transform + return transformers[i].transformed(rawG); + } + } + + return transformers[0].transformed(rawG); + + } + } else { + if (s.getDate().compareTo(extremeT) < 0) { + // we are at the backward end of the history + + // check if a new rough root has been crossed + final Transformer previous = transformers[0]; + final Transformer next = selectTransformer(previous, extremeG, isEnabled); + if (next != previous) { + // there is a status change somewhere between extremeT and t. + // the new transformer is valid for t (this is how we have just computed + // it above), but it is in fact valid on both sides of the change, so + // it was already valid before t and even up to previous time. We store + // the switch at extremeT for safety, to ensure the previous transformer + // is not applied too close of the root + System.arraycopy(updates, 0, updates, 1, updates.length - 1); + System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1); + updates[0] = extremeT; + transformers[0] = next; + } + + extremeT = s.getDate(); + extremeG = rawG; + + // apply the transform + return next.transformed(rawG); + + } else { + // we are in the middle of the history + + // select the transformer + for (int i = 0; i < updates.length - 1; ++i) { + if (s.getDate().compareTo(updates[i]) <= 0) { + // apply the transform + return transformers[i].transformed(rawG); + } + } + + return transformers[updates.length - 1].transformed(rawG); + + } + } + + } + + /** Get next function transformer in the specified direction. + * @param previous transformer active on the previous point with respect + * to integration direction (may be null if no previous point is known) + * @param previousG value of the g function at the previous point + * @param isEnabled if true the event should be enabled now + * @return next transformer transformer + */ + private Transformer selectTransformer(final Transformer previous, final T previousG, final boolean isEnabled) { + if (isEnabled) { + // we need to select a transformer that can produce zero crossings, + // so it is either Transformer.PLUS or Transformer.MINUS + switch (previous) { + case UNINITIALIZED : + return Transformer.PLUS; // this initial choice is arbitrary, it could have been Transformer.MINUS + case MIN : + return previousG.getReal() >= 0 ? Transformer.MINUS : Transformer.PLUS; + case MAX : + return previousG.getReal() >= 0 ? Transformer.PLUS : Transformer.MINUS; + default : + return previous; + } + } else { + // we need to select a transformer that cannot produce any zero crossings, + // so it is either Transformer.MAX or Transformer.MIN + switch (previous) { + case UNINITIALIZED : + return Transformer.MAX; // this initial choice is arbitrary, it could have been Transformer.MIN + case PLUS : + return previousG.getReal() >= 0 ? Transformer.MAX : Transformer.MIN; + case MINUS : + return previousG.getReal() >= 0 ? Transformer.MIN : Transformer.MAX; + default : + return previous; + } + } + } + + /** Local handler. + * @param type of the field elements + */ + private static class LocalHandler> implements FieldEventHandler { + + /** {@inheritDoc} */ + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { + final FieldEventEnablingPredicateFilter ef = (FieldEventEnablingPredicateFilter) detector; + final Transformer transformer = ef.forward ? ef.transformers[ef.transformers.length - 1] : ef.transformers[0]; + return ef.rawDetector.getHandler().eventOccurred(s, ef.rawDetector, transformer == Transformer.PLUS ? increasing : !increasing); + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) { + final FieldEventEnablingPredicateFilter ef = (FieldEventEnablingPredicateFilter) detector; + return ef.rawDetector.getHandler().resetState(ef.rawDetector, oldState); + } + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java b/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java new file mode 100644 index 0000000000..8e5d9883c5 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldEventSlopeFilter.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.propagation.events; + +import java.lang.reflect.Array; +import java.util.Arrays; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.time.FieldAbsoluteDate; + +/** Wrapper used to detect only increasing or decreasing events. + * + *

        This class is heavily based on the class EventFilter from the + * Hipparchus library. The changes performed consist in replacing + * raw types (double and double arrays) with space dynamics types + * ({@link FieldAbsoluteDate}, {@link FieldSpacecraftState}).

        + * + *

        General {@link FieldEventDetector events} are defined implicitly + * by a {@link FieldEventDetector#g(FieldSpacecraftState) g function} crossing + * zero. This function needs to be continuous in the event neighborhood, + * and its sign must remain consistent between events. This implies that + * during an orbit propagation, events triggered are alternately events + * for which the function increases from negative to positive values, + * and events for which the function decreases from positive to + * negative values. + *

        + * + *

        Sometimes, users are only interested in one type of event (say + * increasing events for example) and not in the other type. In these + * cases, looking precisely for all events location and triggering + * events that will later be ignored is a waste of computing time.

        + * + *

        Users can wrap a regular {@link FieldEventDetector event detector} in + * an instance of this class and provide this wrapping instance to + * a {@link org.orekit.propagation.FieldPropagator} + * in order to avoid wasting time looking for uninteresting events. + * The wrapper will intercept the calls to the {@link + * FieldEventDetector#g(FieldSpacecraftState) g function} and to the {@link + * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) + * eventOccurred} method in order to ignore uninteresting events. The + * wrapped regular {@link FieldEventDetector event detector} will then see only + * the interesting events, i.e. either only {@code increasing} events or + * only {@code decreasing} events. The number of calls to the {@link + * FieldEventDetector#g(FieldSpacecraftState) g function} will also be reduced.

        + * @see FieldEventEnablingPredicateFilter + * @param type of the detector + * @param type of the field elements + */ + +public class FieldEventSlopeFilter, T extends CalculusFieldElement> + extends FieldAbstractDetector, T> { + + /** Number of past transformers updates stored. */ + private static final int HISTORY_SIZE = 100; + + /** Wrapped event detector. */ + private final D rawDetector; + + /** Filter to use. */ + private final FilterType filter; + + /** Transformers of the g function. */ + private final Transformer[] transformers; + + /** Update time of the transformers. */ + private final FieldAbsoluteDate[] updates; + + /** Indicator for forward integration. */ + private boolean forward; + + /** Extreme time encountered so far. */ + private FieldAbsoluteDate extremeT; + + /** Wrap an {@link EventDetector event detector}. + * @param rawDetector event detector to wrap + * @param filter filter to use + */ + public FieldEventSlopeFilter(final D rawDetector, final FilterType filter) { + this(rawDetector.getMaxCheckInterval(), rawDetector.getThreshold(), + rawDetector.getMaxIterationCount(), new LocalHandler<>(), + rawDetector, filter); + } + + /** Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

        + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @param rawDetector event detector to wrap + * @param filter filter to use + */ + @SuppressWarnings("unchecked") + protected FieldEventSlopeFilter(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final D rawDetector, final FilterType filter) { + super(maxCheck, threshold, maxIter, handler); + this.rawDetector = rawDetector; + this.filter = filter; + this.transformers = new Transformer[HISTORY_SIZE]; + this.updates = (FieldAbsoluteDate[]) Array.newInstance(FieldAbsoluteDate.class, HISTORY_SIZE); + } + + /** {@inheritDoc} */ + @Override + protected FieldEventSlopeFilter create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new FieldEventSlopeFilter<>(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, filter); + } + + /** + * Get the wrapped raw detector. + * @return the wrapped raw detector + */ + public D getDetector() { + return rawDetector; + } + + /** {@inheritDoc} */ + public void init(final FieldSpacecraftState s0, + final FieldAbsoluteDate t) { + super.init(s0, t); + + // delegate to raw detector + rawDetector.init(s0, t); + + // initialize events triggering logic + forward = t.compareTo(s0.getDate()) >= 0; + extremeT = forward ? + FieldAbsoluteDate.getPastInfinity(t.getField()) : + FieldAbsoluteDate.getFutureInfinity(t.getField()); + Arrays.fill(transformers, Transformer.UNINITIALIZED); + Arrays.fill(updates, extremeT); + + } + + /** {@inheritDoc} */ + public T g(final FieldSpacecraftState s) { + + final T rawG = rawDetector.g(s); + + // search which transformer should be applied to g + if (forward) { + final int last = transformers.length - 1; + if (extremeT.compareTo(s.getDate()) < 0) { + // we are at the forward end of the history + + // check if a new rough root has been crossed + final Transformer previous = transformers[last]; + final Transformer next = filter.selectTransformer(previous, rawG.getReal(), forward); + if (next != previous) { + // there is a root somewhere between extremeT and t. + // the new transformer is valid for t (this is how we have just computed + // it above), but it is in fact valid on both sides of the root, so + // it was already valid before t and even up to previous time. We store + // the switch at extremeT for safety, to ensure the previous transformer + // is not applied too close of the root + System.arraycopy(updates, 1, updates, 0, last); + System.arraycopy(transformers, 1, transformers, 0, last); + updates[last] = extremeT; + transformers[last] = next; + } + + extremeT = s.getDate(); + + // apply the transform + return next.transformed(rawG); + + } else { + // we are in the middle of the history + + // select the transformer + for (int i = last; i > 0; --i) { + if (updates[i].compareTo(s.getDate()) <= 0) { + // apply the transform + return transformers[i].transformed(rawG); + } + } + + return transformers[0].transformed(rawG); + + } + } else { + if (s.getDate().compareTo(extremeT) < 0) { + // we are at the backward end of the history + + // check if a new rough root has been crossed + final Transformer previous = transformers[0]; + final Transformer next = filter.selectTransformer(previous, rawG.getReal(), forward); + if (next != previous) { + // there is a root somewhere between extremeT and t. + // the new transformer is valid for t (this is how we have just computed + // it above), but it is in fact valid on both sides of the root, so + // it was already valid before t and even up to previous time. We store + // the switch at extremeT for safety, to ensure the previous transformer + // is not applied too close of the root + System.arraycopy(updates, 0, updates, 1, updates.length - 1); + System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1); + updates[0] = extremeT; + transformers[0] = next; + } + + extremeT = s.getDate(); + + // apply the transform + return next.transformed(rawG); + + } else { + // we are in the middle of the history + + // select the transformer + for (int i = 0; i < updates.length - 1; ++i) { + if (s.getDate().compareTo(updates[i]) <= 0) { + // apply the transform + return transformers[i].transformed(rawG); + } + } + + return transformers[updates.length - 1].transformed(rawG); + + } + } + + } + + /** Local handler. */ + private static class LocalHandler, T extends CalculusFieldElement> implements FieldEventHandler { + + /** {@inheritDoc} */ + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { + @SuppressWarnings("unchecked") + final FieldEventSlopeFilter esf = (FieldEventSlopeFilter) detector; + return esf.rawDetector.getHandler().eventOccurred(s, esf.rawDetector, esf.filter.getTriggeredIncreasing()); + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) { + @SuppressWarnings("unchecked") + final FieldEventSlopeFilter esf = (FieldEventSlopeFilter) detector; + return esf.rawDetector.getHandler().resetState(esf.rawDetector, oldState); + } + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldEventState.java b/src/main/java/org/orekit/propagation/events/FieldEventState.java index 7a6a26d9f2..a36189b69b 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventState.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventState.java @@ -32,6 +32,7 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.sampling.FieldOrekitStepInterpolator; import org.orekit.time.FieldAbsoluteDate; @@ -51,12 +52,16 @@ * occurs at a bound rather than inside the step).

        * @author Luc Maisonobe * @param class type for the generic version + * @param type of the field elements */ public class FieldEventState, T extends CalculusFieldElement> { /** Event detector. */ private D detector; + /** Event handler. */ + private FieldEventHandler handler; + /** Time of the previous call to g. */ private FieldAbsoluteDate lastT; @@ -107,9 +112,10 @@ public class FieldEventState, T extends Calculus public FieldEventState(final D detector) { this.detector = detector; + this.handler = detector.getHandler(); // some dummy values ... - final Field field = detector.getMaxCheckInterval().getField(); + final Field field = detector.getThreshold().getField(); final T nan = field.getZero().add(Double.NaN); lastT = FieldAbsoluteDate.getPastInfinity(field); lastG = nan; @@ -146,7 +152,7 @@ public D getEventDetector() { public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { detector.init(s0, t); - final Field field = detector.getMaxCheckInterval().getField(); + final Field field = detector.getThreshold().getField(); lastT = FieldAbsoluteDate.getPastInfinity(field); lastG = field.getZero().add(Double.NaN); } @@ -201,28 +207,29 @@ public void reinitializeBegin(final FieldOrekitStepInterpolator interpolator) public boolean evaluateStep(final FieldOrekitStepInterpolator interpolator) throws MathRuntimeException { forward = interpolator.isForward(); + final FieldSpacecraftState s0 = interpolator.getPreviousState(); final FieldSpacecraftState s1 = interpolator.getCurrentState(); final FieldAbsoluteDate t1 = s1.getDate(); final T dt = t1.durationFrom(t0); if (FastMath.abs(dt.getReal()) < detector.getThreshold().getReal()) { // we cannot do anything on such a small step, don't trigger any events + pendingEvent = false; + pendingEventTime = null; return false; } - // number of points to check in the current step - final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt.getReal()) / detector.getMaxCheckInterval().getReal())); - final T h = dt.divide(n); - FieldAbsoluteDate ta = t0; T ga = g0; - for (int i = 0; i < n; ++i) { + for (FieldSpacecraftState sb = nextCheck(s0, s1, interpolator); + sb != null; + sb = nextCheck(sb, s1, interpolator)) { // evaluate handler value at the end of the substep - final FieldAbsoluteDate tb = (i == n - 1) ? t1 : t0.shiftedBy(h.multiply(i + 1)); - final T gb = g(interpolator.getInterpolatedState(tb)); + final FieldAbsoluteDate tb = sb.getDate(); + final T gb = g(sb); // check events occurrence - if (gb.getReal() == 0.0 || (g0Positive ^ (gb.getReal() > 0))) { + if (gb.getReal() == 0.0 || (g0Positive ^ gb.getReal() > 0)) { // there is a sign change: an event is expected during this step if (findRoot(interpolator, ta, ga, tb, gb)) { return true; @@ -241,6 +248,29 @@ public boolean evaluateStep(final FieldOrekitStepInterpolator interpolator) } + /** Estimate next state to check. + * @param done state already checked + * @param target target state towards which we are checking + * @param interpolator step interpolator for the proposed step + * @return intermediate state to check, or exactly {@code null} + * if we already have {@code done == target} + * @since 12.0 + */ + private FieldSpacecraftState nextCheck(final FieldSpacecraftState done, final FieldSpacecraftState target, + final FieldOrekitStepInterpolator interpolator) { + if (done == target) { + // we have already reached target + return null; + } else { + // we have to select some intermediate state + // attempting to split the remaining time in an integer number of checks + final T dt = target.getDate().durationFrom(done.getDate()); + final double maxCheck = detector.getMaxCheckInterval().currentInterval(done); + final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt).divide(maxCheck).getReal())); + return n == 1 ? target : interpolator.getInterpolatedState(done.getDate().shiftedBy(dt.divide(n))); + } + } + /** * Find a root in a bracketing interval. * @@ -491,8 +521,8 @@ public boolean tryAdvance(final FieldSpacecraftState state, /** * Notify the user's listener of the event. The event occurs wholly within this method - * call including a call to {@link FieldEventDetector#resetState(FieldSpacecraftState)} - * if necessary. + * call including a call to {@link FieldEventHandler#resetState(FieldEventDetector, + * FieldSpacecraftState)} if necessary. * * @param state the state at the time of the event. This must be at the same time as * the current value of {@link #getEventDate()}. @@ -508,10 +538,10 @@ public EventOccurrence doEvent(final FieldSpacecraftState state) { check(pendingEvent); check(state.getDate().equals(this.pendingEventTime)); - final Action action = detector.eventOccurred(state, increasing == forward); + final Action action = handler.eventOccurred(state, detector, increasing == forward); final FieldSpacecraftState newState; if (action == Action.RESET_STATE) { - newState = detector.resetState(state); + newState = handler.resetState(detector, state); } else { newState = state; } @@ -563,7 +593,7 @@ private FieldAbsoluteDate shiftedBy(final FieldAbsoluteDate t, final T del * @return min(a, b) if forward, else max (a, b) */ private FieldAbsoluteDate minTime(final FieldAbsoluteDate a, final FieldAbsoluteDate b) { - return (forward ^ (a.compareTo(b) > 0)) ? a : b; + return (forward ^ a.compareTo(b) > 0) ? a : b; } /** @@ -597,6 +627,7 @@ private void check(final boolean condition) throws MathRuntimeException { /** * Class to hold the data related to an event occurrence that is needed to decide how * to modify integration. + * @param type of the field elements */ public static class EventOccurrence> { diff --git a/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java b/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java index ce90a39314..26239cbfe6 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventsLogger.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,7 +30,7 @@ * *

        As {@link FieldEventDetector events detectors} are triggered during * orbit propagation, an event specific {@link - * FieldEventDetector#eventOccurred(FieldSpacecraftState, boolean) eventOccurred} + * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) eventOccurred} * method is called. This class can be used to add a global logging * feature registering all events with their corresponding states in * a chronological sequence (or reverse-chronological if propagation @@ -38,12 +38,13 @@ *

        This class works by wrapping user-provided {@link FieldEventDetector * events detectors} before they are registered to the propagator. The * wrapper monitor the calls to {@link - * FieldEventDetector#eventOccurred(FieldSpacecraftState, boolean) eventOccurred} + * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) eventOccurred} * and store the corresponding events as {@link FieldLoggedEvent} instances. * After propagation is complete, the user can retrieve all the events * that have occurred at once by calling method {@link #getLoggedEvents()}.

        * * @author Luc Maisonobe + * @param type of the field elements */ public class FieldEventsLogger> { @@ -81,10 +82,9 @@ public FieldEventsLogger() { *

        * @param monitoredDetector event detector to monitor * @return the wrapping detector to add to the propagator - * @param class type for the generic version */ - public > FieldEventDetector monitorDetector(final D monitoredDetector) { - return new FieldLoggingWrapper<>(monitoredDetector); + public FieldAbstractDetector monitorDetector(final FieldEventDetector monitoredDetector) { + return new FieldLoggingWrapper(monitoredDetector); } /** Clear the logged events. @@ -105,7 +105,9 @@ public List> getLoggedEvents() { return new ArrayList>(log); } - /** Class for logged events entries. */ + /** Class for logged events entries. + * @param type of the field elements + */ public static class FieldLoggedEvent > { /** Event detector triggered. */ @@ -138,7 +140,7 @@ public FieldEventDetector getEventDetector() { /** Get the triggering state. * @return triggering state - * @see FieldEventDetector#eventOccurred(FieldSpacecraftState, boolean) + * @see FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) */ public FieldSpacecraftState getState() { return state; @@ -146,7 +148,7 @@ public FieldSpacecraftState getState() { /** Get the Increasing/decreasing status of the event. * @return increasing/decreasing status of the event - * @see FieldEventDetector#eventOccurred(FieldSpacecraftState, boolean) + * @see FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) */ public boolean isIncreasing() { return increasing; @@ -154,20 +156,18 @@ public boolean isIncreasing() { } - /** Internal wrapper for events detectors. - * @param class type for the generic version - */ - private class FieldLoggingWrapper> extends FieldAbstractDetector, T> { + /** Internal wrapper for events detectors. */ + private class FieldLoggingWrapper extends FieldAbstractDetector { /** Wrapped events detector. */ - private final D detector; + private final FieldEventDetector detector; /** Simple constructor. * @param detector events detector to wrap */ - FieldLoggingWrapper(final D detector) { + FieldLoggingWrapper(final FieldEventDetector detector) { this(detector.getMaxCheckInterval(), detector.getThreshold(), - detector.getMaxIterationCount(), new FieldLocalHandler<>(), + detector.getMaxIterationCount(), null, detector); } @@ -177,25 +177,25 @@ private class FieldLoggingWrapper> extends Field * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param detector events detector to wrap * @since 6.1 */ - private FieldLoggingWrapper(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler, - final D detector) { + private FieldLoggingWrapper(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final FieldEventDetector detector) { super(maxCheck, threshold, maxIter, handler); this.detector = detector; } /** {@inheritDoc} */ @Override - protected FieldLoggingWrapper create(final T newMaxCheck, final T newThreshold, - final int newMaxIter, final FieldEventHandler, T> newHandler) { - return new FieldLoggingWrapper<>(newMaxCheck, newThreshold, newMaxIter, newHandler, detector); + protected FieldLoggingWrapper create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new FieldLoggingWrapper(newMaxCheck, newThreshold, newMaxIter, newHandler, detector); } /** Log an event. @@ -218,23 +218,29 @@ public T g(final FieldSpacecraftState s) { return detector.g(s); } - } + /** {@inheritDoc} */ + public FieldEventHandler getHandler() { - /** Local class for handling events. - * @param class type for the generic version - */ - private class FieldLocalHandler> implements FieldEventHandler, T> { + final FieldEventHandler handler = detector.getHandler(); - /** {@inheritDoc} */ - public Action eventOccurred(final FieldSpacecraftState s, final FieldLoggingWrapper wrapper, final boolean increasing) { - wrapper.logEvent(s, increasing); - return wrapper.detector.eventOccurred(s, increasing); - } + return new FieldEventHandler() { - /** {@inheritDoc} */ - @Override - public FieldSpacecraftState resetState(final FieldLoggingWrapper wrapper, final FieldSpacecraftState oldState) { - return wrapper.detector.resetState(oldState); + /** {@inheritDoc} */ + public Action eventOccurred(final FieldSpacecraftState s, + final FieldEventDetector d, + final boolean increasing) { + logEvent(s, increasing); + return handler.eventOccurred(s, detector, increasing); + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState resetState(final FieldEventDetector d, + final FieldSpacecraftState oldState) { + return handler.resetState(detector, oldState); + } + + }; } } diff --git a/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java b/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java index 7cc753906e..d72651c2c0 100644 --- a/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java @@ -55,26 +55,26 @@ public class FieldFunctionalDetector> * @param field on which this detector is defined. */ public FieldFunctionalDetector(final Field field) { - this(field.getZero().add(DEFAULT_MAXCHECK), - field.getZero().add(DEFAULT_THRESHOLD), - DEFAULT_MAX_ITER, - new FieldContinueOnEvent<>(), value -> field.getOne()); + this(s -> DEFAULT_MAXCHECK, + field.getZero().add(DEFAULT_THRESHOLD), + DEFAULT_MAX_ITER, + new FieldContinueOnEvent<>(), value -> field.getOne()); } /** * Private constructor. * - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param function the switching function. */ - private FieldFunctionalDetector( - final T maxCheck, + protected FieldFunctionalDetector( + final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, - final FieldEventHandler, T> handler, + final FieldEventHandler handler, final Function, T> function) { super(maxCheck, threshold, maxIter, handler); this.function = function; @@ -88,10 +88,10 @@ public T g(final FieldSpacecraftState s) { @Override protected FieldFunctionalDetector create( - final T newMaxCheck, + final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { + final FieldEventHandler newHandler) { return new FieldFunctionalDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, function); @@ -99,7 +99,7 @@ protected FieldFunctionalDetector create( /** * Create a new event detector with a new g function, keeping all other attributes the - * same. It is recommended to use {@link #withMaxCheck(CalculusFieldElement)} and {@link + * same. It is recommended to use {@link #withMaxCheck(FieldAdaptableInterval)} and {@link * #withThreshold(CalculusFieldElement)} to set appropriate values for this g function. * * @param newGFunction the new g function. diff --git a/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java index 68dd97ed24..afc334971f 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,6 +30,7 @@ * @author Luc Maisonobe * @author Evan Ward * @since 9.3 + * @param type of the field elements */ public class FieldLatitudeCrossingDetector > extends FieldAbstractDetector, T> { @@ -51,8 +52,8 @@ public class FieldLatitudeCrossingDetector > public FieldLatitudeCrossingDetector(final Field field, final OneAxisEllipsoid body, final double latitude) { - this(field.getZero().add(DEFAULT_MAXCHECK), - field.getZero().add(DEFAULT_THRESHOLD), + this(s -> DEFAULT_MAXCHECK, + field.getZero().add(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, latitude); } @@ -67,28 +68,28 @@ public FieldLatitudeCrossingDetector(final T maxCheck, final T threshold, final OneAxisEllipsoid body, final double latitude) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, latitude); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param body body on which the latitude is defined * @param latitude latitude to be crossed */ - private FieldLatitudeCrossingDetector( - final T maxCheck, + protected FieldLatitudeCrossingDetector( + final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, - final FieldEventHandler, T> handler, + final FieldEventHandler handler, final OneAxisEllipsoid body, final double latitude) { super(maxCheck, threshold, maxIter, handler); @@ -99,10 +100,10 @@ private FieldLatitudeCrossingDetector( /** {@inheritDoc} */ @Override protected FieldLatitudeCrossingDetector create( - final T newMaxCheck, + final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { + final FieldEventHandler newHandler) { return new FieldLatitudeCrossingDetector<>( newMaxCheck, newThreshold, newMaxIter, newHandler, body, latitude); } @@ -134,7 +135,7 @@ public T g(final FieldSpacecraftState s) { // convert state to geodetic coordinates final FieldGeodeticPoint gp = body.transform( - s.getPVCoordinates().getPosition(), + s.getPosition(), s.getFrame(), s.getDate()); diff --git a/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java new file mode 100644 index 0000000000..bcf25f9169 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java @@ -0,0 +1,247 @@ +/* Copyright 2023-2023 Alberto Ferrero + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Alberto Ferrero licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; +import org.orekit.time.FieldAbsoluteDate; + +/** Detector for geographic longitude crossing. + *

        This detector identifies when a spacecraft crosses a fixed + * longitude with respect to a central body.

        + * @author Alberto Ferrero + * @since 12.0 + * @param type of the field elements + */ +public class FieldLongitudeCrossingDetector > + extends FieldAbstractDetector, T> { + + /** + * Body on which the longitude is defined. + */ + private OneAxisEllipsoid body; + + /** + * Fixed longitude to be crossed. + */ + private final double longitude; + + /** + * Filtering detector. + */ + private final FieldEventEnablingPredicateFilter filtering; + + /** + * Build a new detector. + *

        The new instance uses default values for maximal checking interval + * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link + * #DEFAULT_THRESHOLD}).

        + * + * @param field the type of numbers to use. + * @param body body on which the longitude is defined + * @param longitude longitude to be crossed + */ + public FieldLongitudeCrossingDetector(final Field field, final OneAxisEllipsoid body, final double longitude) { + this(s -> DEFAULT_MAXCHECK, + field.getZero().add(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, longitude); + } + + /** + * Build a detector. + * + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param body body on which the longitude is defined + * @param longitude longitude to be crossed + */ + public FieldLongitudeCrossingDetector(final T maxCheck, + final T threshold, + final OneAxisEllipsoid body, + final double longitude) { + this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, longitude); + } + + /** + * Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

        + * + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @param body body on which the longitude is defined + * @param longitude longitude to be crossed + */ + protected FieldLongitudeCrossingDetector( + final FieldAdaptableInterval maxCheck, + final T threshold, + final int maxIter, + final FieldEventHandler handler, + final OneAxisEllipsoid body, + final double longitude) { + + super(maxCheck, threshold, maxIter, handler); + + this.body = body; + this.longitude = longitude; + + // we filter out spurious longitude crossings occurring at the antimeridian + final FieldRawLongitudeCrossingDetector raw = new FieldRawLongitudeCrossingDetector<>(maxCheck, threshold, maxIter, + new FieldContinueOnEvent<>()); + final FieldEnablingPredicate predicate = + (state, detector, g) -> FastMath.abs(g).getReal() < 0.5 * FastMath.PI; + this.filtering = new FieldEventEnablingPredicateFilter(raw, predicate); + + } + + /** + * {@inheritDoc} + */ + @Override + protected FieldLongitudeCrossingDetector create( + final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldLongitudeCrossingDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, + body, longitude); + } + + /** + * Get the body on which the geographic zone is defined. + * + * @return body on which the geographic zone is defined + */ + public OneAxisEllipsoid getBody() { + return body; + } + + /** + * Get the fixed longitude to be crossed (radians). + * + * @return fixed longitude to be crossed (radians) + */ + public double getLongitude() { + return longitude; + } + + /** + * {@inheritDoc} + */ + public void init(final FieldSpacecraftState s0, final FieldAbsoluteDate t) { + filtering.init(s0, t); + } + + /** + * Compute the value of the detection function. + *

        + * The value is the longitude difference between the spacecraft and the fixed + * longitude to be crossed, with some sign tweaks to ensure continuity. + * These tweaks imply the {@code increasing} flag in events detection becomes + * irrelevant here! As an example, the longitude of a prograde spacecraft + * will always increase, but this g function will increase and decrease so it + * will cross the zero value once per orbit, in increasing and decreasing + * directions on alternate orbits. If eastwards and westwards crossing have to + * be distinguished, the velocity direction has to be checked instead of looking + * at the {@code increasing} flag. + *

        + * + * @param s the current state information: date, kinematics, attitude + * @return longitude difference between the spacecraft and the fixed + * longitude, with some sign tweaks to ensure continuity + */ + public T g(final FieldSpacecraftState s) { + return filtering.g(s); + } + + private class FieldRawLongitudeCrossingDetector > + extends FieldAbstractDetector, TT> { + + /** + * Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

        + * + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + */ + protected FieldRawLongitudeCrossingDetector( + final FieldAdaptableInterval maxCheck, + final TT threshold, + final int maxIter, + final FieldEventHandler handler) { + super(maxCheck, threshold, maxIter, handler); + } + + /** + * {@inheritDoc} + */ + @Override + protected FieldRawLongitudeCrossingDetector create( + final FieldAdaptableInterval newMaxCheck, + final TT newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldRawLongitudeCrossingDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler); + } + + /** + * Compute the value of the detection function. + *

        + * The value is the longitude difference between the spacecraft and the fixed + * longitude to be crossed, and it does change sign twice around + * the central body: once at expected longitude and once at antimeridian. + * The second sign change is a spurious one and is filtered out by the + * outer class. + *

        + * + * @param s the current state information: date, kinematics, attitude + * @return longitude difference between the spacecraft and the fixed + * longitude + */ + public TT g(final FieldSpacecraftState s) { + + // convert state to geodetic coordinates + final FieldGeodeticPoint gp = body.transform(s.getPosition(), + s.getFrame(), s.getDate()); + + // longitude difference + final TT zero = gp.getLongitude().getField().getZero(); + return MathUtils.normalizeAngle(gp.getLongitude().subtract(longitude), zero); + + } + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java b/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java new file mode 100644 index 0000000000..5a17b21f32 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldNegateDetector.java @@ -0,0 +1,104 @@ +/* Contributed in the public domain. + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.time.FieldAbsoluteDate; + +/** + * An event detector that negates the sign on another event detector's {@link + * FieldEventDetector#g(FieldSpacecraftState) g} function. + * + * @since 12.0 + * @param type of the field element + * @author Evan Ward + * @author Luc Maisonobe + */ +public class FieldNegateDetector> extends FieldAbstractDetector, T> { + + /** the delegate event detector. */ + private final FieldEventDetector original; + + /** + * Create a new event detector that negates an existing event detector. + * + *

        This detector will be initialized with the same {@link + * FieldEventDetector#getMaxCheckInterval()}, {@link FieldEventDetector#getThreshold()}, and + * {@link FieldEventDetector#getMaxIterationCount()} as {@code original}. Initially this + * detector will use the {@link FieldContinueOnEvent} event handler. + * + * @param original detector. + */ + public FieldNegateDetector(final FieldEventDetector original) { + this(original.getMaxCheckInterval(), + original.getThreshold(), + original.getMaxIterationCount(), + new FieldContinueOnEvent<>(), + original); + } + + /** + * Private constructor. + * + * @param newMaxCheck max check interval. + * @param newThreshold convergence threshold in seconds. + * @param newMaxIter max iterations. + * @param newHandler event handler. + * @param original event detector. + */ + protected FieldNegateDetector(final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler, + final FieldEventDetector original) { + super(newMaxCheck, newThreshold, newMaxIter, newHandler); + this.original = original; + } + + /** + * Get the delegate event detector. + * @return the delegate event detector + */ + public FieldEventDetector getOriginal() { + return original; + } + + @Override + public void init(final FieldSpacecraftState s0, + final FieldAbsoluteDate t) { + super.init(s0, t); + original.init(s0, t); + } + + @Override + public T g(final FieldSpacecraftState s) { + return original.g(s).negate(); + } + + @Override + protected FieldNegateDetector create(final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldNegateDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, + original); + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java b/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java index 15efea0ed2..a35daceca1 100644 --- a/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,7 +25,7 @@ import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; @@ -45,6 +45,7 @@ * encountered during validation ...

        * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) * @author Luc Maisonobe + * @param type of the field elements */ public class FieldNodeDetector> extends FieldAbstractDetector, T> { @@ -73,18 +74,18 @@ public FieldNodeDetector(final FieldOrbit orbit, final Frame frame) { * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) */ public FieldNodeDetector(final T threshold, final FieldOrbit orbit, final Frame frame) { - this(orbit.getA().getField().getZero().add(2 * estimateNodesTimeSeparation(orbit.toOrbit()) / 3), threshold, - DEFAULT_MAX_ITER, new FieldStopOnIncreasing, T>(), + this(s -> orbit.getA().getField().getZero().add(2 * estimateNodesTimeSeparation(orbit.toOrbit()) / 3).getReal(), threshold, + DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), frame); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -93,17 +94,17 @@ public FieldNodeDetector(final T threshold, final FieldOrbit orbit, final Fra * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) * @since 6.1 */ - private FieldNodeDetector(final T maxCheck, final T threshold, - final int maxIter, final FieldEventHandler, T> handler, - final Frame frame) { + protected FieldNodeDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final Frame frame) { super(maxCheck, threshold, maxIter, handler); this.frame = frame; } /** {@inheritDoc} */ @Override - protected FieldNodeDetector create(final T newMaxCheck, final T newThreshold, - final int newMaxIter, final FieldEventHandler, T> newHandler) { + protected FieldNodeDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { return new FieldNodeDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, frame); } @@ -125,7 +126,7 @@ private static double estimateNodesTimeSeparation(final Orbit orbit) { keplerian.getI(), keplerian.getPerigeeArgument(), keplerian.getRightAscensionOfAscendingNode(), - -keplerian.getPerigeeArgument(), PositionAngle.TRUE, + -keplerian.getPerigeeArgument(), PositionAngleType.TRUE, keplerian.getFrame(), keplerian.getDate(), keplerian.getMu()).getMeanAnomaly(); @@ -134,7 +135,7 @@ private static double estimateNodesTimeSeparation(final Orbit orbit) { keplerian.getI(), keplerian.getPerigeeArgument(), keplerian.getRightAscensionOfAscendingNode(), - FastMath.PI - keplerian.getPerigeeArgument(), PositionAngle.TRUE, + FastMath.PI - keplerian.getPerigeeArgument(), PositionAngleType.TRUE, keplerian.getFrame(), keplerian.getDate(), keplerian.getMu()).getMeanAnomaly(); @@ -160,7 +161,7 @@ public Frame getFrame() { * @return value of the switching function */ public T g(final FieldSpacecraftState s) { - return s.getPVCoordinates(frame).getPosition().getZ(); + return s.getPosition(frame).getZ(); } // public NodeDetector toNoField() { diff --git a/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java b/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java index 77a7437de4..ff48ac2ce8 100644 --- a/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -81,18 +81,18 @@ public FieldOfViewDetector(final PVCoordinatesProvider pvTarget, final FieldOfVi */ public FieldOfViewDetector(final PVCoordinatesProvider pvTarget, final double radiusTarget, final VisibilityTrigger trigger, final FieldOfView fov) { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, - new StopOnIncreasing(), + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new StopOnIncreasing(), pvTarget, radiusTarget, trigger, fov); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -101,10 +101,10 @@ public FieldOfViewDetector(final PVCoordinatesProvider pvTarget, final double ra * @param trigger visibility trigger for spherical bodies * @param fov Field Of View */ - private FieldOfViewDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler, - final PVCoordinatesProvider pvTarget, final double radiusTarget, - final VisibilityTrigger trigger, final FieldOfView fov) { + protected FieldOfViewDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler, + final PVCoordinatesProvider pvTarget, final double radiusTarget, + final VisibilityTrigger trigger, final FieldOfView fov) { super(maxCheck, threshold, maxIter, handler); this.targetPVProvider = pvTarget; this.radiusTarget = radiusTarget; @@ -114,9 +114,9 @@ private FieldOfViewDetector(final double maxCheck, final double threshold, final /** {@inheritDoc} */ @Override - protected FieldOfViewDetector create(final double newMaxCheck, final double newThreshold, + protected FieldOfViewDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new FieldOfViewDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, targetPVProvider, radiusTarget, trigger, fov); } @@ -156,7 +156,7 @@ public double g(final SpacecraftState s) { // get line of sight in spacecraft frame final Vector3D targetPosInert = - targetPVProvider.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + targetPVProvider.getPosition(s.getDate(), s.getFrame()); final Vector3D lineOfSightSC = s.toTransform().transformPosition(targetPosInert); final double angularRadius = FastMath.asin(radiusTarget / lineOfSightSC.getNorm()); diff --git a/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java b/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java index bad86523dd..d10ca37ac3 100644 --- a/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,6 +31,8 @@ import org.orekit.utils.DateDriver; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** Detector for date intervals that may be offset thanks to parameter drivers. *

        @@ -47,7 +49,8 @@ * @author Luc Maisonobe * @since 11.1 */ -public class FieldParameterDrivenDateIntervalDetector> extends FieldAbstractDetector, T> { +public class FieldParameterDrivenDateIntervalDetector> + extends FieldAbstractDetector, T> { /** Default suffix for start driver. */ public static final String START_SUFFIX = "_START"; @@ -94,23 +97,23 @@ public FieldParameterDrivenDateIntervalDetector(final Field field, final Stri */ public FieldParameterDrivenDateIntervalDetector(final Field field, final String prefix, final AbsoluteDate refStart, final AbsoluteDate refStop) { - this(field.getZero().newInstance(DEFAULT_MAXCHECK), + this(s -> DEFAULT_MAXCHECK, field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, - new FieldStopOnEvent, T>(), + new FieldStopOnEvent<>(), new DateDriver(refStart, prefix + START_SUFFIX, true), new DateDriver(refStop, prefix + STOP_SUFFIX, false), new DateDriver(refStart.shiftedBy(0.5 * refStop.durationFrom(refStart)), prefix + MEDIAN_SUFFIX, true), new ParameterDriver(prefix + DURATION_SUFFIX, refStop.durationFrom(refStart), 1.0, 0.0, Double.POSITIVE_INFINITY)); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -119,10 +122,10 @@ public FieldParameterDrivenDateIntervalDetector(final Field field, final Stri * @param median median date driver * @param duration duration driver */ - private FieldParameterDrivenDateIntervalDetector(final T maxCheck, final T threshold, final int maxIter, - final FieldEventHandler, T> handler, - final DateDriver start, final DateDriver stop, - final DateDriver median, final ParameterDriver duration) { + protected FieldParameterDrivenDateIntervalDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, + final FieldEventHandler handler, + final DateDriver start, final DateDriver stop, + final DateDriver median, final ParameterDriver duration) { super(maxCheck, threshold, maxIter, handler); this.start = start; this.stop = stop; @@ -157,8 +160,8 @@ private void replaceBindingObserver(final ParameterDriver driver, final BindingO /** {@inheritDoc} */ @Override - protected FieldParameterDrivenDateIntervalDetector create(final T newMaxCheck, final T newThreshold, final int newMaxIter, - final FieldEventHandler, T> newHandler) { + protected FieldParameterDrivenDateIntervalDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, final int newMaxIter, + final FieldEventHandler newHandler) { return new FieldParameterDrivenDateIntervalDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, start, stop, median, duration); } @@ -233,9 +236,19 @@ private abstract class BindingObserver implements ParameterObserver { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { if (driver.isSelected()) { - setDelta(driver.getValue() - previousValue); + setDelta(driver.getValue(date) - previousValue, date); + } + } + + /** {@inheritDoc} */ + @Override + public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { + if (driver.isSelected()) { + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + setDelta(span.getData() - previousValue.get(span.getStart()), span.getStart()); + } } } @@ -251,9 +264,10 @@ public void selectionChanged(final boolean previousSelection, final ParameterDri } /** Change a value. + * @param date date for which the value wants to be change * @param delta change of value */ - protected abstract void setDelta(double delta); + protected abstract void setDelta(double delta, AbsoluteDate date); } @@ -261,9 +275,9 @@ public void selectionChanged(final boolean previousSelection, final ParameterDri private class StartObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - median.setValue(median.getValue() + 0.5 * delta); - duration.setValue(duration.getValue() - delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + median.setValue(median.getValue(date) + 0.5 * delta, date); + duration.setValue(duration.getValue(date) - delta, date); } } @@ -271,9 +285,9 @@ protected void setDelta(final double delta) { private class StopObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - median.setValue(median.getValue() + 0.5 * delta); - duration.setValue(duration.getValue() + delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + median.setValue(median.getValue(date) + 0.5 * delta, date); + duration.setValue(duration.getValue(date) + delta, date); } } @@ -281,9 +295,9 @@ protected void setDelta(final double delta) { private class MedianObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - start.setValue(start.getValue() + delta); - stop.setValue(stop.getValue() + delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + start.setValue(start.getValue(date) + delta, date); + stop.setValue(stop.getValue(date) + delta, date); } } @@ -291,9 +305,9 @@ protected void setDelta(final double delta) { private class DurationObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - start.setValue(start.getValue() - 0.5 * delta); - stop.setValue(stop.getValue() + 0.5 * delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + start.setValue(start.getValue(date) - 0.5 * delta, date); + stop.setValue(stop.getValue(date) + 0.5 * delta, date); } } diff --git a/src/main/java/org/orekit/propagation/events/FilterType.java b/src/main/java/org/orekit/propagation/events/FilterType.java index 5f37798b20..2bc425c3c6 100644 --- a/src/main/java/org/orekit/propagation/events/FilterType.java +++ b/src/main/java/org/orekit/propagation/events/FilterType.java @@ -18,6 +18,7 @@ package org.orekit.propagation.events; import org.orekit.errors.OrekitInternalError; +import org.orekit.propagation.events.handlers.EventHandler; /** Enumerate for {@link EventSlopeFilter filtering events}. *

        This class is heavily based on the class with the same name from the @@ -29,11 +30,11 @@ public enum FilterType { /** Constant for triggering only decreasing events. - *

        When this filter is used, the wrapped {@link EventDetector - * event detector} {@link - * EventDetector#eventOccurred(org.orekit.propagation.SpacecraftState, - * boolean) eventOccurred} method will be called only with - * its {@code increasing} argument set to false.

        + *

        When this filter is used, the wrapped {@link EventHandler + * event handler} {@link + * EventHandler#eventOccurred(org.orekit.propagation.SpacecraftState, + * EventDetector, boolean) eventOccurred} method will be called + * only with its {@code increasing} argument set to false.

        */ TRIGGER_ONLY_DECREASING_EVENTS { @@ -179,10 +180,9 @@ protected Transformer selectTransformer(final Transformer previous, }, /** Constant for triggering only increasing events. - *

        When this filter is used, the wrapped {@link EventDetector - * event detector} {@link - * EventDetector#eventOccurred(org.orekit.propagation.SpacecraftState, - * boolean) eventOccurred} method will be called only with + *

        When this filter is used, the wrapped {@link EventHandler + * event handler} {@link EventHandler#eventOccurred(org.orekit.propagation.SpacecraftState, + * EventDetector, boolean) eventOccurred} method will be called only with * its {@code increasing} argument set to true.

        */ TRIGGER_ONLY_INCREASING_EVENTS { diff --git a/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java b/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java index d373515766..b4ec6cb398 100644 --- a/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java +++ b/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -103,18 +103,18 @@ public FootprintOverlapDetector(final FieldOfView fov, final OneAxisEllipsoid body, final SphericalPolygonsSet zone, final double samplingStep) { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, - new StopOnIncreasing(), + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new StopOnIncreasing(), fov, body, zone, samplingStep, sample(body, zone, samplingStep)); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -124,13 +124,13 @@ public FootprintOverlapDetector(final FieldOfView fov, * @param sampledZone sampling of the geographic zone * @param samplingStep linear step used for sampling the geographic zone (in meters) */ - private FootprintOverlapDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final FieldOfView fov, - final OneAxisEllipsoid body, - final SphericalPolygonsSet zone, - final double samplingStep, - final List sampledZone) { + protected FootprintOverlapDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final FieldOfView fov, + final OneAxisEllipsoid body, + final SphericalPolygonsSet zone, + final double samplingStep, + final List sampledZone) { super(maxCheck, threshold, maxIter, handler); this.fov = fov; @@ -192,9 +192,9 @@ private static List sample(final OneAxisEllipsoid body, /** {@inheritDoc} */ @Override - protected FootprintOverlapDetector create(final double newMaxCheck, final double newThreshold, + protected FootprintOverlapDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new FootprintOverlapDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, fov, body, zone, samplingStep, sampledZone); } @@ -248,7 +248,7 @@ public double g(final SpacecraftState s) { double value = FastMath.PI; // get spacecraft position in body frame - final Vector3D scBody = s.getPVCoordinates(body.getBodyFrame()).getPosition(); + final Vector3D scBody = s.getPosition(body.getBodyFrame()); // map the point to a sphere final GeodeticPoint gp = body.transform(scBody, body.getBodyFrame(), s.getDate()); diff --git a/src/main/java/org/orekit/propagation/events/FunctionalDetector.java b/src/main/java/org/orekit/propagation/events/FunctionalDetector.java index c44193e6c2..c68f4a8501 100644 --- a/src/main/java/org/orekit/propagation/events/FunctionalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FunctionalDetector.java @@ -48,25 +48,25 @@ public class FunctionalDetector extends AbstractDetector { * ContinueOnEvent}, and a g function that is identically unity. */ public FunctionalDetector() { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, - new ContinueOnEvent<>(), - (ToDoubleFunction) value -> 1.0); + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new ContinueOnEvent(), + (ToDoubleFunction) value -> 1.0); } /** * Private constructor. * - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param function the switching function. */ - private FunctionalDetector(final double maxCheck, - final double threshold, - final int maxIter, - final EventHandler handler, - final ToDoubleFunction function) { + protected FunctionalDetector(final AdaptableInterval maxCheck, + final double threshold, + final int maxIter, + final EventHandler handler, + final ToDoubleFunction function) { super(maxCheck, threshold, maxIter, handler); this.function = function; } @@ -79,10 +79,10 @@ public double g(final SpacecraftState s) { @Override protected FunctionalDetector create( - final double newMaxCheck, + final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new FunctionalDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, function); @@ -90,7 +90,7 @@ protected FunctionalDetector create( /** * Create a new event detector with a new g function, keeping all other attributes the - * same. It is recommended to use {@link #withMaxCheck(double)} and {@link + * same. It is recommended to use {@link #withMaxCheck(AdaptableInterval)} and {@link * #withThreshold(double)} to set appropriate values for this g function. * * @param newGFunction the new g function. diff --git a/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java b/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java index 7038d0e01e..173eff4512 100644 --- a/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java +++ b/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -77,17 +77,17 @@ public GeographicZoneDetector(final BodyShape body, public GeographicZoneDetector(final double maxCheck, final double threshold, final BodyShape body, final SphericalPolygonsSet zone, final double margin) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, zone, zone.getEnclosingCap(), margin); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -96,12 +96,12 @@ public GeographicZoneDetector(final double maxCheck, final double threshold, * @param cap spherical cap surrounding the zone * @param margin angular margin to apply to the zone */ - private GeographicZoneDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final BodyShape body, - final SphericalPolygonsSet zone, - final EnclosingBall cap, - final double margin) { + protected GeographicZoneDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final BodyShape body, + final SphericalPolygonsSet zone, + final EnclosingBall cap, + final double margin) { super(maxCheck, threshold, maxIter, handler); this.body = body; this.zone = zone; @@ -111,8 +111,8 @@ private GeographicZoneDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected GeographicZoneDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected GeographicZoneDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new GeographicZoneDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, body, zone, cap, margin); } @@ -159,7 +159,7 @@ public double getMargin() { public double g(final SpacecraftState s) { // convert state to geodetic coordinates - final GeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), + final GeodeticPoint gp = body.transform(s.getPosition(), s.getFrame(), s.getDate()); // map the point to a sphere (geodetic coordinates have already taken care of ellipsoid flatness) diff --git a/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java b/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java index b97516701b..d5174faf34 100644 --- a/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java +++ b/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -83,8 +83,8 @@ public GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoor final double dawnDuskElevation, final AtmosphericRefractionModel refractionModel) { this(groundLocation, sun, dawnDuskElevation, refractionModel, - DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, - new ContinueOnEvent<>()); + s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new ContinueOnEvent()); } /** Private constructor. @@ -93,18 +93,18 @@ public GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoor * @param dawnDuskElevation Sun elevation below which we consider night is dark enough (rad) * (typically {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION}) * @param refractionModel reference to refraction model (null if refraction should be ignored), - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences */ - private GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoordinatesProvider sun, - final double dawnDuskElevation, - final AtmosphericRefractionModel refractionModel, - final double maxCheck, - final double threshold, - final int maxIter, - final EventHandler handler) { + protected GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoordinatesProvider sun, + final double dawnDuskElevation, + final AtmosphericRefractionModel refractionModel, + final AdaptableInterval maxCheck, + final double threshold, + final int maxIter, + final EventHandler handler) { super(maxCheck, threshold, maxIter, handler); this.groundLocation = groundLocation; this.sun = sun; @@ -114,10 +114,10 @@ private GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoo /** {@inheritDoc} */ @Override - protected GroundAtNightDetector create(final double newMaxCheck, + protected GroundAtNightDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new GroundAtNightDetector(groundLocation, sun, dawnDuskElevation, refractionModel, newMaxCheck, newThreshold, newMaxIter, newHandler); } @@ -136,7 +136,7 @@ public double g(final SpacecraftState state) { final AbsoluteDate date = state.getDate(); final Frame frame = state.getFrame(); - final Vector3D position = sun.getPVCoordinates(date, frame).getPosition(); + final Vector3D position = sun.getPosition(date, frame); final double trueElevation = groundLocation.getElevation(position, frame, date); final double calculatedElevation; diff --git a/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java b/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java index 30fd63e3dc..fc3399f5d9 100644 --- a/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -64,31 +64,31 @@ public class GroundFieldOfViewDetector extends AbstractDetector(), - frame, fov); + this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new StopOnIncreasing(), + frame, fov); } /** - * Private constructor with full parameters. - * - *

        This constructor is private as users are expected to use the builder + * Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance in * a readable manner without using a huge amount of parameters.

        * - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param frame the reference frame attached to the sensor. * @param fov Field Of View of the sensor. */ - private GroundFieldOfViewDetector(final double maxCheck, - final double threshold, - final int maxIter, - final EventHandler handler, - final Frame frame, - final FieldOfView fov) { + protected GroundFieldOfViewDetector(final AdaptableInterval maxCheck, + final double threshold, + final int maxIter, + final EventHandler handler, + final Frame frame, + final FieldOfView fov) { super(maxCheck, threshold, maxIter, handler); this.frame = frame; this.fov = fov; @@ -96,10 +96,10 @@ private GroundFieldOfViewDetector(final double maxCheck, /** {@inheritDoc} */ @Override - protected GroundFieldOfViewDetector create(final double newMaxCheck, + protected GroundFieldOfViewDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new GroundFieldOfViewDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, this.frame, this.fov); } @@ -137,7 +137,7 @@ public FieldOfView getFOV() { public double g(final SpacecraftState s) { // get line of sight in sensor frame - final Vector3D los = s.getPVCoordinates(this.frame).getPosition(); + final Vector3D los = s.getPosition(this.frame); return this.fov.offsetFromBoundary(los, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV); } diff --git a/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java b/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java index f67f1e0c18..d7e4e5a2c5 100644 --- a/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -32,33 +32,33 @@ public class HaloXZPlaneCrossingDetector extends AbstractDetector()); + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, + new StopOnIncreasing()); } /** - * Private constructor with full parameters. + * Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder API + * This constructor is not public as users are expected to use the builder API * with the various {@code withXxx()} methods to set up the instance in a * readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences */ - private HaloXZPlaneCrossingDetector(final double maxCheck, final double threshold, - final int maxIter, - final EventHandler handler) { + protected HaloXZPlaneCrossingDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, + final EventHandler handler) { super(maxCheck, threshold, maxIter, handler); } /** {@inheritDoc} */ @Override - protected HaloXZPlaneCrossingDetector create(final double newMaxCheck, final double newThreshold, + protected HaloXZPlaneCrossingDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new HaloXZPlaneCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); } @@ -67,7 +67,7 @@ protected HaloXZPlaneCrossingDetector create(final double newMaxCheck, final dou * @return Position on Y axis */ public double g(final SpacecraftState s) { - return s.getPVCoordinates().getPosition().getY(); + return s.getPosition().getY(); } } diff --git a/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java b/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java index 8a1e3a9c04..b251eb542b 100644 --- a/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,7 @@ */ package org.orekit.propagation.events; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.FastMath; +import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.frames.Frame; import org.orekit.propagation.PropagatorsParallelizer; @@ -72,11 +71,10 @@ public class InterSatDirectViewDetector extends AbstractDetector()); + this(body, 0.0, secondary, s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new ContinueOnEvent()); } /** Private constructor. * @param body central body + * @param skimmingAltitude skimming altitude at which events are triggered * @param secondary provider for the secondary satellite - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences + * @since 12.0 */ - private InterSatDirectViewDetector(final OneAxisEllipsoid body, - final PVCoordinatesProvider secondary, - final double maxCheck, - final double threshold, - final int maxIter, - final EventHandler handler) { + protected InterSatDirectViewDetector(final OneAxisEllipsoid body, + final double skimmingAltitude, + final PVCoordinatesProvider secondary, + final AdaptableInterval maxCheck, + final double threshold, + final int maxIter, + final EventHandler handler) { super(maxCheck, threshold, maxIter, handler); - this.body = body; - this.ae2 = body.getEquatorialRadius() * body.getEquatorialRadius(); - this.g2 = (1.0 - body.getFlattening()) * (1.0 - body.getFlattening()); - this.secondary = secondary; + this.body = body; + this.skimmingAltitude = skimmingAltitude; + this.secondary = secondary; } /** Get the central body. @@ -119,6 +119,14 @@ public OneAxisEllipsoid getCentralBody() { return body; } + /** Get the skimming altitude. + * @return skimming altitude at which events are triggered + * @since 12.0 + */ + public double getSkimmingAltitude() { + return skimmingAltitude; + } + /** Get the provider for the secondary satellite. * @return provider for the secondary satellite */ @@ -128,69 +136,53 @@ public PVCoordinatesProvider getSecondary() { /** {@inheritDoc} */ @Override - protected InterSatDirectViewDetector create(final double newMaxCheck, + protected InterSatDirectViewDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { - return new InterSatDirectViewDetector(body, secondary, newMaxCheck, newThreshold, newMaxIter, newHandler); + final EventHandler newHandler) { + return new InterSatDirectViewDetector(body, skimmingAltitude, secondary, + newMaxCheck, newThreshold, newMaxIter, newHandler); + } + + /** + * Setup the skimming altitude. + *

        + * The skimming altitude is the lowest altitude of the path between satellites + * at which events should be triggered. If set to 0.0, events are triggered + * exactly when the path passes just at central body limb. + *

        + * @param newSkimmingAltitude skimming altitude (m) + * @return a new detector with updated configuration (the instance is not changed) + * @see #getSkimmingAltitude() + * @since 12.0 + */ + public InterSatDirectViewDetector withSkimmingAltitude(final double newSkimmingAltitude) { + return new InterSatDirectViewDetector(body, newSkimmingAltitude, secondary, + getMaxCheckInterval(), getThreshold(), + getMaxIterationCount(), getHandler()); } /** {@inheritDoc} *

        - * The {@code g} function of this detector is positive when satellites can see - * each other directly and negative when the central body limb is in between and - * blocks the direct view. + * The {@code g} function of this detector is the difference between the minimum + * altitude of intermediate points along the line of sight between satellites and the + * {@link #getSkimmingAltitude() skimming altitude}. It is therefore positive when + * all intermediate points are above the skimming altitude, meaning satellites can see + * each other and it is negative when some intermediate points (which may be either + * endpoints) dive below this altitude, meaning satellites cannot see each other. *

        */ @Override public double g(final SpacecraftState state) { - // get the line between primary and secondary in body frame - final AbsoluteDate date = state.getDate(); - final Frame frame = body.getBodyFrame(); - final Vector3D pPrimary = state.getPVCoordinates(frame).getPosition(); - final Vector3D pSecondary = secondary.getPVCoordinates(date, frame).getPosition(); - - // points along the primary/secondary lines are defined as - // xk = x + k * dx, yk = y + k * dy, zk = z + k * dz - // so k is 0 at primary and 1 at secondary - final double x = pPrimary.getX(); - final double y = pPrimary.getY(); - final double z = pPrimary.getZ(); - final double dx = pSecondary.getX() - x; - final double dy = pSecondary.getY() - y; - final double dz = pSecondary.getZ() - z; - - // intersection between line and central body surface - // is a root of a 2nd degree polynomial : - // a k^2 - 2 b k + c = 0 - final double a = g2 * (dx * dx + dy * dy) + dz * dz; - final double b = -(g2 * (x * dx + y * dy) + z * dz); - final double c = g2 * (x * x + y * y - ae2) + z * z; - final double s = b * b - a * c; - if (s < 0) { - // the quadratic has no solution, the line between primary and secondary - // doesn't crosses central body limb, direct view is possible - // return a positive value, preserving continuity across zero crossing - return -s; - } - - // the quadratic has two solutions (degenerated to one if s = 0) - // direct view is blocked when one of these solutions is between 0 and 1 - final double k1 = (b < 0) ? (b - FastMath.sqrt(s)) / a : c / (b + FastMath.sqrt(s)); - final double k2 = c / (a * k1); - if (FastMath.max(k1, k2) < 0.0 || FastMath.min(k1, k2) > 1.0) { - // the intersections are either behind primary or farther away than secondary - // along the line, direct view is possible - // return a positive value, preserving continuity across zero crossing - return s; - } else { - // part of the central body is between primary and secondary - // this includes unrealistic cases where primary, secondary or both are inside the central body ;-) - // in all these cases, direct view is blocked - // return a negative value, preserving continuity across zero crossing - return -s; - } + // get the lowest point between primary and secondary + final AbsoluteDate date = state.getDate(); + final Frame frame = body.getBodyFrame(); + final GeodeticPoint lowest = body.lowestAltitudeIntermediate(state.getPosition(frame), + secondary.getPosition(date, frame)); + + // compute switching function value as altitude difference + return lowest.getAltitude() - skimmingAltitude; } diff --git a/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java index 45b5b2b3dc..f3cad05bbb 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -55,26 +55,26 @@ public LatitudeCrossingDetector(final OneAxisEllipsoid body, final double latitu */ public LatitudeCrossingDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body, final double latitude) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, latitude); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param body body on which the latitude is defined * @param latitude latitude to be crossed */ - private LatitudeCrossingDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final OneAxisEllipsoid body, final double latitude) { + protected LatitudeCrossingDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final OneAxisEllipsoid body, final double latitude) { super(maxCheck, threshold, maxIter, handler); this.body = body; this.latitude = latitude; @@ -82,9 +82,9 @@ private LatitudeCrossingDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected LatitudeCrossingDetector create(final double newMaxCheck, final double newThreshold, + protected LatitudeCrossingDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new LatitudeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, body, latitude); } @@ -115,7 +115,7 @@ public double getLatitude() { public double g(final SpacecraftState s) { // convert state to geodetic coordinates - final GeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), + final GeodeticPoint gp = body.transform(s.getPosition(), s.getFrame(), s.getDate()); // latitude difference diff --git a/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java index d40aa8548b..92890ae172 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -52,34 +52,34 @@ public LatitudeExtremumDetector(final OneAxisEllipsoid body) { */ public LatitudeExtremumDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param body body on which the latitude is defined */ - private LatitudeExtremumDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final OneAxisEllipsoid body) { + protected LatitudeExtremumDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final OneAxisEllipsoid body) { super(maxCheck, threshold, maxIter, handler); this.body = body; } /** {@inheritDoc} */ @Override - protected LatitudeExtremumDetector create(final double newMaxCheck, final double newThreshold, + protected LatitudeExtremumDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new LatitudeExtremumDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, body); } diff --git a/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java index 081e08ec80..cbb29adc9b 100644 --- a/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,11 +18,13 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; -import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.ContinueOnEvent; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnIncreasing; +import org.orekit.time.AbsoluteDate; /** Detector for geographic longitude crossing. *

        This detector identifies when a spacecraft crosses a fixed @@ -38,11 +40,8 @@ public class LongitudeCrossingDetector extends AbstractDetectorThe new instance uses default values for maximal checking interval @@ -63,40 +62,47 @@ public LongitudeCrossingDetector(final OneAxisEllipsoid body, final double longi */ public LongitudeCrossingDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body, final double longitude) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, longitude); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param body body on which the longitude is defined * @param longitude longitude to be crossed */ - private LongitudeCrossingDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final OneAxisEllipsoid body, final double longitude) { + protected LongitudeCrossingDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final OneAxisEllipsoid body, final double longitude) { + super(maxCheck, threshold, maxIter, handler); - this.body = body; - this.longitude = longitude; - this.sign = +1.0; - this.previousDelta = Double.NaN; + + this.body = body; + this.longitude = longitude; + + // we filter out spurious longitude crossings occurring at the antimeridian + final RawLongitudeCrossingDetector raw = new RawLongitudeCrossingDetector(maxCheck, threshold, maxIter, + new ContinueOnEvent()); + final EnablingPredicate predicate = + (state, detector, g) -> FastMath.abs(g) < 0.5 * FastMath.PI; + this.filtering = new EventEnablingPredicateFilter(raw, predicate); + } /** {@inheritDoc} */ @Override - protected LongitudeCrossingDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, - final EventHandler newHandler) { + protected LongitudeCrossingDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, + final EventHandler newHandler) { return new LongitudeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, - body, longitude); + body, longitude); } /** Get the body on which the geographic zone is defined. @@ -113,6 +119,11 @@ public double getLongitude() { return longitude; } + /** {@inheritDoc} */ + public void init(final SpacecraftState s0, final AbsoluteDate t) { + filtering.init(s0, t); + } + /** Compute the value of the detection function. *

        * The value is the longitude difference between the spacecraft and the fixed @@ -130,22 +141,57 @@ public double getLongitude() { * longitude, with some sign tweaks to ensure continuity */ public double g(final SpacecraftState s) { + return filtering.g(s); + } - // convert state to geodetic coordinates - final GeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), - s.getFrame(), s.getDate()); - - // longitude difference - double delta = MathUtils.normalizeAngle(sign * (gp.getLongitude() - longitude), 0.0); + private class RawLongitudeCrossingDetector extends AbstractDetector { + + /** Protected constructor with full parameters. + *

        + * This constructor is not public as users are expected to use the builder + * API with the various {@code withXxx()} methods to set up the instance + * in a readable manner without using a huge amount of parameters. + *

        + * @param maxCheck maximum checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + */ + protected RawLongitudeCrossingDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler) { + super(maxCheck, threshold, maxIter, handler); + } - // ensure continuity - if (FastMath.abs(delta - previousDelta) > FastMath.PI) { - sign = -sign; - delta = MathUtils.normalizeAngle(sign * (gp.getLongitude() - longitude), 0.0); + /** {@inheritDoc} */ + @Override + protected RawLongitudeCrossingDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { + return new RawLongitudeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler); } - previousDelta = delta; - return delta; + /** Compute the value of the detection function. + *

        + * The value is the longitude difference between the spacecraft and the fixed + * longitude to be crossed, and it does change sign twice around + * the central body: once at expected longitude and once at antimeridian. + * The second sign change is a spurious one and is filtered out by the + * outer class. + *

        + * @param s the current state information: date, kinematics, attitude + * @return longitude difference between the spacecraft and the fixed + * longitude + */ + public double g(final SpacecraftState s) { + + // convert state to geodetic coordinates + final GeodeticPoint gp = body.transform(s.getPosition(), + s.getFrame(), s.getDate()); + + // longitude difference + return MathUtils.normalizeAngle(gp.getLongitude() - longitude, 0.0); + + } } diff --git a/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java b/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java index 75fa1d8867..677e3f5427 100644 --- a/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -52,34 +52,34 @@ public LongitudeExtremumDetector(final OneAxisEllipsoid body) { */ public LongitudeExtremumDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body) { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param body body on which the longitude is defined */ - private LongitudeExtremumDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final OneAxisEllipsoid body) { + protected LongitudeExtremumDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final OneAxisEllipsoid body) { super(maxCheck, threshold, maxIter, handler); this.body = body; } /** {@inheritDoc} */ @Override - protected LongitudeExtremumDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, - final EventHandler newHandler) { + protected LongitudeExtremumDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { return new LongitudeExtremumDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, body); } diff --git a/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java b/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java index a48380c2da..7d3302d3f1 100644 --- a/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java +++ b/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,104 +21,107 @@ import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.models.earth.GeoMagneticField; +import org.orekit.models.earth.GeoMagneticFieldFactory; import org.orekit.models.earth.GeoMagneticFieldFactory.FieldModel; -import org.orekit.orbits.OrbitType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnIncreasing; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; -/** Detector for South-Atlantic anomaly frontier crossing. +/** Detector for Earth magnetic field strength. *

        - * The detector is based on the value of the earth magnetic field at see level at the satellite latitude and longitude. + * The detector is based on the field intensity calculated at the + * satellite's latitude and longitude, either at sea level or at + * satellite altitude, depending on the value chosen for the + * atSeaLevel indicator.
        + * It can detect flyovers of the South-Atlantic anomaly with + * a classically accepted limit value of 32,000 nT at sea level. *

        * @author Romaric Her */ public class MagneticFieldDetector extends AbstractDetector { - /** Fixed threshold value of Magnetic field to be crossed, in nano Teslas. */ + /** Fixed threshold value of Magnetic field to be crossed, in Teslas. */ private final double limit; - /** Fixed altitude of computed magnetic field value. */ - private final boolean seaLevel; + /** Switch for calculating field strength at sea level (true) or satellite altitude (false). */ + private final boolean atSeaLevel; - /** earth geomagnetic field. */ + /** Earth geomagnetic field. */ private GeoMagneticField field; /** year of the current state. */ private double currentYear; - /** the geomagnetic field model enum. */ - private final FieldModel type; + /** Earth geomagnetic field model. */ + private final FieldModel model; - /** the body. */ + /** Earth body shape. */ private final OneAxisEllipsoid body; - /** the timescale. */ + /** Current data context. */ private final DataContext dataContext; /** Build a new detector. - *

        The new instance uses default values for maximal checking interval - * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link - * #DEFAULT_THRESHOLD}).

        * - *

        This method uses the {@link DataContext#getDefault() default data context}. + *

        This constructor uses: + *

          + *
        • the {@link DataContext#getDefault() default data context}
        • + *
        • the {@link AbstractDetector#DEFAULT_MAXCHECK default value} for maximal checking interval
        • + *
        • the {@link AbstractDetector#DEFAULT_THRESHOLD default value} for convergence threshold
        • + *
        • the atSeaLevel switch set to false
        • + *
        * - * @param limit the threshold value of magnetic field at see level, in nano Teslas - * @param type the magnetic field model - * @param body the body - * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} + * @param limit threshold value for magnetic field detection, in Teslas + * @param model magnetic field model + * @param body Earth body shape * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext) */ @DefaultDataContext - public MagneticFieldDetector(final double limit, final FieldModel type, final OneAxisEllipsoid body) - throws OrekitIllegalArgumentException { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, type, body, false); + public MagneticFieldDetector(final double limit, final FieldModel model, final OneAxisEllipsoid body) { + this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, model, body, false); } /** Build a new detector. - *

        The new instance uses default values for maximal checking interval - * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link - * #DEFAULT_THRESHOLD}).

        * - *

        This method uses the {@link DataContext#getDefault() default data context}. + *

        This constructor uses: + *

          + *
        • the {@link DataContext#getDefault() default data context}
        • + *
        • the {@link AbstractDetector#DEFAULT_MAXCHECK default value} for maximal checking interval
        • + *
        • the {@link AbstractDetector#DEFAULT_THRESHOLD default value} for convergence threshold
        • + *
        * - * @param limit the threshold value of magnetic field at see level, in nano Teslas - * @param type the magnetic field model - * @param body the body - * @param seaLevel true if the magnetic field intensity is computed at the sea level, false if it is computed at satellite altitude - * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} + * @param limit threshold value for magnetic field detection, in Teslas + * @param model magnetic field model + * @param body Earth body shape + * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext) */ @DefaultDataContext - public MagneticFieldDetector(final double limit, final FieldModel type, final OneAxisEllipsoid body, final boolean seaLevel) - throws OrekitIllegalArgumentException { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, type, body, seaLevel); + public MagneticFieldDetector(final double limit, final FieldModel model, + final OneAxisEllipsoid body, final boolean atSeaLevel) { + this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, model, body, atSeaLevel); } /** Build a detector. * - *

        This method uses the {@link DataContext#getDefault() default data context}. + *

        This method uses the {@link DataContext#getDefault() default data context}.

        * - * @param maxCheck maximal checking interval (s) - * @param threshold convergence threshold (s) - * @param limit the threshold value of magnetic field at see level, in nano Teslas - * @param type the magnetic field model - * @param body the body - * @param seaLevel true if the magnetic field intensity is computed at the sea level, false if it is computed at satellite altitude - * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param limit threshold value for magnetic field detection, in Teslas + * @param model magnetic field model + * @param body Earth body shape + * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext) */ @DefaultDataContext public MagneticFieldDetector(final double maxCheck, final double threshold, final double limit, - final FieldModel type, final OneAxisEllipsoid body, final boolean seaLevel) - throws OrekitIllegalArgumentException { - this(maxCheck, threshold, limit, type, body, seaLevel, - DataContext.getDefault()); + final FieldModel model, final OneAxisEllipsoid body, final boolean atSeaLevel) { + this(maxCheck, threshold, limit, model, body, atSeaLevel, DataContext.getDefault()); } /** @@ -126,65 +129,58 @@ public MagneticFieldDetector(final double maxCheck, final double threshold, fina * * @param maxCheck maximal checking interval (s) * @param threshold convergence threshold (s) - * @param limit the threshold value of magnetic field at see level, in nano Teslas - * @param type the magnetic field model - * @param body the body - * @param seaLevel true if the magnetic field intensity is computed at the sea - * level, false if it is computed at satellite altitude + * @param limit threshold value for magnetic field detection, in Teslas + * @param model magnetic field model + * @param body Earth body shape + * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) * @param dataContext used to look up the magnetic field model. - * @throws OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} * @since 10.1 */ public MagneticFieldDetector(final double maxCheck, final double threshold, final double limit, - final FieldModel type, + final FieldModel model, final OneAxisEllipsoid body, - final boolean seaLevel, - final DataContext dataContext) - throws OrekitIllegalArgumentException { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing<>(), - limit, type, body, seaLevel, dataContext); + final boolean atSeaLevel, + final DataContext dataContext) { + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + limit, model, body, atSeaLevel, dataContext); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) - * @param threshold convergence threshold (s) - * @param maxIter maximum number of iterations in the event time search - * @param handler event handler to call at event occurrences - * @param limit the threshold value of magnetic field at see level, in nano Teslas - * @param type the magnetic field model - * @param body the body - * @param seaLevel true if the magnetic field intensity is computed at the sea level, false if it is computed at satellite altitude + * @param maxCheck maximal checking interval + * @param threshold convergence threshold (s) + * @param maxIter maximum number of iterations in the event time search + * @param handler event handler to call at event occurrences + * @param limit threshold value for magnetic field detection, in Teslas + * @param model magnetic field model + * @param body Earth body shape + * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) * @param dataContext used to look up the magnetic field model. - * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} */ - private MagneticFieldDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final double limit, final FieldModel type, final OneAxisEllipsoid body, final boolean seaLevel, - final DataContext dataContext) - throws OrekitIllegalArgumentException { - + protected MagneticFieldDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final double limit, final FieldModel model, final OneAxisEllipsoid body, + final boolean atSeaLevel, final DataContext dataContext) { super(maxCheck, threshold, maxIter, handler); - - this.limit = limit; - this.type = type; - this.body = body; - this.seaLevel = seaLevel; + this.limit = limit; + this.model = model; + this.body = body; + this.atSeaLevel = atSeaLevel; this.dataContext = dataContext; } /** {@inheritDoc} */ @Override - protected MagneticFieldDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected MagneticFieldDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new MagneticFieldDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, - limit, type, body, seaLevel, dataContext); + limit, model, body, atSeaLevel, dataContext); } /** {@inheritDoc} */ @@ -192,37 +188,26 @@ public void init(final SpacecraftState s0, final AbsoluteDate t) { super.init(s0, t); final TimeScale utc = dataContext.getTimeScales().getUTC(); this.currentYear = s0.getDate().getComponents(utc).getDate().getYear(); - this.field = dataContext.getGeoMagneticFields().getField(type, currentYear); + this.field = dataContext.getGeoMagneticFields().getField(model, currentYear); } /** Compute the value of the detection function. *

        - * The value is the angle difference between the spacecraft and the fixed - * angle to be crossed, with some sign tweaks to ensure continuity. - * These tweaks imply the {@code increasing} flag in events detection becomes - * irrelevant here! As an example, the angle always increase in a Keplerian - * orbit, but this g function will increase and decrease so it - * will cross the zero value once per orbit, in increasing and decreasing - * directions on alternate orbits.. + * The returned value is the difference between the field intensity at spacecraft location, + * taking atSeaLevel switch into account, and the fixed threshold value. *

        * @param s the current state information: date, kinematics, attitude - * @return angle difference between the spacecraft and the fixed - * angle, with some sign tweaks to ensure continuity + * @return difference between the field intensity at spacecraft location + * and the fixed threshold value */ public double g(final SpacecraftState s) { final TimeScale utc = dataContext.getTimeScales().getUTC(); if (s.getDate().getComponents(utc).getDate().getYear() != currentYear) { this.currentYear = s.getDate().getComponents(utc).getDate().getYear(); - this.field = dataContext.getGeoMagneticFields().getField(type, currentYear); - } - final GeodeticPoint geoPoint = body.transform(s.getPVCoordinates().getPosition(), s.getFrame(), s.getDate()); - final double altitude; - if (seaLevel) { - altitude = 0; - } - else { - altitude = geoPoint.getAltitude(); + this.field = dataContext.getGeoMagneticFields().getField(model, currentYear); } + final GeodeticPoint geoPoint = body.transform(s.getPosition(), s.getFrame(), s.getDate()); + final double altitude = atSeaLevel ? 0. : geoPoint.getAltitude(); final double value = field.calculateField(geoPoint.getLatitude(), geoPoint.getLongitude(), altitude).getTotalIntensity(); return value - limit; } diff --git a/src/main/java/org/orekit/propagation/events/NegateDetector.java b/src/main/java/org/orekit/propagation/events/NegateDetector.java index 9e6c71cf3a..f1793143cc 100644 --- a/src/main/java/org/orekit/propagation/events/NegateDetector.java +++ b/src/main/java/org/orekit/propagation/events/NegateDetector.java @@ -46,24 +46,24 @@ public NegateDetector(final EventDetector original) { this(original.getMaxCheckInterval(), original.getThreshold(), original.getMaxIterationCount(), - new ContinueOnEvent<>(), + new ContinueOnEvent(), original); } /** * Private constructor. * - * @param newMaxCheck max check interval in seconds. + * @param newMaxCheck max check interval. * @param newThreshold convergence threshold in seconds. * @param newMaxIter max iterations. * @param newHandler event handler. * @param original event detector. */ - private NegateDetector(final double newMaxCheck, - final double newThreshold, - final int newMaxIter, - final EventHandler newHandler, - final EventDetector original) { + protected NegateDetector(final AdaptableInterval newMaxCheck, + final double newThreshold, + final int newMaxIter, + final EventHandler newHandler, + final EventDetector original) { super(newMaxCheck, newThreshold, newMaxIter, newHandler); this.original = original; } @@ -91,10 +91,10 @@ public double g(final SpacecraftState s) { @Override protected NegateDetector create( - final double newMaxCheck, + final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + final EventHandler newHandler) { return new NegateDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, this.original); } diff --git a/src/main/java/org/orekit/propagation/events/NodeDetector.java b/src/main/java/org/orekit/propagation/events/NodeDetector.java index dd94908761..dcb33116e7 100644 --- a/src/main/java/org/orekit/propagation/events/NodeDetector.java +++ b/src/main/java/org/orekit/propagation/events/NodeDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,7 +23,7 @@ import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnIncreasing; @@ -67,8 +67,8 @@ public class NodeDetector extends AbstractDetector { * @since 10.3 */ public NodeDetector(final Frame frame) { - this(DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, - new StopOnIncreasing(), frame); + this(s -> DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + new StopOnIncreasing(), frame); } /** Build a new instance. @@ -94,18 +94,18 @@ public NodeDetector(final Orbit orbit, final Frame frame) { * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) */ public NodeDetector(final double threshold, final Orbit orbit, final Frame frame) { - this(2 * estimateNodesTimeSeparation(orbit) / 3, threshold, - DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(s -> 2 * estimateNodesTimeSeparation(orbit) / 3, threshold, + DEFAULT_MAX_ITER, new StopOnIncreasing(), frame); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -114,17 +114,17 @@ public NodeDetector(final double threshold, final Orbit orbit, final Frame frame * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) * @since 6.1 */ - private NodeDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final Frame frame) { + protected NodeDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final Frame frame) { super(maxCheck, threshold, maxIter, handler); this.frame = frame; } /** {@inheritDoc} */ @Override - protected NodeDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, final EventHandler newHandler) { + protected NodeDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { return new NodeDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, frame); } @@ -146,7 +146,7 @@ private static double estimateNodesTimeSeparation(final Orbit orbit) { keplerian.getI(), keplerian.getPerigeeArgument(), keplerian.getRightAscensionOfAscendingNode(), - -keplerian.getPerigeeArgument(), PositionAngle.TRUE, + -keplerian.getPerigeeArgument(), PositionAngleType.TRUE, keplerian.getFrame(), keplerian.getDate(), keplerian.getMu()).getMeanAnomaly(); @@ -155,7 +155,7 @@ private static double estimateNodesTimeSeparation(final Orbit orbit) { keplerian.getI(), keplerian.getPerigeeArgument(), keplerian.getRightAscensionOfAscendingNode(), - FastMath.PI - keplerian.getPerigeeArgument(), PositionAngle.TRUE, + FastMath.PI - keplerian.getPerigeeArgument(), PositionAngleType.TRUE, keplerian.getFrame(), keplerian.getDate(), keplerian.getMu()).getMeanAnomaly(); @@ -181,7 +181,7 @@ public Frame getFrame() { * @return value of the switching function */ public double g(final SpacecraftState s) { - return s.getPVCoordinates(frame).getPosition().getZ(); + return s.getPosition(frame).getZ(); } } diff --git a/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java b/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java index f168fac492..105aa0c78e 100644 --- a/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java +++ b/src/main/java/org/orekit/propagation/events/ParameterDrivenDateIntervalDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,8 @@ import org.orekit.utils.DateDriver; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** Detector for date intervals that may be offset thanks to parameter drivers. *

        @@ -39,7 +41,10 @@ * be propagated to the other pair, but attempting to select drivers in both * pairs at the same time will trigger an exception. Changing the value of a driver * that is not selected should be avoided as it leads to inconsistencies between the pairs. - *

        + *

        . Warning, startDate driver, stopDate driver, duration driver and medianDate driver + * must all have the same number of values to estimate (same number of span in valueSpanMap), that is is to + * say that the {@link org.orekit.utils.ParameterDriver#addSpans(AbsoluteDate, AbsoluteDate, double)} + * should be called with same arguments. * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) * @author Luc Maisonobe * @since 11.1 @@ -92,22 +97,22 @@ public ParameterDrivenDateIntervalDetector(final String prefix, */ public ParameterDrivenDateIntervalDetector(final String prefix, final AbsoluteDate refStart, final AbsoluteDate refStop) { - this(FastMath.max(0.5 * refStop.durationFrom(refStart), THRESHOLD), + this(s -> FastMath.max(0.5 * refStop.durationFrom(refStart), THRESHOLD), THRESHOLD, DEFAULT_MAX_ITER, - new StopOnDecreasing(), + new StopOnDecreasing(), new DateDriver(refStart, prefix + START_SUFFIX, true), new DateDriver(refStop, prefix + STOP_SUFFIX, false), new DateDriver(refStart.shiftedBy(0.5 * refStop.durationFrom(refStart)), prefix + MEDIAN_SUFFIX, true), new ParameterDriver(prefix + DURATION_SUFFIX, refStop.durationFrom(refStart), 1.0, 0.0, Double.POSITIVE_INFINITY)); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences @@ -116,10 +121,10 @@ public ParameterDrivenDateIntervalDetector(final String prefix, * @param median median date driver * @param duration duration driver */ - private ParameterDrivenDateIntervalDetector(final double maxCheck, final double threshold, final int maxIter, - final EventHandler handler, - final DateDriver start, final DateDriver stop, - final DateDriver median, final ParameterDriver duration) { + protected ParameterDrivenDateIntervalDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler, + final DateDriver start, final DateDriver stop, + final DateDriver median, final ParameterDriver duration) { super(maxCheck, threshold, maxIter, handler); this.start = start; this.stop = stop; @@ -154,8 +159,8 @@ private void replaceBindingObserver(final ParameterDriver driver, final BindingO /** {@inheritDoc} */ @Override - protected ParameterDrivenDateIntervalDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter, - final EventHandler newHandler) { + protected ParameterDrivenDateIntervalDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, final int newMaxIter, + final EventHandler newHandler) { return new ParameterDrivenDateIntervalDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, start, stop, median, duration); } @@ -230,12 +235,20 @@ private abstract class BindingObserver implements ParameterObserver { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { if (driver.isSelected()) { - setDelta(driver.getValue() - previousValue); + setDelta(driver.getValue(date) - previousValue, date); + } + } + /** {@inheritDoc} */ + @Override + public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { + if (driver.isSelected()) { + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + setDelta(span.getData() - previousValue.get(span.getStart()), span.getStart()); + } } } - /** {@inheritDoc} */ @Override public void selectionChanged(final boolean previousSelection, final ParameterDriver driver) { @@ -249,8 +262,9 @@ public void selectionChanged(final boolean previousSelection, final ParameterDri /** Change a value. * @param delta change of value + * @param date date at which the delta wants to be set */ - protected abstract void setDelta(double delta); + protected abstract void setDelta(double delta, AbsoluteDate date); } @@ -258,9 +272,13 @@ public void selectionChanged(final boolean previousSelection, final ParameterDri private class StartObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - median.setValue(median.getValue() + 0.5 * delta); - duration.setValue(duration.getValue() - delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + // date driver has no validity period, only 1 value is estimated + // over the all interval so there is no problem for calling getValue with null argument + // or any date, it would give the same result as there is only 1 span on the valueSpanMap + // of the driver + median.setValue(median.getValue(date) + 0.5 * delta, date); + duration.setValue(duration.getValue(date) - delta, date); } } @@ -268,9 +286,13 @@ protected void setDelta(final double delta) { private class StopObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - median.setValue(median.getValue() + 0.5 * delta); - duration.setValue(duration.getValue() + delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + // date driver has no validity period, only 1 value is estimated + // over the all interval so there is no problem for calling getValue with null argument + // or any date, it would give the same result as there is only 1 span on the valueSpanMap + // of the driver + median.setValue(median.getValue(date) + 0.5 * delta, date); + duration.setValue(duration.getValue(date) + delta, date); } } @@ -278,9 +300,13 @@ protected void setDelta(final double delta) { private class MedianObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - start.setValue(start.getValue() + delta); - stop.setValue(stop.getValue() + delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + // date driver has no validity period, only 1 value is estimated + // over the all interval so there is no problem for calling getValue with null argument + // or any date, it would give the same result as there is only 1 span on the valueSpanMap + // of the driver + start.setValue(start.getValue(date) + delta, date); + stop.setValue(stop.getValue(date) + delta, date); } } @@ -288,9 +314,13 @@ protected void setDelta(final double delta) { private class DurationObserver extends BindingObserver { /** {@inheritDoc} */ @Override - protected void setDelta(final double delta) { - start.setValue(start.getValue() - 0.5 * delta); - stop.setValue(stop.getValue() + 0.5 * delta); + protected void setDelta(final double delta, final AbsoluteDate date) { + // date driver has no validity period, only 1 value is estimated + // over the all interval so there is no problem for calling getValue with null argument + // or any date, it would give the same result as there is only 1 span on the valueSpanMap + // of the driver + start.setValue(start.getValue(date) - 0.5 * delta, date); + stop.setValue(stop.getValue(date) + 0.5 * delta, date); } } diff --git a/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java b/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java index 3f6eec41df..4f2a75a6c7 100644 --- a/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java +++ b/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,10 +29,10 @@ import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.handlers.EventHandler; -import org.orekit.propagation.events.handlers.StopOnIncreasing; +import org.orekit.propagation.events.handlers.StopOnEvent; import org.orekit.time.AbsoluteDate; import org.orekit.utils.TimeSpanMap; @@ -42,8 +42,8 @@ * orbits, latitude argument for {@link OrbitType#CIRCULAR circular} orbits, * or longitude argument for {@link OrbitType#EQUINOCTIAL equinoctial} orbits. * It does not support {@link OrbitType#CARTESIAN Cartesian} orbits. The - * angles can be either {@link PositionAngle#TRUE true}, {link {@link PositionAngle#MEAN - * mean} or {@link PositionAngle#ECCENTRIC eccentric} angles. + * angles can be either {@link PositionAngleType#TRUE true}, {link {@link PositionAngleType#MEAN + * mean} or {@link PositionAngleType#ECCENTRIC eccentric} angles. *

        * @author Luc Maisonobe * @since 7.1 @@ -54,7 +54,7 @@ public class PositionAngleDetector extends AbstractDetector * @param orbitType orbit type defining the angle type - * @param positionAngle type of position angle + * @param positionAngleType type of position angle * @param angle fixed angle to be crossed * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} */ - public PositionAngleDetector(final OrbitType orbitType, final PositionAngle positionAngle, + public PositionAngleDetector(final OrbitType orbitType, final PositionAngleType positionAngleType, final double angle) throws OrekitIllegalArgumentException { - this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, orbitType, positionAngle, angle); + this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, orbitType, positionAngleType, angle); } /** Build a detector. + *

        This instance uses by default the {@link StopOnEvent} handler

        * @param maxCheck maximal checking interval (s) * @param threshold convergence threshold (s) * @param orbitType orbit type defining the angle type - * @param positionAngle type of position angle + * @param positionAngleType type of position angle * @param angle fixed angle to be crossed * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} */ public PositionAngleDetector(final double maxCheck, final double threshold, - final OrbitType orbitType, final PositionAngle positionAngle, + final OrbitType orbitType, final PositionAngleType positionAngleType, final double angle) throws OrekitIllegalArgumentException { - this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), - orbitType, positionAngle, angle); + this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnEvent(), + orbitType, positionAngleType, angle); } - /** Private constructor with full parameters. + /** Protected constructor with full parameters. *

        - * This constructor is private as users are expected to use the builder + * This constructor is not public as users are expected to use the builder * API with the various {@code withXxx()} methods to set up the instance * in a readable manner without using a huge amount of parameters. *

        - * @param maxCheck maximum checking interval (s) + * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) * @param maxIter maximum number of iterations in the event time search * @param handler event handler to call at event occurrences * @param orbitType orbit type defining the angle type - * @param positionAngle type of position angle + * @param positionAngleType type of position angle * @param angle fixed angle to be crossed * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN} */ - private PositionAngleDetector(final double maxCheck, final double threshold, - final int maxIter, final EventHandler handler, - final OrbitType orbitType, final PositionAngle positionAngle, - final double angle) + protected PositionAngleDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final OrbitType orbitType, final PositionAngleType positionAngleType, + final double angle) throws OrekitIllegalArgumentException { super(maxCheck, threshold, maxIter, handler); this.orbitType = orbitType; - this.positionAngle = positionAngle; + this.positionAngleType = positionAngleType; this.angle = angle; this.offsetEstimators = null; switch (orbitType) { case KEPLERIAN: - positionAngleExtractor = o -> ((KeplerianOrbit) orbitType.convertType(o)).getAnomaly(positionAngle); + positionAngleExtractor = o -> ((KeplerianOrbit) orbitType.convertType(o)).getAnomaly(positionAngleType); break; case CIRCULAR: - positionAngleExtractor = o -> ((CircularOrbit) orbitType.convertType(o)).getAlpha(positionAngle); + positionAngleExtractor = o -> ((CircularOrbit) orbitType.convertType(o)).getAlpha(positionAngleType); break; case EQUINOCTIAL: - positionAngleExtractor = o -> ((EquinoctialOrbit) orbitType.convertType(o)).getL(positionAngle); + positionAngleExtractor = o -> ((EquinoctialOrbit) orbitType.convertType(o)).getL(positionAngleType); break; default: final String sep = ", "; @@ -147,11 +148,11 @@ private PositionAngleDetector(final double maxCheck, final double threshold, /** {@inheritDoc} */ @Override - protected PositionAngleDetector create(final double newMaxCheck, final double newThreshold, - final int newMaxIter, - final EventHandler newHandler) { + protected PositionAngleDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { return new PositionAngleDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, - orbitType, positionAngle, angle); + orbitType, positionAngleType, angle); } /** Get the orbit type defining the angle type. @@ -164,8 +165,8 @@ public OrbitType getOrbitType() { /** Get the type of position angle. * @return type of position angle */ - public PositionAngle getPositionAngle() { - return positionAngle; + public PositionAngleType getPositionAngleType() { + return positionAngleType; } /** Get the fixed angle to be crossed (radians). diff --git a/src/main/java/org/orekit/propagation/events/Transformer.java b/src/main/java/org/orekit/propagation/events/Transformer.java index de712e8cbe..ba126e0d90 100644 --- a/src/main/java/org/orekit/propagation/events/Transformer.java +++ b/src/main/java/org/orekit/propagation/events/Transformer.java @@ -17,6 +17,7 @@ package org.orekit.propagation.events; +import org.hipparchus.CalculusFieldElement; import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; @@ -43,6 +44,10 @@ enum Transformer { protected double transformed(final double g) { return 0; } + /** {@inheritDoc} */ + protected > T transformed(final T g) { + return g.getField().getZero(); + } }, /** Transformer computing transformed = g. @@ -56,6 +61,10 @@ protected double transformed(final double g) { protected double transformed(final double g) { return g; } + /** {@inheritDoc} */ + protected > T transformed(final T g) { + return g; + } }, /** Transformer computing transformed = -g. @@ -69,6 +78,10 @@ protected double transformed(final double g) { protected double transformed(final double g) { return -g; } + /** {@inheritDoc} */ + protected > T transformed(final T g) { + return g.negate(); + } }, /** Transformer computing transformed = min(-{@link Precision#SAFE_MIN}, -g, +g). @@ -82,6 +95,11 @@ protected double transformed(final double g) { protected double transformed(final double g) { return FastMath.min(-Precision.SAFE_MIN, FastMath.min(-g, +g)); } + /** {@inheritDoc} */ + protected > T transformed(final T g) { + final T zero = g.getField().getZero(); + return FastMath.min(zero.newInstance(-Precision.SAFE_MIN), FastMath.min(g.negate(), g)); + } }, /** Transformer computing transformed = max(+{@link Precision#SAFE_MIN}, -g, +g). @@ -95,6 +113,11 @@ protected double transformed(final double g) { protected double transformed(final double g) { return FastMath.max(+Precision.SAFE_MIN, FastMath.max(-g, +g)); } + /** {@inheritDoc} */ + protected > T transformed(final T g) { + final T zero = g.getField().getZero(); + return FastMath.max(zero.newInstance(+Precision.SAFE_MIN), FastMath.max(g.negate(), g)); + } }; /** Transform value of function g. @@ -103,4 +126,12 @@ protected double transformed(final double g) { */ protected abstract double transformed(double g); + /** Transform value of function g. + * @param type of the field elements + * @param g raw value of function g + * @return transformed value of function g + * @since 12.0 + */ + protected abstract > T transformed(T g); + } diff --git a/src/main/java/org/orekit/propagation/events/VisibilityTrigger.java b/src/main/java/org/orekit/propagation/events/VisibilityTrigger.java index 54d6dbab48..4c3dc6de63 100644 --- a/src/main/java/org/orekit/propagation/events/VisibilityTrigger.java +++ b/src/main/java/org/orekit/propagation/events/VisibilityTrigger.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/events/handlers/ContinueOnEvent.java b/src/main/java/org/orekit/propagation/events/handlers/ContinueOnEvent.java index 24c309c28b..126a03ba97 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/ContinueOnEvent.java +++ b/src/main/java/org/orekit/propagation/events/handlers/ContinueOnEvent.java @@ -25,10 +25,20 @@ * Event handler which will always return {@link Action#CONTINUE continue} as a state. * @author Hank Grabowski * - * @param class type for the generic version * @since 6.1 */ -public class ContinueOnEvent implements EventHandler { +public class ContinueOnEvent implements EventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public ContinueOnEvent() { + // nothing to do + } /** * Specific implementation of the eventOccurred interface. @@ -39,7 +49,7 @@ public class ContinueOnEvent implements EventHandler * @return {@link Action#CONTINUE continue} under all circumstances */ @Override - public Action eventOccurred(final SpacecraftState s, final T detector, final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { return Action.CONTINUE; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java b/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java index ed1ad4b664..083bf23aab 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java +++ b/src/main/java/org/orekit/propagation/events/handlers/EventHandler.java @@ -22,19 +22,13 @@ import org.orekit.time.AbsoluteDate; -/** - * An interface defining how to override event handling behavior in the standard - * propagator eventing classes without requiring subclassing. In cases where - * one wishes to use anonymous classes rather than explicit subclassing this - * allows for a more direct way to override the behavior. Event classes have to - * specifically support this capability. +/** An interface defining how to handle events occurring during propagation. * * @author Hank Grabowski * - * @param object type that the handler is called from * @since 6.1 */ -public interface EventHandler { +public interface EventHandler { /** Initialize event handler at the start of a propagation. *

        @@ -50,16 +44,11 @@ public interface EventHandler { * @param detector event detector related to the event handler * */ - default void init(SpacecraftState initialState, AbsoluteDate target, final T detector) { + default void init(SpacecraftState initialState, AbsoluteDate target, final EventDetector detector) { // nothing by default } - /** - * eventOccurred method mirrors the same interface method as in {@link EventDetector} - * and its subclasses, but with an additional parameter that allows the calling - * method to pass in an object from the detector which would have potential - * additional data to allow the implementing class to determine the correct - * return state. + /** Handle an event. * * @param s SpaceCraft state to be used in the evaluation * @param detector object with appropriate type that can be used in determining correct return state @@ -67,7 +56,7 @@ default void init(SpacecraftState initialState, AbsoluteDate target, final T det * @return the Action that the calling detector should pass back to the evaluation system * */ - Action eventOccurred(SpacecraftState s, T detector, boolean increasing); + Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing); /** Reset the state prior to continue propagation. *

        This method is called after the step handler has returned and @@ -84,7 +73,7 @@ default void init(SpacecraftState initialState, AbsoluteDate target, final T det * @param oldState old state * @return new state */ - default SpacecraftState resetState(T detector, SpacecraftState oldState) { + default SpacecraftState resetState(EventDetector detector, SpacecraftState oldState) { return oldState; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java b/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java index fc28ff071e..fb53ad9d35 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java +++ b/src/main/java/org/orekit/propagation/events/handlers/EventMultipleHandler.java @@ -38,16 +38,15 @@ * * @author Lara Hué * - * @param object type of the detector which is linked to the handler * @since 10.3 */ -public class EventMultipleHandler implements EventHandler { +public class EventMultipleHandler implements EventHandler { /** Default list of handlers for event overrides. */ - private List> handlers; + private List handlers; /** List of handlers whose Action returned is RESET_STATE. */ - private List> resetStateHandlers; + private List resetStateHandlers; /** Constructor with list initialisation. */ public EventMultipleHandler() { @@ -73,16 +72,11 @@ public EventMultipleHandler() { * @param detector event detector related to the event handler */ @Override - public void init(final SpacecraftState initialState, final AbsoluteDate target, final D detector) { + public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) { handlers.forEach(handler -> handler.init(initialState, target, detector)); } - /** - * eventOccurred method mirrors the same interface method as in {@link EventDetector} - * and its subclasses, but with an additional parameter that allows the calling - * method to pass in an object from the detector which would have potential - * additional data to allow the implementing class to determine the correct - * return state. + /** Handle an event. * * The MultipleEventHandler class implies a different behaviour on event detections * than with other handlers : @@ -100,8 +94,8 @@ public void init(final SpacecraftState initialState, final AbsoluteDate target, * */ @Override - public Action eventOccurred(final SpacecraftState s, final D detector, final boolean increasing) { - final Map, Action> actions = + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { + final Map actions = handlers.stream(). collect(Collectors.toMap(Function.identity(), handler -> handler.eventOccurred(s, detector, increasing))); @@ -141,9 +135,9 @@ public Action eventOccurred(final SpacecraftState s, final D detector, final boo * @return new state */ @Override - public SpacecraftState resetState(final D detector, final SpacecraftState oldState) { + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { SpacecraftState newState = oldState; - for (EventHandler handler : resetStateHandlers) { + for (EventHandler handler : resetStateHandlers) { newState = handler.resetState(detector, newState); } return newState; @@ -153,7 +147,7 @@ public SpacecraftState resetState(final D detector, final SpacecraftState oldSta * @param handler handler associated with D detector * @return this object */ - public EventMultipleHandler addHandler(final EventHandler handler) { + public EventMultipleHandler addHandler(final EventHandler handler) { handlers.add(handler); return this; } @@ -163,7 +157,7 @@ public EventMultipleHandler addHandler(final EventHandler handler) { * @return this object */ @SafeVarargs // this method is safe - public final EventMultipleHandler addHandlers(final EventHandler... newHandlers) { + public final EventMultipleHandler addHandlers(final EventHandler... newHandlers) { Arrays.stream(newHandlers).forEach(this::addHandler); return this; } @@ -172,14 +166,14 @@ public final EventMultipleHandler addHandlers(final EventHandler... newHan * @param newHandlers new handlers list associated with D detector * */ - public void setHandlers(final List> newHandlers) { + public void setHandlers(final List newHandlers) { handlers = newHandlers; } /** Retrieve managed handlers list. * @return list of handlers for event overrides */ - public List> getHandlers() { + public List getHandlers() { return this.handlers; } } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldContinueOnEvent.java b/src/main/java/org/orekit/propagation/events/handlers/FieldContinueOnEvent.java index c14c230f76..7f238f5e1f 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldContinueOnEvent.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldContinueOnEvent.java @@ -27,10 +27,21 @@ * Event handler which will always return {@link Action#CONTINUE continue} as a state. * @author Hank Grabowski * - * @param class type for the generic version + * @param type of the field element */ -public class FieldContinueOnEvent , T extends CalculusFieldElement> - implements FieldEventHandler { +public class FieldContinueOnEvent > implements FieldEventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public FieldContinueOnEvent() { + // nothing to do + } + /** * Specific implementation of the eventOccurred interface. * @@ -40,7 +51,7 @@ public class FieldContinueOnEvent , T extends C * @return {@link Action#CONTINUE continue} under all circumstances */ @Override - public Action eventOccurred(final FieldSpacecraftState s, final KK detector, final boolean increasing) { + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { return Action.CONTINUE; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java b/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java index c7aafe8699..1a47857d6b 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldEventHandler.java @@ -19,42 +19,18 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.ode.events.Action; import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.time.FieldAbsoluteDate; -/** - * An interface defining how to override event handling behavior in the standard - * propagator eventing classes without requiring subclassing. In cases where - * one wishes to use anonymous classes rather than explicit subclassing this - * allows for a more direct way to override the behavior. Event classes have to - * specifically support this capability. +/** An interface defining how to handle events occurring during propagation.. * * @author Hank Grabowski * - * @param object type that the handler is called from + * @param type of the field element * @since 6.1 */ -public interface FieldEventHandler, T extends CalculusFieldElement> { - - /** Initialize event handler at the start of a propagation. - *

        - * This method is called once at the start of the propagation. It - * may be used by the event handler to initialize some internal data - * if needed. - *

        - *

        - * The default implementation does nothing - *

        - * @param initialState initial state - * @param target target date for the propagation - * @deprecated as of 11.1, replaced by {@link #init(FieldSpacecraftState, FieldAbsoluteDate, FieldEventDetector)} - */ - default void init(final FieldSpacecraftState initialState, - final FieldAbsoluteDate target) { - // nothing by default - } +public interface FieldEventHandler> { /** Initialize event handler at the start of a propagation. *

        @@ -70,20 +46,11 @@ default void init(final FieldSpacecraftState initialState, * @param detector event detector related to the event handler * @since 11.1 */ - default void init(FieldSpacecraftState initialState, - FieldAbsoluteDate target, - final KK detector) { - // TODO remove the default implementation in 12.0 - // when init(initialState, target) is removed - init(initialState, target); + default void init(FieldSpacecraftState initialState, FieldAbsoluteDate target, FieldEventDetector detector) { + // nothing by default } - /** - * eventOccurred method mirrors the same interface method as in {@link EventDetector} - * and its subclasses, but with an additional parameter that allows the calling - * method to pass in an object from the detector which would have potential - * additional data to allow the implementing class to determine the correct - * return state. + /** Handle an event. * * @param s SpaceCraft state to be used in the evaluation * @param detector object with appropriate type that can be used in determining correct return state @@ -91,7 +58,7 @@ default void init(FieldSpacecraftState initialState, * @return the Action that the calling detector should pass back to the evaluation system * */ - Action eventOccurred(FieldSpacecraftState s, KK detector, boolean increasing); + Action eventOccurred(FieldSpacecraftState s, FieldEventDetector detector, boolean increasing); /** Reset the state prior to continue propagation. *

        This method is called after the step handler has returned and @@ -108,7 +75,7 @@ default void init(FieldSpacecraftState initialState, * @param oldState old state * @return new state */ - default FieldSpacecraftState resetState(KK detector, FieldSpacecraftState oldState) { + default FieldSpacecraftState resetState(FieldEventDetector detector, FieldSpacecraftState oldState) { return oldState; } } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldRecordAndContinue.java b/src/main/java/org/orekit/propagation/events/handlers/FieldRecordAndContinue.java index 9908320f42..c1d493a525 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldRecordAndContinue.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldRecordAndContinue.java @@ -32,25 +32,24 @@ * *

        As this handler stores all observed events it may consume large amounts * of memory depending on the duration of propagation and the frequency of events. + *

        * - * @param the type of {@link EventDetector} that this event handler will handle events - * for. - * @param the type of {@link CalculusFieldElement} to use instead of {@code double}. * @author Evan Ward * @see RecordAndContinue * @since 9.3 + * @param type of the field element */ -public class FieldRecordAndContinue - , E extends CalculusFieldElement> - implements FieldEventHandler { +public class FieldRecordAndContinue > implements FieldEventHandler { - /** A single event detected during propagation. */ - public static class Event> { + /** A single event detected during propagation. + * @param type of the field element + */ + public static class Event> { /** The observed state. */ - private final FieldSpacecraftState state; + private final FieldSpacecraftState state; /** The detector. */ - private final T detector; + private final FieldEventDetector detector; /** The sign of the derivative of the g function. */ private final boolean increasing; @@ -61,8 +60,8 @@ public static class Event> { * @param state of the event. * @param increasing if the g function is increasing. */ - private Event(final T detector, - final FieldSpacecraftState state, + private Event(final FieldEventDetector detector, + final FieldSpacecraftState state, final boolean increasing) { this.detector = detector; this.state = state; @@ -75,7 +74,7 @@ private Event(final T detector, * @return the detector that found the event. * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) */ - public T getDetector() { + public FieldEventDetector getDetector() { return detector; } @@ -96,7 +95,7 @@ public boolean isIncreasing() { * @return the satellite's state when the event was triggered. * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) */ - public FieldSpacecraftState getState() { + public FieldSpacecraftState getState() { return state; } @@ -111,7 +110,7 @@ public String toString() { } /** Observed events. */ - private final List> events; + private final List> events; /** Create a new handler using an {@link ArrayList} to store events. */ public FieldRecordAndContinue() { @@ -123,7 +122,7 @@ public FieldRecordAndContinue() { * * @param events collection. */ - public FieldRecordAndContinue(final List> events) { + public FieldRecordAndContinue(final List> events) { this.events = events; } @@ -141,7 +140,7 @@ public FieldRecordAndContinue(final List> events) { * * @return the events observed by the handler in the order they were observed. */ - public List> getEvents() { + public List> getEvents() { return Collections.unmodifiableList(this.events); } @@ -151,8 +150,8 @@ public void clear() { } @Override - public Action eventOccurred(final FieldSpacecraftState s, - final T detector, + public Action eventOccurred(final FieldSpacecraftState s, + final FieldEventDetector detector, final boolean increasing) { events.add(new Event<>(detector, s, increasing)); return Action.CONTINUE; diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnDecreasing.java b/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnDecreasing.java index 182434f189..36f49ec678 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnDecreasing.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnDecreasing.java @@ -29,10 +29,20 @@ * * @author Hank Grabowski * - * @param class type for the generic version + * @param type of the field element */ -public class FieldStopOnDecreasing , T extends CalculusFieldElement> implements FieldEventHandler { +public class FieldStopOnDecreasing > implements FieldEventHandler { + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public FieldStopOnDecreasing() { + // nothing to do + } /** Handle a detection event and choose what to do next. *

        KKhe implementation behavior is to {@link @@ -45,7 +55,7 @@ public class FieldStopOnDecreasing , T extends * @return {@link Action#STOP} or {@link Action#CONTINUE} */ @Override - public Action eventOccurred(final FieldSpacecraftState s, final KK detector, final boolean increasing) { + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { return increasing ? Action.CONTINUE : Action.STOP; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnEvent.java b/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnEvent.java index e90aa1df30..4d0872ac7d 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnEvent.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnEvent.java @@ -28,10 +28,20 @@ * Event handler which will always return {@link Action#CONTINUE continue} as a state. * @author Hank Grabowski * - * @param class type for the generic version + * @param type of the field element */ -public class FieldStopOnEvent , T extends CalculusFieldElement> - implements FieldEventHandler { +public class FieldStopOnEvent > implements FieldEventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public FieldStopOnEvent() { + // nothing to do + } /** * Specific implementation of the eventOccurred interface. @@ -42,7 +52,7 @@ public class FieldStopOnEvent , T extends Calcu * @return {@link Action#STOP stop} under all circumstances */ @Override - public Action eventOccurred(final FieldSpacecraftState s, final KK detector, final boolean increasing) { + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { return Action.STOP; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnIncreasing.java b/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnIncreasing.java index f866e6a0f1..764d621047 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnIncreasing.java +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldStopOnIncreasing.java @@ -29,10 +29,20 @@ * * @author Hank Grabowski * - * @param class type for the generic version + * @param type of the field element */ -public class FieldStopOnIncreasing , - T extends CalculusFieldElement> implements FieldEventHandler { +public class FieldStopOnIncreasing > implements FieldEventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public FieldStopOnIncreasing() { + // nothing to do + } /** Handle a detection event and choose what to do next. *

        The implementation behavior is to {@link @@ -45,7 +55,7 @@ public class FieldStopOnIncreasing , * @return {@link Action#STOP} or {@link Action#CONTINUE} */ @Override - public Action eventOccurred(final FieldSpacecraftState s, final KK detector, final boolean increasing) { + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { return increasing ? Action.STOP : Action.CONTINUE; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/RecordAndContinue.java b/src/main/java/org/orekit/propagation/events/handlers/RecordAndContinue.java index 9983b6e920..aa6aa1c5b4 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/RecordAndContinue.java +++ b/src/main/java/org/orekit/propagation/events/handlers/RecordAndContinue.java @@ -32,20 +32,17 @@ * of memory depending on the duration of propagation and the frequency of * events. * - * @param the type of {@link EventDetector} that this event handler will - * handle events for. * @author Evan Ward */ -public class RecordAndContinue - implements EventHandler { +public class RecordAndContinue implements EventHandler { /** A single event detected during propagation. */ - public static class Event { + public static class Event { /** The observed state. */ private final SpacecraftState state; /** The detector. */ - private final T detector; + private final EventDetector detector; /** The sign of the derivative of the g function. */ private final boolean increasing; @@ -56,7 +53,7 @@ public static class Event { * @param state of the event. * @param increasing if the g function is increasing. */ - private Event(final T detector, + private Event(final EventDetector detector, final SpacecraftState state, final boolean increasing) { this.detector = detector; @@ -71,7 +68,7 @@ private Event(final T detector, * @see EventHandler#eventOccurred(SpacecraftState, EventDetector, * boolean) */ - public T getDetector() { + public EventDetector getDetector() { return detector; } @@ -109,7 +106,7 @@ public String toString() { } /** Observed events. */ - private final List> events; + private final List events; /** Create a new handler using an {@link ArrayList} to store events. */ public RecordAndContinue() { @@ -121,7 +118,7 @@ public RecordAndContinue() { * * @param events collection. */ - public RecordAndContinue(final List> events) { + public RecordAndContinue(final List events) { this.events = events; } @@ -141,7 +138,7 @@ public RecordAndContinue(final List> events) { * @return the events observed by the handler in the order they were * observed. */ - public List> getEvents() { + public List getEvents() { return Collections.unmodifiableList(this.events); } @@ -152,9 +149,9 @@ public void clear() { @Override public Action eventOccurred(final SpacecraftState s, - final T detector, + final EventDetector detector, final boolean increasing) { - events.add(new Event(detector, s, increasing)); + events.add(new Event(detector, s, increasing)); return Action.CONTINUE; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/StopOnDecreasing.java b/src/main/java/org/orekit/propagation/events/handlers/StopOnDecreasing.java index 60a6a6ded7..9c7385bc35 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/StopOnDecreasing.java +++ b/src/main/java/org/orekit/propagation/events/handlers/StopOnDecreasing.java @@ -28,10 +28,20 @@ * * @author Hank Grabowski * - * @param class type for the generic version * @since 6.1 */ -public class StopOnDecreasing implements EventHandler { +public class StopOnDecreasing implements EventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public StopOnDecreasing() { + // nothing to do + } /** Handle a detection event and choose what to do next. *

        The implementation behavior is to {@link @@ -43,7 +53,7 @@ public class StopOnDecreasing implements EventHandler< * when times increases around event * @return {@link Action#STOP} or {@link Action#CONTINUE} */ - public Action eventOccurred(final SpacecraftState s, final T detector, final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { return increasing ? Action.CONTINUE : Action.STOP; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/StopOnEvent.java b/src/main/java/org/orekit/propagation/events/handlers/StopOnEvent.java index 699bcd14e3..3fbd8f0e19 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/StopOnEvent.java +++ b/src/main/java/org/orekit/propagation/events/handlers/StopOnEvent.java @@ -25,10 +25,20 @@ * * @author Hank Grabowski * - * @param class type for the generic version * @since 6.1 */ -public class StopOnEvent implements EventHandler { +public class StopOnEvent implements EventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public StopOnEvent() { + // nothing to do + } /** * Specific implementation of the eventOccurred interface. @@ -39,7 +49,7 @@ public class StopOnEvent implements EventHandler { * @return {@link Action#STOP stop} under all circumstances */ @Override - public Action eventOccurred(final SpacecraftState s, final T detector, final boolean increasing) { + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { return Action.STOP; } diff --git a/src/main/java/org/orekit/propagation/events/handlers/StopOnIncreasing.java b/src/main/java/org/orekit/propagation/events/handlers/StopOnIncreasing.java index e5d0d31f5a..9343b1e590 100644 --- a/src/main/java/org/orekit/propagation/events/handlers/StopOnIncreasing.java +++ b/src/main/java/org/orekit/propagation/events/handlers/StopOnIncreasing.java @@ -28,10 +28,20 @@ * * @author Hank Grabowski * - * @param class type for the generic version * @since 6.1 */ -public class StopOnIncreasing implements EventHandler { +public class StopOnIncreasing implements EventHandler { + + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public StopOnIncreasing() { + // nothing to do + } /** Handle a detection event and choose what to do next. *

        The implementation behavior is to {@link @@ -44,7 +54,7 @@ public class StopOnIncreasing implements EventHandler diff --git a/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java b/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java index 11f71b72d9..08324b5b21 100644 --- a/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java +++ b/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,19 +19,26 @@ import java.util.ArrayList; import java.util.List; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FieldAttitude; import org.orekit.orbits.FieldCartesianOrbit; -import org.orekit.orbits.FieldOrbit; import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.FieldAngularCoordinates; import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.FieldAbsolutePVCoordinates; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; import org.orekit.utils.TimeStampedFieldAngularCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeSpanMap.Span; /** Converter for states and parameters arrays. * @author Luc Maisonobe @@ -105,18 +112,90 @@ protected FieldRotation extend(final FieldRotation original, extend(original.getQ3(), freeParameters), false); } + + /** Process a state into a Gradient version without force model parameter. + * @param state state + * @param freeStateParameters number of free parameters + * @param provider attitude provider + * @return Gradient version of the state + * @since 12.0 + */ + protected static FieldSpacecraftState buildBasicGradientSpacecraftState(final SpacecraftState state, + final int freeStateParameters, + final AttitudeProvider provider) { + + // Derivative field + final Field field = GradientField.getField(freeStateParameters); + + // position always has derivatives + final Vector3D pos = state.getPosition(); + final FieldVector3D posG = new FieldVector3D<>( + Gradient.variable(freeStateParameters, 0, pos.getX()), + Gradient.variable(freeStateParameters, 1, pos.getY()), + Gradient.variable(freeStateParameters, 2, pos.getZ())); + + // velocity may have derivatives or not + final Vector3D vel = state.getPVCoordinates().getVelocity(); + final FieldVector3D velG; + if (freeStateParameters > 3) { + velG = new FieldVector3D<>( + Gradient.variable(freeStateParameters, 3, vel.getX()), + Gradient.variable(freeStateParameters, 4, vel.getY()), + Gradient.variable(freeStateParameters, 5, vel.getZ())); + } else { + velG = new FieldVector3D<>(field, vel); + } + + // acceleration never has derivatives + final Vector3D acc = state.getPVCoordinates().getAcceleration(); + final FieldVector3D accG = new FieldVector3D<>(field, acc); + + // mass never has derivatives + final Gradient gMass = Gradient.constant(freeStateParameters, state.getMass()); + + final TimeStampedFieldPVCoordinates timeStampedFieldPVCoordinates = new TimeStampedFieldPVCoordinates<>( + state.getDate(), posG, velG, accG); + + final FieldCartesianOrbit gOrbit; + final FieldAbsolutePVCoordinates gAbsolutePV; + if (state.isOrbitDefined()) { + final Gradient gMu = Gradient.constant(freeStateParameters, state.getMu()); + gOrbit = new FieldCartesianOrbit<>(timeStampedFieldPVCoordinates, state.getFrame(), gMu); + gAbsolutePV = null; + } else { + gOrbit = null; + gAbsolutePV = new FieldAbsolutePVCoordinates<>(state.getFrame(), timeStampedFieldPVCoordinates); + } + + final FieldAttitude gAttitude; + if (freeStateParameters > 3) { + // compute attitude partial derivatives with respect to position/velocity + gAttitude = provider.getAttitude((state.isOrbitDefined()) ? gOrbit : gAbsolutePV, + timeStampedFieldPVCoordinates.getDate(), state.getFrame()); + } else { + // force model does not depend on attitude, don't bother recomputing it + gAttitude = new FieldAttitude<>(field, state.getAttitude()); + } + + if (state.isOrbitDefined()) { + return new FieldSpacecraftState<>(gOrbit, gAttitude, gMass); + } else { + return new FieldSpacecraftState<>(gAbsolutePV, gAttitude, gMass); + } + } + /** * Get the state with the number of parameters consistent with parametric model. * @param parametricModel parametric model * @return state with the number of parameters consistent with parametric model */ - public FieldSpacecraftState getState(final ParametersDriversProvider parametricModel) { + public FieldSpacecraftState getState(final ParameterDriversProvider parametricModel) { // count the required number of parameters int nbParams = 0; for (final ParameterDriver driver : parametricModel.getParametersDrivers()) { if (driver.isSelected()) { - ++nbParams; + nbParams += driver.getNbOfValues(); } } @@ -130,29 +209,37 @@ public FieldSpacecraftState getState(final ParametersDriversProvider p // we need to create the state final int freeParameters = freeStateParameters + nbParams; final FieldSpacecraftState s0 = gStates.get(0); - - // orbit - final FieldPVCoordinates pv0 = s0.getPVCoordinates(); - final FieldOrbit gOrbit = - new FieldCartesianOrbit<>(new TimeStampedFieldPVCoordinates<>(s0.getDate().toAbsoluteDate(), - extend(pv0.getPosition(), freeParameters), - extend(pv0.getVelocity(), freeParameters), - extend(pv0.getAcceleration(), freeParameters)), - s0.getFrame(), extend(s0.getMu(), freeParameters)); + final AbsoluteDate date = s0.getDate().toAbsoluteDate(); // attitude final FieldAngularCoordinates ac0 = s0.getAttitude().getOrientation(); final FieldAttitude gAttitude = - new FieldAttitude<>(s0.getAttitude().getReferenceFrame(), - new TimeStampedFieldAngularCoordinates<>(gOrbit.getDate(), - extend(ac0.getRotation(), freeParameters), - extend(ac0.getRotationRate(), freeParameters), - extend(ac0.getRotationAcceleration(), freeParameters))); + new FieldAttitude<>(s0.getAttitude().getReferenceFrame(), + new TimeStampedFieldAngularCoordinates<>(date, + extend(ac0.getRotation(), freeParameters), + extend(ac0.getRotationRate(), freeParameters), + extend(ac0.getRotationAcceleration(), freeParameters))); // mass - final Gradient gM = extend(s0.getMass(), freeParameters); + final Gradient gMass = extend(s0.getMass(), freeParameters); - gStates.set(nbParams, new FieldSpacecraftState<>(gOrbit, gAttitude, gM)); + // orbit or absolute position-velocity coordinates + final FieldPVCoordinates pv0 = s0.getPVCoordinates(); + final TimeStampedFieldPVCoordinates timeStampedFieldPVCoordinates = new TimeStampedFieldPVCoordinates<>( + date, + extend(pv0.getPosition(), freeParameters), + extend(pv0.getVelocity(), freeParameters), + extend(pv0.getAcceleration(), freeParameters)); + final FieldSpacecraftState spacecraftState; + if (s0.isOrbitDefined()) { + spacecraftState = new FieldSpacecraftState<>(new FieldCartesianOrbit<>(timeStampedFieldPVCoordinates, + s0.getFrame(), extend(s0.getMu(), freeParameters)), gAttitude, gMass); + } else { + spacecraftState = new FieldSpacecraftState<>(new FieldAbsolutePVCoordinates<>(s0.getFrame(), + timeStampedFieldPVCoordinates), gAttitude, gMass); + } + + gStates.set(nbParams, spacecraftState); } @@ -160,24 +247,67 @@ public FieldSpacecraftState getState(final ParametersDriversProvider p } - /** Get the parametric model parameters. - * @param state state as returned by {@link #getState(ParametersDriversProvider) getState(parametricModel)} + /** Get the parametric model parameters, return gradient values for each span of each driver (several gradient + * values for each parameter). + * Different from {@link #getParametersAtStateDate(FieldSpacecraftState, ParameterDriversProvider)} + * which return a Gradient list containing for each driver the gradient value at state date (only 1 gradient + * value for each parameter). + * @param state state as returned by {@link #getState(ParameterDriversProvider) getState(parametricModel)} * @param parametricModel parametric model associated with the parameters - * @return parametric model parameters + * @return parametric model parameters (for all span of each driver) */ public Gradient[] getParameters(final FieldSpacecraftState state, - final ParametersDriversProvider parametricModel) { + final ParameterDriversProvider parametricModel) { + final int freeParameters = state.getMass().getFreeParameters(); + final List drivers = parametricModel.getParametersDrivers(); + int sizeDrivers = 0; + for ( ParameterDriver driver : drivers) { + sizeDrivers += driver.getNbOfValues(); + } + final Gradient[] parameters = new Gradient[sizeDrivers]; + int index = freeStateParameters; + int i = 0; + for (ParameterDriver driver : drivers) { + // Loop on the spans + for (Span span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + + parameters[i++] = driver.isSelected() ? + Gradient.variable(freeParameters, index++, span.getData()) : + Gradient.constant(freeParameters, span.getData()); + } + } + return parameters; + } + + /** Get the parametric model parameters, return gradient values at state date for each driver (only 1 gradient + * value for each parameter). + * Different from {@link #getParameters(FieldSpacecraftState, ParameterDriversProvider)} + * which return a Gradient list containing for each driver the gradient values for each span value (several gradient + * values for each parameter). + * @param state state as returned by {@link #getState(ParameterDriversProvider) getState(parametricModel)} + * @param parametricModel parametric model associated with the parameters + * @return parametric model parameters (for all span of each driver) + */ + public Gradient[] getParametersAtStateDate(final FieldSpacecraftState state, + final ParameterDriversProvider parametricModel) { final int freeParameters = state.getMass().getFreeParameters(); final List drivers = parametricModel.getParametersDrivers(); + final Gradient[] parameters = new Gradient[drivers.size()]; int index = freeStateParameters; int i = 0; for (ParameterDriver driver : drivers) { - parameters[i++] = driver.isSelected() ? - Gradient.variable(freeParameters, index++, driver.getValue()) : - Gradient.constant(freeParameters, driver.getValue()); + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (span.getData().equals(driver.getNameSpan(state.getDate().toAbsoluteDate()))) { + parameters[i++] = driver.isSelected() ? + Gradient.variable(freeParameters, index, driver.getValue(state.getDate().toAbsoluteDate())) : + Gradient.constant(freeParameters, driver.getValue(state.getDate().toAbsoluteDate())); + } + index = driver.isSelected() ? index + 1 : index; + } } return parameters; } + } diff --git a/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java index 415d79408f..6d957b86b0 100644 --- a/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,6 +26,9 @@ import java.util.Map; import java.util.Queue; +import org.hipparchus.analysis.UnivariateFunction; +import org.hipparchus.analysis.solvers.BracketedUnivariateSolver; +import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.exception.MathRuntimeException; import org.hipparchus.ode.DenseOutputModel; import org.hipparchus.ode.ExpandableODE; @@ -35,7 +38,8 @@ import org.hipparchus.ode.OrdinaryDifferentialEquation; import org.hipparchus.ode.SecondaryODE; import org.hipparchus.ode.events.Action; -import org.hipparchus.ode.events.EventHandlerConfiguration; +import org.hipparchus.ode.events.AdaptableInterval; +import org.hipparchus.ode.events.ODEEventDetector; import org.hipparchus.ode.events.ODEEventHandler; import org.hipparchus.ode.sampling.AbstractODEStateInterpolator; import org.hipparchus.ode.sampling.ODEStateInterpolator; @@ -47,13 +51,14 @@ import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractPropagator; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.sampling.OrekitStepHandler; import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; @@ -102,7 +107,7 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator { /** Type of orbit to output (mean or osculating)
        *

        - * This is used only in the case of semianalitical propagators where there is a clear separation between + * This is used only in the case of semi-analytical propagators where there is a clear separation between * mean and short periodic elements. It is ignored by the Numerical propagator. *

        */ @@ -138,11 +143,27 @@ public void setResetAtEnd(final boolean resetAtEnd) { this.resetAtEnd = resetAtEnd; } + /** Getter for the resetting flag regarding initial state. + * @return resetting flag + * @since 12.0 + */ + public boolean getResetAtEnd() { + return this.resetAtEnd; + } + /** Initialize the mapper. */ protected void initMapper() { stateMapper = createMapper(null, Double.NaN, null, null, null, null); } + /** Get the integrator's name. + * @return name of underlying integrator + * @since 12.0 + */ + public String getIntegratorName() { + return integrator.getName(); + } + /** {@inheritDoc} */ public void setAttitudeProvider(final AttitudeProvider attitudeProvider) { super.setAttitudeProvider(attitudeProvider); @@ -171,16 +192,6 @@ protected OrbitType getOrbitType() { return stateMapper.getOrbitType(); } - /** Check if only the mean elements should be used in a semianalitical propagation. - * @return {@link PropagationType MEAN} if only mean elements have to be used or - * {@link PropagationType OSCULATING} if osculating elements have to be also used. - * @deprecated as of 11.1, replaced by {@link #getPropagationType()} - */ - @Deprecated - protected PropagationType isMeanOrbit() { - return getPropagationType(); - } - /** Get the propagation type. * @return propagation type. * @since 11.1 @@ -198,7 +209,7 @@ public PropagationType getPropagationType() { *

        * @param positionAngleType angle type to use for propagation */ - protected void setPositionAngleType(final PositionAngle positionAngleType) { + protected void setPositionAngleType(final PositionAngleType positionAngleType) { stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(), stateMapper.getOrbitType(), positionAngleType, stateMapper.getAttitudeProvider(), stateMapper.getFrame()); @@ -207,7 +218,7 @@ protected void setPositionAngleType(final PositionAngle positionAngleType) { /** Get propagation parameter type. * @return angle type to use for propagation */ - protected PositionAngle getPositionAngleType() { + protected PositionAngleType getPositionAngleType() { return stateMapper.getPositionAngleType(); } @@ -268,15 +279,6 @@ public String[] getManagedAdditionalStates() { return managed; } - /** Add a set of user-specified equations to be integrated along with the orbit propagation. - * @param additional additional equations - * @deprecated as of 11.1, replaced by {@link #addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} - */ - @Deprecated - public void addAdditionalEquations(final AdditionalEquations additional) { - addAdditionalDerivativesProvider(new AdditionalEquationsAdapter(additional, this::getInitialState)); - } - /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation. * @param provider provider for additional derivatives * @see #addAdditionalStateProvider(org.orekit.propagation.AdditionalStateProvider) @@ -334,10 +336,7 @@ protected void setUpUserEventDetectors() { * @param detector event detector to wrap */ protected void setUpEventDetector(final ODEIntegrator integ, final EventDetector detector) { - integ.addEventHandler(new AdaptedEventDetector(detector), - detector.getMaxCheckInterval(), - detector.getThreshold(), - detector.getMaxIterationCount()); + integ.addEventDetector(new AdaptedEventDetector(detector)); } /** {@inheritDoc} */ @@ -365,7 +364,7 @@ public EphemerisGenerator getEphemerisGenerator() { * @return new mapper */ protected abstract StateMapper createMapper(AbsoluteDate referenceDate, double mu, - OrbitType orbitType, PositionAngle positionAngleType, + OrbitType orbitType, PositionAngleType positionAngleType, AttitudeProvider attitudeProvider, Frame frame); /** Get the differential equations to integrate (for main state only). @@ -462,14 +461,14 @@ private SpacecraftState integrateDynamics(final AbsoluteDate tEnd) { } if (getInitialState().getMass() <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, getInitialState().getMass()); } // convert space flight dynamics API to math API final SpacecraftState initialIntegrationState = getInitialIntegrationState(); final ODEState mathInitialState = createInitialState(initialIntegrationState); - final ExpandableODE mathODE = createODE(integrator, mathInitialState); + final ExpandableODE mathODE = createODE(integrator); // mathematical integration final ODEStateAndDerivative mathFinalState; @@ -591,11 +590,9 @@ private double[][] secondaryDerivative(final SpacecraftState state) { /** Create an ODE with all equations. * @param integ numerical integrator to use for propagation. - * @param mathInitialState initial state * @return a new ode */ - private ExpandableODE createODE(final ODEIntegrator integ, - final ODEState mathInitialState) { + private ExpandableODE createODE(final ODEIntegrator integ) { final ExpandableODE ode = new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ))); @@ -752,8 +749,8 @@ public double[] computeDerivatives(final double t, final double[] y) { // update space dynamics view SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN); - currentState = updateAdditionalStates(currentState); + currentState = updateAdditionalStates(currentState); // compute main state differentials return main.computeDerivatives(currentState); @@ -811,7 +808,7 @@ public double[] computeDerivatives(final double t, final double[] primary, int yieldCount = 0; while (!pending.isEmpty()) { final AdditionalDerivativesProvider provider = pending.remove(); - if (provider.yield(updated)) { + if (provider.yields(updated)) { // this provider has to wait for another one, // we put it again in the pending queue pending.add(provider); @@ -871,14 +868,19 @@ private SpacecraftState convert(final double t, final double[] primary, } /** Adapt an {@link org.orekit.propagation.events.EventDetector} - * to Hipparchus {@link org.hipparchus.ode.events.ODEEventHandler} interface. + * to Hipparchus {@link org.hipparchus.ode.events.ODEEventDetector} interface. * @author Fabien Maussion */ - private class AdaptedEventDetector implements ODEEventHandler { + private class AdaptedEventDetector implements ODEEventDetector { /** Underlying event detector. */ private final EventDetector detector; + /** Underlying event handler. + * @since 12.0 + */ + private final EventHandler handler; + /** Time of the previous call to g. */ private double lastT; @@ -890,10 +892,29 @@ private class AdaptedEventDetector implements ODEEventHandler { */ AdaptedEventDetector(final EventDetector detector) { this.detector = detector; + this.handler = detector.getHandler(); this.lastT = Double.NaN; this.lastG = Double.NaN; } + /** {@inheritDoc} */ + @Override + public AdaptableInterval getMaxCheckInterval() { + return s -> detector.getMaxCheckInterval().currentInterval(convert(s)); + } + + /** {@inheritDoc} */ + @Override + public int getMaxIterationCount() { + return detector.getMaxIterationCount(); + } + + /** {@inheritDoc} */ + @Override + public BracketedUnivariateSolver getSolver() { + return new BracketingNthOrderBrentSolver(0, detector.getThreshold(), 0, 5); + } + /** {@inheritDoc} */ public void init(final ODEStateAndDerivative s0, final double t) { detector.init(convert(s0), stateMapper.mapDoubleToDate(t)); @@ -911,33 +932,41 @@ public double g(final ODEStateAndDerivative s) { } /** {@inheritDoc} */ - public Action eventOccurred(final ODEStateAndDerivative s, final boolean increasing) { - return detector.eventOccurred(convert(s), increasing); - } + public ODEEventHandler getHandler() { - /** {@inheritDoc} */ - public ODEState resetState(final ODEStateAndDerivative s) { + return new ODEEventHandler() { + + /** {@inheritDoc} */ + public Action eventOccurred(final ODEStateAndDerivative s, final ODEEventDetector d, final boolean increasing) { + return handler.eventOccurred(convert(s), detector, increasing); + } - final SpacecraftState oldState = convert(s); - final SpacecraftState newState = detector.resetState(oldState); - stateChanged(newState); + /** {@inheritDoc} */ + public ODEState resetState(final ODEEventDetector d, final ODEStateAndDerivative s) { - // main part - final double[] primary = new double[s.getPrimaryStateDimension()]; - stateMapper.mapStateToArray(newState, primary, null); + final SpacecraftState oldState = convert(s); + final SpacecraftState newState = handler.resetState(detector, oldState); + stateChanged(newState); - // secondary part - final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)]; - for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) { - final String name = provider.getName(); - final int offset = secondaryOffsets.get(name); - final int dimension = provider.getDimension(); - System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension); - } + // main part + final double[] primary = new double[s.getPrimaryStateDimension()]; + stateMapper.mapStateToArray(newState, primary, null); + + // secondary part + final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)]; + for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) { + final String name = provider.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = provider.getDimension(); + System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension); + } - return new ODEState(newState.getDate().durationFrom(getStartDate()), - primary, secondary); + return new ODEState(newState.getDate().durationFrom(getStartDate()), + primary, secondary); + + } + }; } } @@ -1177,8 +1206,8 @@ private static class IntegratorResetter implements AutoCloseable { /** Wrapped integrator. */ private final ODEIntegrator integrator; - /** Initial event handlers list. */ - private final List eventHandlersConfigurations; + /** Initial event detectors list. */ + private final List detectors; /** Initial step handlers list. */ private final List stepHandlers; @@ -1187,9 +1216,9 @@ private static class IntegratorResetter implements AutoCloseable { * @param integrator wrapped integrator */ IntegratorResetter(final ODEIntegrator integrator) { - this.integrator = integrator; - this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations()); - this.stepHandlers = new ArrayList<>(integrator.getStepHandlers()); + this.integrator = integrator; + this.detectors = new ArrayList<>(integrator.getEventDetectors()); + this.stepHandlers = new ArrayList<>(integrator.getStepHandlers()); } /** {@inheritDoc} @@ -1201,16 +1230,12 @@ private static class IntegratorResetter implements AutoCloseable { public void close() { // reset event handlers - integrator.clearEventHandlers(); - eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(), - c.getMaxCheckInterval(), - c.getConvergence(), - c.getMaxIterationCount(), - c.getSolver())); + integrator.clearEventDetectors(); + detectors.forEach(integrator::addEventDetector); // reset step handlers integrator.clearStepHandlers(); - stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler)); + stepHandlers.forEach(integrator::addStepHandler); } diff --git a/src/main/java/org/orekit/propagation/integration/AbstractJacobiansMapper.java b/src/main/java/org/orekit/propagation/integration/AbstractJacobiansMapper.java deleted file mode 100644 index 7bfefd9272..0000000000 --- a/src/main/java/org/orekit/propagation/integration/AbstractJacobiansMapper.java +++ /dev/null @@ -1,153 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.integration; - -import java.util.List; -import java.util.stream.Collectors; - -import org.hipparchus.linear.Array2DRowRealMatrix; -import org.hipparchus.linear.RealMatrix; -import org.orekit.propagation.MatricesHarvester; -import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.ParameterDriversList; - -/** Base class for jacobian mapper. - * @author Bryan Cazabonne - * @since 10.0 - */ -public abstract class AbstractJacobiansMapper implements MatricesHarvester { - - /** State dimension, fixed to 6. - * @since 9.0 - */ - public static final int STATE_DIMENSION = 6; - - /** Name. */ - private String name; - - /** Selected parameters for Jacobian computation. */ - private final ParameterDriversList parameters; - - /** Simple constructor. - * @param name name of the Jacobians - * @param parameters selected parameters for Jacobian computation - */ - protected AbstractJacobiansMapper(final String name, final ParameterDriversList parameters) { - this.name = name; - this.parameters = parameters; - } - - /** Get the name of the partial Jacobians. - * @return name of the Jacobians - */ - public String getName() { - return name; - } - - /** Get the number of parameters. - * @return number of parameters - */ - public int getParameters() { - return parameters.getNbParams(); - } - - /** Compute the length of the one-dimensional additional state array needed. - * @return length of the one-dimensional additional state array - */ - public int getAdditionalStateDimension() { - return STATE_DIMENSION * (STATE_DIMENSION + parameters.getNbParams()); - } - - /** Not used anymore. - * @param s spacecraft state - * @deprecated as of 11.1, not used anymore - */ - @Deprecated - public void analyticalDerivatives(final SpacecraftState s) { - // nothing by default - } - - /** {@inheritDoc} */ - @Override - public void setReferenceState(final SpacecraftState reference) { - // nothing by default - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getStateTransitionMatrix(final SpacecraftState s) { - final double[][] dYdY0 = new double[STATE_DIMENSION][STATE_DIMENSION]; - getStateJacobian(s, dYdY0); - return new Array2DRowRealMatrix(dYdY0, false); - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getParametersJacobian(final SpacecraftState s) { - if (getParameters() == 0) { - return null; - } else { - final double[][] dYdP = new double[STATE_DIMENSION][getParameters()]; - getParametersJacobian(s, dYdP); - return new Array2DRowRealMatrix(dYdP, false); - } - } - - /** {@inheritDoc} */ - @Override - public List getJacobiansColumnsNames() { - return parameters.getDrivers().stream().map(d -> d.getName()).collect(Collectors.toList()); - } - - /** Set the Jacobian with respect to state into a one-dimensional additional state array. - * @param state spacecraft state - * @param dY1dY0 Jacobian of current state at time t₁ - * with respect to state at some previous time t₀ - * @param dY1dP Jacobian of current state at time t₁ - * with respect to parameters (may be null if there are no parameters) - * @param p placeholder where to put the one-dimensional additional state - * @see #getStateJacobian(SpacecraftState, double[][]) - */ - public abstract void setInitialJacobians(SpacecraftState state, double[][] dY1dY0, double[][] dY1dP, double[] p); - - /** Get the Jacobian with respect to state from a one-dimensional additional state array. - *

        - * This method extract the data from the {@code state} and put it in the - * {@code dYdY0} array. - *

        - * @param state spacecraft state - * @param dYdY0 placeholder where to put the Jacobian with respect to state - * @see #getParametersJacobian(SpacecraftState, double[][]) - */ - public abstract void getStateJacobian(SpacecraftState state, double[][] dYdY0); - - /** Get the Jacobian with respect to parameters from a one-dimensional additional state array. - *

        - * This method extract the data from the {@code state} and put it in the - * {@code dYdP} array. - *

        - *

        - * If no parameters have been set in the constructor, the method returns immediately and - * does not reference {@code dYdP} which can safely be null in this case. - *

        - * @param state spacecraft state - * @param dYdP placeholder where to put the Jacobian with respect to parameters - * @see #getStateJacobian(SpacecraftState, double[][]) - */ - public abstract void getParametersJacobian(SpacecraftState state, double[][] dYdP); - -} diff --git a/src/main/java/org/orekit/propagation/integration/AdditionalDerivativesProvider.java b/src/main/java/org/orekit/propagation/integration/AdditionalDerivativesProvider.java index 4933e46499..d6eb4173f7 100644 --- a/src/main/java/org/orekit/propagation/integration/AdditionalDerivativesProvider.java +++ b/src/main/java/org/orekit/propagation/integration/AdditionalDerivativesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -91,37 +91,18 @@ default void init(final SpacecraftState initialState, final AbsoluteDate target) * @return true if this provider should yield so another provider has an opportunity to add missing parts * as the state is incrementally built up */ - default boolean yield(SpacecraftState state) { + default boolean yields(SpacecraftState state) { return false; } - /** Compute the derivatives related to the additional state parameters. - * @param s current state information: date, kinematics, attitude, and - * additional states this equations depend on (according to the - * {@link #yield(SpacecraftState) yield} method) - * @return computed derivatives - * @deprecated as of 11.2, replaced by {@link #combinedDerivatives(SpacecraftState)} - */ - @Deprecated - double[] derivatives(SpacecraftState s); - /** Compute the derivatives related to the additional state (and optionally main state increments). - *

        - * As of 11.2, there is a default implementation that calls the deprecated - * {@link #derivatives(SpacecraftState)} method. This has been done for - * backward compatibility only and will be removed in 12.0. - *

        * @param s current state information: date, kinematics, attitude, and * additional states this equations depend on (according to the - * {@link #yield(SpacecraftState) yield} method) + * {@link #yields(SpacecraftState) yield} method) * @return computed combined derivatives, which may include some incremental * coupling effect to add to main state derivatives * @since 11.2 */ - default CombinedDerivatives combinedDerivatives(SpacecraftState s) { - // this default implementation will be removed - // when the deprecated derivatives method above is removed - return new CombinedDerivatives(derivatives(s), null); - } + CombinedDerivatives combinedDerivatives(SpacecraftState s); } diff --git a/src/main/java/org/orekit/propagation/integration/AdditionalEquations.java b/src/main/java/org/orekit/propagation/integration/AdditionalEquations.java deleted file mode 100644 index 4f26a3f1f7..0000000000 --- a/src/main/java/org/orekit/propagation/integration/AdditionalEquations.java +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright 2010-2011 Centre National d'Études Spatiales - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.integration; - -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; - -/** This interface allows users to add their own differential equations to a numerical propagator. - * - *

        - * In some cases users may need to integrate some problem-specific equations along with - * classical spacecraft equations of motions. One example is optimal control in low - * thrust where adjoint parameters linked to the minimized Hamiltonian must be integrated. - * Another example is formation flying or rendez-vous which use the Clohessy-Whiltshire - * equations for the relative motion. - *

        - *

        - * This interface allows users to add such equations to a {@link - * org.orekit.propagation.numerical.NumericalPropagator numerical propagator} or a {@link - * org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSST propagator}. Users provide the - * equations as an implementation of this interface and register it to the propagator thanks to - * its {@link org.orekit.propagation.integration.AbstractIntegratedPropagator#addAdditionalEquations(AdditionalEquations)} - * method. Several such objects can be registered with each numerical propagator, but it is - * recommended to gather in the same object the sets of parameters which equations can interact - * on each others states. - *

        - *

        - * The additional parameters are gathered in a simple p array. The additional equations compute - * the pDot array, which is the time-derivative of the p array. Since the additional parameters - * p may also have an influence on the equations of motion themselves that should be accumulated - * to the main state derivatives (for example an equation linked to a complex thrust model may - * induce an acceleration and a mass change), the {@link #computeDerivatives(SpacecraftState, double[]) - * computeDerivatives} method can return a double array that will be - * added to the main state derivatives. This means these equations can be used as an - * additional force model if needed. If the additional parameters have no influence at all on - * the main spacecraft state, a null reference may be returned. - *

        - *

        - * This interface is the numerical (read not already integrated) counterpart of - * the {@link org.orekit.propagation.AdditionalStateProvider} interface. - * It allows to append various additional state parameters to any {@link - * org.orekit.propagation.numerical.NumericalPropagator numerical propagator} or {@link - * org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSST propagator}. - *

        - * @see AbstractIntegratedPropagator - * @see org.orekit.propagation.AdditionalStateProvider - * @author Luc Maisonobe - * @deprecated as of 11.1, replaced by {@link AdditionalDerivativesProvider} - */ -@Deprecated -public interface AdditionalEquations { - - /** Get the name of the additional state. - * @return name of the additional state - */ - String getName(); - - /** - * Initialize the equations at the start of propagation. - * - *

        - * This method will be called once at propagation start, - * before any calls to {@link #computeDerivatives(SpacecraftState, double[])}. - *

        - * - *

        - * The default implementation of this method does nothing. - *

        - * - * @param initialState initial state information at the start of propagation. - * @param target date of propagation. Not equal to {@code - * initialState.getDate()}. - */ - default void init(final SpacecraftState initialState, final AbsoluteDate target) { - // nothing by default - } - - /** Compute the derivatives related to the additional state parameters. - *

        - * When this method is called, the spacecraft state contains the main - * state (orbit, attitude and mass), all the states provided through - * the {@link org.orekit.propagation.AdditionalStateProvider additional - * state providers} registered to the propagator, and the additional state - * integrated using this equation. It does not contains any other - * states to be integrated alongside during the same propagation. - *

        - * @param s current state information: date, kinematics, attitude, and - * additional state - * @param pDot placeholder where the derivatives of the additional parameters - * should be put - * @return cumulative effect of the equations on the main state (may be null if - * equations do not change main state at all) - */ - double[] computeDerivatives(SpacecraftState s, double[] pDot); - -} diff --git a/src/main/java/org/orekit/propagation/integration/AdditionalEquationsAdapter.java b/src/main/java/org/orekit/propagation/integration/AdditionalEquationsAdapter.java deleted file mode 100644 index 8e8641532c..0000000000 --- a/src/main/java/org/orekit/propagation/integration/AdditionalEquationsAdapter.java +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.integration; - -import java.util.function.Supplier; - -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; - - -/** Temporary adapter from {@link AdditionalEquations} to {@link AdditionalDerivativesProvider}. - * @since 11.1 - * @deprecated must be removed in 12.0 when {@link AdditionalEquations} is removed - */ -@Deprecated -public class AdditionalEquationsAdapter implements AdditionalDerivativesProvider { - - /** Wrapped equations. */ - private final AdditionalEquations equations; - - /** Supplier for reference state. */ - private final Supplier stateSupplier; - - /** Dimension. */ - private int dimension; - - /** Simple constructor. - * @param equations wrapped equations - * @param stateSupplier supplier for reference state - */ - public AdditionalEquationsAdapter(final AdditionalEquations equations, final Supplier stateSupplier) { - this.equations = equations; - this.stateSupplier = stateSupplier; - this.dimension = -1; - } - - /** {@inheritDoc} */ - @Override - public String getName() { - return equations.getName(); - } - - /** {@inheritDoc} */ - @Override - public int getDimension() { - if (dimension < 0) { - // retrieve the dimension the first time we need it - dimension = stateSupplier.get().getAdditionalState(getName()).length; - } - return dimension; - } - - /** {@inheritDoc} */ - @Override - public boolean yield(final SpacecraftState state) { - return false; - } - - /** {@inheritDoc} */ - @Override - public void init(final SpacecraftState initialState, final AbsoluteDate target) { - equations.init(initialState, target); - } - - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - - /** {@inheritDoc} */ - @Override - public CombinedDerivatives combinedDerivatives(final SpacecraftState state) { - final double[] pDot = new double[getDimension()]; - final double[] mainDot = equations.computeDerivatives(state, pDot); - return new CombinedDerivatives(pDot, mainDot); - } - -} diff --git a/src/main/java/org/orekit/propagation/integration/CombinedDerivatives.java b/src/main/java/org/orekit/propagation/integration/CombinedDerivatives.java index cd833be6c3..20b92688b0 100644 --- a/src/main/java/org/orekit/propagation/integration/CombinedDerivatives.java +++ b/src/main/java/org/orekit/propagation/integration/CombinedDerivatives.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java index de51b98e29..8778caaade 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.analysis.solvers.FieldBracketingNthOrderBrentSolver; import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.exception.MathIllegalStateException; import org.hipparchus.ode.FieldDenseOutputModel; @@ -38,7 +39,8 @@ import org.hipparchus.ode.FieldOrdinaryDifferentialEquation; import org.hipparchus.ode.FieldSecondaryODE; import org.hipparchus.ode.events.Action; -import org.hipparchus.ode.events.FieldEventHandlerConfiguration; +import org.hipparchus.ode.events.FieldAdaptableInterval; +import org.hipparchus.ode.events.FieldODEEventDetector; import org.hipparchus.ode.events.FieldODEEventHandler; import org.hipparchus.ode.sampling.AbstractFieldODEStateInterpolator; import org.hipparchus.ode.sampling.FieldODEStateInterpolator; @@ -51,13 +53,14 @@ import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldAbstractPropagator; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.sampling.FieldOrekitStepHandler; import org.orekit.propagation.sampling.FieldOrekitStepInterpolator; import org.orekit.time.FieldAbsoluteDate; @@ -66,8 +69,8 @@ /** Common handling of {@link org.orekit.propagation.FieldPropagator FieldPropagator} * methods for both numerical and semi-analytical propagators. - * @param the type of the field elements - * @author Luc Maisonobe + * @author Luc Maisonobe + * @param type of the field element */ public abstract class FieldAbstractIntegratedPropagator> extends FieldAbstractPropagator { @@ -106,7 +109,7 @@ public abstract class FieldAbstractIntegratedPropagator *

        - * This is used only in the case of semianalitical propagators where there is a clear separation between + * This is used only in the case of semi-analytical propagators where there is a clear separation between * mean and short periodic elements. It is ignored by the Numerical propagator. *

        */ @@ -144,6 +147,14 @@ public void setResetAtEnd(final boolean resetAtEnd) { this.resetAtEnd = resetAtEnd; } + /** Getter for the resetting flag regarding initial state. + * @return resetting flag + * @since 12.0 + */ + public boolean getResetAtEnd() { + return this.resetAtEnd; + } + /** Initialize the mapper. * @param field Field used by default */ @@ -152,6 +163,14 @@ protected void initMapper(final Field field) { stateMapper = createMapper(null, zero.add(Double.NaN), null, null, null, null); } + /** Get the integrator's name. + * @return name of underlying integrator + * @since 12.0 + */ + public String getIntegratorName() { + return integrator.getName(); + } + /** {@inheritDoc} */ public void setAttitudeProvider(final AttitudeProvider attitudeProvider) { super.setAttitudeProvider(attitudeProvider); @@ -176,7 +195,7 @@ protected OrbitType getOrbitType() { return stateMapper.getOrbitType(); } - /** Check if only the mean elements should be used in a semianalitical propagation. + /** Check if only the mean elements should be used in a semi-analytical propagation. * @return {@link PropagationType MEAN} if only mean elements have to be used or * {@link PropagationType OSCULATING} if osculating elements have to be also used. */ @@ -201,7 +220,7 @@ public PropagationType getPropagationType() { *

        * @param positionAngleType angle type to use for propagation */ - protected void setPositionAngleType(final PositionAngle positionAngleType) { + protected void setPositionAngleType(final PositionAngleType positionAngleType) { stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(), stateMapper.getOrbitType(), positionAngleType, stateMapper.getAttitudeProvider(), stateMapper.getFrame()); @@ -210,7 +229,7 @@ protected void setPositionAngleType(final PositionAngle positionAngleType) { /** Get propagation parameter type. * @return angle type to use for propagation */ - protected PositionAngle getPositionAngleType() { + protected PositionAngleType getPositionAngleType() { return stateMapper.getPositionAngleType(); } @@ -271,15 +290,6 @@ public String[] getManagedAdditionalStates() { return managed; } - /** Add a set of user-specified equations to be integrated along with the orbit propagation. - * @param additional additional equations - * @deprecated as of 11.1, replaced by {@link #addAdditionalDerivativesProvider(FieldAdditionalDerivativesProvider)} - */ - @Deprecated - public void addAdditionalEquations(final FieldAdditionalEquations additional) { - addAdditionalDerivativesProvider(new FieldAdditionalEquationsAdapter<>(additional, this::getInitialState)); - } - /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation. * @param provider provider for additional derivatives * @see #addAdditionalStateProvider(org.orekit.propagation.FieldAdditionalStateProvider) @@ -336,10 +346,7 @@ protected void setUpUserEventDetectors() { * @param detector event detector to wrap */ protected void setUpEventDetector(final FieldODEIntegrator integ, final FieldEventDetector detector) { - integ.addEventHandler(new FieldAdaptedEventDetector(detector), - detector.getMaxCheckInterval().getReal(), - detector.getThreshold().getReal(), - detector.getMaxIterationCount()); + integ.addEventDetector(new FieldAdaptedEventDetector(detector)); } /** {@inheritDoc} */ @@ -367,7 +374,7 @@ public FieldEphemerisGenerator getEphemerisGenerator() { * @return new mapper */ protected abstract FieldStateMapper createMapper(FieldAbsoluteDate referenceDate, T mu, - OrbitType orbitType, PositionAngle positionAngleType, + OrbitType orbitType, PositionAngleType positionAngleType, AttitudeProvider attitudeProvider, Frame frame); /** Get the differential equations to integrate (for main state only). @@ -452,14 +459,14 @@ private FieldSpacecraftState integrateDynamics(final FieldAbsoluteDate tEn setMu(getInitialState().getMu()); } if (getInitialState().getMass().getReal() <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, getInitialState().getMass()); } // convert space flight dynamics API to math API final FieldSpacecraftState initialIntegrationState = getInitialIntegrationState(); final FieldODEState mathInitialState = createInitialState(initialIntegrationState); - final FieldExpandableODE mathODE = createODE(integrator, mathInitialState); + final FieldExpandableODE mathODE = createODE(integrator); // mathematical integration final FieldODEStateAndDerivative mathFinalState; @@ -582,11 +589,9 @@ private T[][] secondaryDerivative(final FieldSpacecraftState state) { /** Create an ODE with all equations. * @param integ numerical integrator to use for propagation. - * @param mathInitialState initial state * @return a new ode */ - private FieldExpandableODE createODE(final FieldODEIntegrator integ, - final FieldODEState mathInitialState) { + private FieldExpandableODE createODE(final FieldODEIntegrator integ) { final FieldExpandableODE ode = new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ))); @@ -685,7 +690,9 @@ private FieldODEStateAndDerivative convert(final FieldSpacecraftState stat } - /** Differential equations for the main state (orbit, attitude and mass). */ + /** Differential equations for the main state (orbit, attitude and mass). + * @param type of the field element + */ public interface MainStateEquations> { /** @@ -802,7 +809,7 @@ public T[] computeDerivatives(final T t, final T[] primary, int yieldCount = 0; while (!pending.isEmpty()) { final FieldAdditionalDerivativesProvider equations = pending.remove(); - if (equations.yield(updated)) { + if (equations.yields(updated)) { // these equations have to wait for another set, // we put them again in the pending queue pending.add(equations); @@ -862,15 +869,20 @@ private FieldSpacecraftState convert(final T t, final T[] primary, } /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector} - * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventHandler} interface. + * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventDetector} interface. * @param class type for the generic version * @author Fabien Maussion */ - private class FieldAdaptedEventDetector implements FieldODEEventHandler { + private class FieldAdaptedEventDetector implements FieldODEEventDetector { /** Underlying event detector. */ private final FieldEventDetector detector; + /** Underlying event handler. + * @since 12.0 + */ + private final FieldEventHandler handler; + /** Time of the previous call to g. */ private T lastT; @@ -882,10 +894,30 @@ private class FieldAdaptedEventDetector implements FieldODEEventHandler { */ FieldAdaptedEventDetector(final FieldEventDetector detector) { this.detector = detector; + this.handler = detector.getHandler(); this.lastT = getField().getZero().add(Double.NaN); this.lastG = getField().getZero().add(Double.NaN); } + /** {@inheritDoc} */ + @Override + public FieldAdaptableInterval getMaxCheckInterval() { + return s -> detector.getMaxCheckInterval().currentInterval(convert(s)); + } + + /** {@inheritDoc} */ + @Override + public int getMaxIterationCount() { + return detector.getMaxIterationCount(); + } + + /** {@inheritDoc} */ + @Override + public FieldBracketingNthOrderBrentSolver getSolver() { + final T zero = detector.getThreshold().getField().getZero(); + return new FieldBracketingNthOrderBrentSolver<>(zero, detector.getThreshold(), zero, 5); + } + /** {@inheritDoc} */ public void init(final FieldODEStateAndDerivative s0, final T t) { detector.init(convert(s0), stateMapper.mapDoubleToDate(t)); @@ -903,32 +935,43 @@ public T g(final FieldODEStateAndDerivative s) { } /** {@inheritDoc} */ - public Action eventOccurred(final FieldODEStateAndDerivative s, final boolean increasing) { - return detector.eventOccurred(convert(s), increasing); - } + public FieldODEEventHandler getHandler() { - /** {@inheritDoc} */ - public FieldODEState resetState(final FieldODEStateAndDerivative s) { + return new FieldODEEventHandler() { - final FieldSpacecraftState oldState = convert(s); - final FieldSpacecraftState newState = detector.resetState(oldState); - stateChanged(newState); + /** {@inheritDoc} */ + public Action eventOccurred(final FieldODEStateAndDerivative s, + final FieldODEEventDetector d, + final boolean increasing) { + return handler.eventOccurred(convert(s), detector, increasing); + } - // main part - final T[] primary = MathArrays.buildArray(getField(), s.getPrimaryStateDimension()); - stateMapper.mapStateToArray(newState, primary, null); + /** {@inheritDoc} */ + public FieldODEState resetState(final FieldODEEventDetector d, + final FieldODEStateAndDerivative s) { + + final FieldSpacecraftState oldState = convert(s); + final FieldSpacecraftState newState = handler.resetState(detector, oldState); + stateChanged(newState); + + // main part + final T[] primary = MathArrays.buildArray(getField(), s.getPrimaryStateDimension()); + stateMapper.mapStateToArray(newState, primary, null); + + // secondary part + final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalDerivativesProviders.size()); + for (final FieldAdditionalDerivativesProvider provider : additionalDerivativesProviders) { + final String name = provider.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = provider.getDimension(); + System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension); + } - // secondary part - final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalDerivativesProviders.size()); - for (final FieldAdditionalDerivativesProvider provider : additionalDerivativesProviders) { - final String name = provider.getName(); - final int offset = secondaryOffsets.get(name); - final int dimension = provider.getDimension(); - System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension); - } + return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()), + primary, secondary); + } + }; - return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()), - primary, secondary); } } @@ -1146,7 +1189,7 @@ unmanaged, getAdditionalStateProviders(), * If propagator-specific event handlers and step handlers are added to * the integrator in the try block, they will be removed automatically * when leaving the block, so the integrator only keep its own handlers - * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate). + * between calls to {@link AbstractIntegratedPropagator#propagate(FieldAbsoluteDate, FieldAbsoluteDate). *

        * @param the type of the field elements * @since 11.0 @@ -1156,8 +1199,8 @@ private static class IntegratorResetter> imple /** Wrapped integrator. */ private final FieldODEIntegrator integrator; - /** Initial event handlers list. */ - private final List> eventHandlersConfigurations; + /** Initial event detectors list. */ + private final List> detectors; /** Initial step handlers list. */ private final List> stepHandlers; @@ -1166,9 +1209,9 @@ private static class IntegratorResetter> imple * @param integrator wrapped integrator */ IntegratorResetter(final FieldODEIntegrator integrator) { - this.integrator = integrator; - this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations()); - this.stepHandlers = new ArrayList<>(integrator.getStepHandlers()); + this.integrator = integrator; + this.detectors = new ArrayList<>(integrator.getEventDetectors()); + this.stepHandlers = new ArrayList<>(integrator.getStepHandlers()); } /** {@inheritDoc} @@ -1180,16 +1223,12 @@ private static class IntegratorResetter> imple public void close() { // reset event handlers - integrator.clearEventHandlers(); - eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(), - c.getMaxCheckInterval(), - c.getConvergence().getReal(), - c.getMaxIterationCount(), - c.getSolver())); + integrator.clearEventDetectors(); + detectors.forEach(integrator::addEventDetector); // reset step handlers integrator.clearStepHandlers(); - stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler)); + stepHandlers.forEach(integrator::addStepHandler); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldAdditionalDerivativesProvider.java b/src/main/java/org/orekit/propagation/integration/FieldAdditionalDerivativesProvider.java index eaab33be23..83c81c97cc 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldAdditionalDerivativesProvider.java +++ b/src/main/java/org/orekit/propagation/integration/FieldAdditionalDerivativesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -49,6 +49,7 @@ * @see org.orekit.propagation.integration.FieldAbstractIntegratedPropagator * @author Luc Maisonobe * @since 11.1 +* @param type of the field elements */ public interface FieldAdditionalDerivativesProvider> { @@ -86,43 +87,24 @@ default void init(final FieldSpacecraftState initialState, final FieldAbsolut * } *

        * The default implementation returns {@code false}, meaning that derivative data can be - * {@link #derivatives(FieldSpacecraftState) computed} immediately. + * {@link #combinedDerivatives(FieldSpacecraftState) computed} immediately. *

        * @param state state to handle * @return true if this provider should yield so another provider has an opportunity to add missing parts * as the state is incrementally built up */ - default boolean yield(FieldSpacecraftState state) { + default boolean yields(FieldSpacecraftState state) { return false; } - /** Compute the derivatives related to the additional state parameters. - * @param s current state information: date, kinematics, attitude, and - * additional states this equations depend on (according to the - * {@link #yield(FieldSpacecraftState) yield} method) - * @return computed derivatives - * @deprecated as of 11.2, replaced by {@link #combinedDerivatives(FieldSpacecraftState)} - */ - @Deprecated - T[] derivatives(FieldSpacecraftState s); - /** Compute the derivatives related to the additional state (and optionally main state increments). - *

        - * As of 11.2, there is a default implementation that calls the deprecated - * {@link #derivatives(FieldSpacecraftState)} method. This has been done for - * backward compatibility only and will be removed in 12.0. - *

        * @param s current state information: date, kinematics, attitude, and * additional states this equations depend on (according to the - * {@link #yield(FieldSpacecraftState) yield} method) + * {@link #yields(FieldSpacecraftState) yield} method) * @return computed combined derivatives, which may include some incremental * coupling effect to add to main state derivatives * @since 11.2 */ - default FieldCombinedDerivatives combinedDerivatives(FieldSpacecraftState s) { - // this default implementation will be removed - // when the deprecated derivatives method above is removed - return new FieldCombinedDerivatives<>(derivatives(s), null); - } + FieldCombinedDerivatives combinedDerivatives(FieldSpacecraftState s); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java b/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java deleted file mode 100644 index 858997ecc4..0000000000 --- a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright 2010-2011 Centre National d'Études Spatiales - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.integration; - -import org.hipparchus.CalculusFieldElement; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.time.FieldAbsoluteDate; - -/** This interface allows users to add their own differential equations to a numerical propagator. - * - *

        - * In some cases users may need to integrate some problem-specific equations along with - * classical spacecraft equations of motions. One example is optimal control in low - * thrust where adjoint parameters linked to the minimized Hamiltonian must be integrated. - * Another example is formation flying or rendez-vous which use the Clohessy-Whiltshire - * equations for the relative motion. - *

        - *

        - * This interface allows users to add such equations to a {@link - * org.orekit.propagation.numerical.FieldNumericalPropagator numerical propagator}. Users provide the - * equations as an implementation of this interface and register it to the propagator thanks to - * its {@link org.orekit.propagation.numerical.FieldNumericalPropagator#addAdditionalEquations(FieldAdditionalEquations)} - * method. Several such objects can be registered with each numerical propagator, but it is - * recommended to gather in the same object the sets of parameters which equations can interact - * on each others states. - *

        - *

        - * The additional parameters are gathered in a simple p array. The additional equations compute - * the pDot array, which is the time-derivative of the p array. Since the additional parameters - * p may also have an influence on the equations of motion themselves that should be accumulated - * to the main state derivatives (for example an equation linked to a complex thrust model may - * induce an acceleration and a mass change), the {@link #computeDerivatives(FieldSpacecraftState, - * CalculusFieldElement[]) computeDerivatives} method can return a double array that will be - * added to the main state derivatives. This means these equations can be used as an - * additional force model if needed. If the additional parameters have no influence at all on - * the main spacecraft state, a null reference may be returned. - *

        - *

        - * This interface is the numerical (read not already integrated) counterpart of - * the {@link org.orekit.propagation.FieldAdditionalStateProvider} interface. - * It allows to append various additional state parameters to any {@link - * org.orekit.propagation.numerical.FieldNumericalPropagator numerical propagator}. - *

        - * @see AbstractIntegratedPropagator - * @see org.orekit.propagation.FieldAdditionalStateProvider - * @author Luc Maisonobe - * @deprecated as of 11.1, replaced by {@link FieldAdditionalDerivativesProvider} - */ -@Deprecated -public interface FieldAdditionalEquations> { - - /** Get the name of the additional state. - * @return name of the additional state - */ - String getName(); - - /** - * Initialize the equations at the start of propagation. - * - *

        - * This method will be called once at propagation start, - * before any calls to {@link #computeDerivatives(FieldSpacecraftState , CalculusFieldElement[])}. - *

        - * - *

        - * The default implementation of this method does nothing. - *

        - * - * @param initialState initial state information at the start of propagation. - * @param target date of propagation. Not equal to {@code - * initialState.getDate()}. - */ - default void init(final FieldSpacecraftState initialState, final FieldAbsoluteDate target) { - // nothing by default - } - - /** Compute the derivatives related to the additional state parameters. - *

        - * When this method is called, the spacecraft state contains the main - * state (orbit, attitude and mass), all the states provided through - * the {@link org.orekit.propagation.AdditionalStateProvider additional - * state providers} registered to the propagator, and the additional state - * integrated using this equation. It does not contains any other - * states to be integrated alongside during the same propagation. - *

        - * @param s current state information: date, kinematics, attitude, and - * additional state - * @param pDot placeholder where the derivatives of the additional parameters - * should be put - * @return cumulative effect of the equations on the main state (may be null if - * equations do not change main state at all) - */ - T[] computeDerivatives(FieldSpacecraftState s, T[] pDot); - -} diff --git a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationsAdapter.java b/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationsAdapter.java deleted file mode 100644 index 9ae5c6fed9..0000000000 --- a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationsAdapter.java +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.integration; - -import java.util.function.Supplier; - -import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.MathArrays; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.time.FieldAbsoluteDate; - -/** Temporary adapter from {@link FieldAdditionalEquations} to {@link FieldAdditionalDerivativesProvider}. - * @since 11.1 - * @deprecated must be removed in 12.0 when {@link AdditionalEquations} is removed - */ -@Deprecated -public class FieldAdditionalEquationsAdapter> implements FieldAdditionalDerivativesProvider { - - /** Wrapped equations. */ - private final FieldAdditionalEquations equations; - - /** Supplier for reference state. */ - private final Supplier> stateSupplier; - - /** Dimension. */ - private int dimension; - - /** Simple constructor. - * @param equations wrapped equations - * @param stateSupplier supplier for reference state - */ - public FieldAdditionalEquationsAdapter(final FieldAdditionalEquations equations, final Supplier> stateSupplier) { - this.equations = equations; - this.stateSupplier = stateSupplier; - this.dimension = -1; - } - - /** {@inheritDoc} */ - @Override - public String getName() { - return equations.getName(); - } - - /** {@inheritDoc} */ - @Override - public int getDimension() { - if (dimension < 0) { - // retrieve the dimension the first time we need it - dimension = stateSupplier.get().getAdditionalState(getName()).length; - } - return dimension; - } - - /** {@inheritDoc} */ - @Override - public boolean yield(final FieldSpacecraftState state) { - return false; - } - - /** {@inheritDoc} */ - @Override - public void init(final FieldSpacecraftState initialState, final FieldAbsoluteDate target) { - equations.init(initialState, target); - } - - /** {@inheritDoc} */ - @Override - public T[] derivatives(final FieldSpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - - /** {@inheritDoc} */ - @Override - public FieldCombinedDerivatives combinedDerivatives(final FieldSpacecraftState state) { - final T[] pDot = MathArrays.buildArray(state.getDate().getField(), getDimension()); - final T[] mainDot = equations.computeDerivatives(state, pDot); - return new FieldCombinedDerivatives<>(pDot, mainDot); - } - -} diff --git a/src/main/java/org/orekit/propagation/integration/FieldCombinedDerivatives.java b/src/main/java/org/orekit/propagation/integration/FieldCombinedDerivatives.java index a474ad6982..4dee144381 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldCombinedDerivatives.java +++ b/src/main/java/org/orekit/propagation/integration/FieldCombinedDerivatives.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java b/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java index 93a352f9f1..20df769619 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java +++ b/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,13 +19,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import org.hipparchus.CalculusFieldElement; import org.hipparchus.ode.FieldDenseOutputModel; import org.hipparchus.ode.FieldODEStateAndDerivative; import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.FieldOrbit; @@ -68,6 +66,7 @@ * @author Mathieu Roméro * @author Luc Maisonobe * @author Véronique Pommier-Maurussane + * @param type of the field elements */ public class FieldIntegratedEphemeris > extends FieldAbstractAnalyticalPropagator implements FieldBoundedPropagator { @@ -111,61 +110,6 @@ public class FieldIntegratedEphemeris > */ private final int[] dimensions; - /** Creates a new instance of IntegratedEphemeris. - * @param startDate Start date of the integration (can be minDate or maxDate) - * @param minDate first date of the range - * @param maxDate last date of the range - * @param mapper mapper between raw double components and spacecraft state - * @param type type of orbit to output (mean or osculating) - * @param model underlying raw mathematical model - * @param unmanaged unmanaged additional states that must be simply copied - * @param providers providers for pre-integrated states - * @param equations names of additional equations - * @deprecated as of 11.2, replaced by {@link #FieldIntegratedEphemeris(FieldAbsoluteDate, - * FieldAbsoluteDate, FieldAbsoluteDate, FieldStateMapper, PropagationType, - * FieldDenseOutputModel, FieldArrayDictionary, List, String[], int[])} - */ - @Deprecated - public FieldIntegratedEphemeris(final FieldAbsoluteDate startDate, - final FieldAbsoluteDate minDate, final FieldAbsoluteDate maxDate, - final FieldStateMapper mapper, final PropagationType type, - final FieldDenseOutputModel model, - final Map unmanaged, - final List> providers, - final String[] equations) { - this(startDate, minDate, maxDate, mapper, type, model, - new FieldArrayDictionary<>(startDate.getField(), unmanaged), - providers, equations); - } - - /** Creates a new instance of IntegratedEphemeris. - * @param startDate Start date of the integration (can be minDate or maxDate) - * @param minDate first date of the range - * @param maxDate last date of the range - * @param mapper mapper between raw double components and spacecraft state - * @param type type of orbit to output (mean or osculating) - * @param model underlying raw mathematical model - * @param unmanaged unmanaged additional states that must be simply copied - * @param providers generators for pre-integrated states - * @param equations names of additional equations - * @since 11.1 - * @deprecated as of 11.2, replaced by {@link #FieldIntegratedEphemeris(FieldAbsoluteDate, - * FieldAbsoluteDate, FieldAbsoluteDate, FieldStateMapper, PropagationType, - * FieldDenseOutputModel, FieldArrayDictionary, List, String[], int[])} - */ - @Deprecated - public FieldIntegratedEphemeris(final FieldAbsoluteDate startDate, - final FieldAbsoluteDate minDate, final FieldAbsoluteDate maxDate, - final FieldStateMapper mapper, final PropagationType type, - final FieldDenseOutputModel model, - final FieldArrayDictionary unmanaged, - final List> providers, - final String[] equations) { - this(startDate, minDate, maxDate, mapper, type, model, - unmanaged, providers, equations, - remainingDimensions(model, unmanaged, providers, equations)); - } - /** Creates a new instance of IntegratedEphemeris. * @param startDate Start date of the integration (can be minDate or maxDate) * @param minDate first date of the range @@ -205,33 +149,9 @@ public FieldIntegratedEphemeris(final FieldAbsoluteDate startDate, this.equations = equations.clone(); this.dimensions = dimensions.clone(); - } + // set up initial state + super.resetInitialState(getInitialState()); - /** Compute remaining dimensions for additional equations. - * @param tupe of the field elements - * @param model underlying raw mathematical model - * @param unmanaged unmanaged additional states that must be simply copied - * @param providers providers for pre-integrated states - * @param equations names of additional equations - * @return dimensions of additional equations - * @deprecated as of 11.2 this method is temporary and should be removed - * when the calling constructors are removed - * @since 11.2 - */ - @Deprecated - private static > int[] remainingDimensions(final FieldDenseOutputModel model, - final FieldArrayDictionary unmanaged, - final List> providers, - final String[] equations) { - final FieldODEStateAndDerivative osd = model.getInterpolatedState(model.getInitialTime()); - if (equations.length != osd.getNumberOfSecondaryStates()) { - throw new OrekitInternalError(null); - } - final int[] dimensions = new int[equations.length]; - for (int i = 0; i < dimensions.length; ++i) { - dimensions[i] = osd.getSecondaryStateDimension(i + 1); - } - return dimensions; } /** Interpolate the model at some date. @@ -355,7 +275,7 @@ protected FieldSpacecraftState updateAdditionalStates(final FieldSpacecraftSt /** {@inheritDoc} */ @Override - protected List getParametersDrivers() { + public List getParametersDrivers() { // Integrated Ephemeris propagation model does not have parameter drivers. return Collections.emptyList(); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java b/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java index a9a12060f9..78c7cb04d7 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java +++ b/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,13 +20,14 @@ import org.orekit.attitudes.AttitudeProvider; import org.orekit.frames.Frame; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.time.FieldAbsoluteDate; /** This class maps between raw double elements and {@link FieldSpacecraftState} instances. * @author Luc Maisonobe + * @param type of the field elements */ public abstract class FieldStateMapper> { @@ -37,7 +38,7 @@ public abstract class FieldStateMapper> { private final OrbitType orbitType; /** Position angle type. */ - private final PositionAngle angleType; + private final PositionAngleType angleType; /** Attitude provider. */ private final AttitudeProvider attitudeProvider; @@ -63,7 +64,7 @@ public abstract class FieldStateMapper> { * @param frame inertial frame */ protected FieldStateMapper(final FieldAbsoluteDate referenceDate, final T mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { this.referenceDate = referenceDate; this.mu = mu; @@ -95,7 +96,7 @@ public void setPositionAngleType() { /** Get propagation parameter type. * @return angle type to use for propagation */ - public PositionAngle getPositionAngleType() { + public PositionAngleType getPositionAngleType() { return angleType; } diff --git a/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java b/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java index 825ea90e92..f3b9f8e02a 100644 --- a/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java +++ b/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,13 +18,11 @@ import java.util.Arrays; import java.util.List; -import java.util.Map; import org.hipparchus.ode.DenseOutputModel; import org.hipparchus.ode.ODEStateAndDerivative; import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; @@ -109,60 +107,6 @@ public class IntegratedEphemeris */ private final int[] dimensions; - /** Creates a new instance of IntegratedEphemeris. - * @param startDate Start date of the integration (can be minDate or maxDate) - * @param minDate first date of the range - * @param maxDate last date of the range - * @param mapper mapper between raw double components and spacecraft state - * @param type type of orbit to output (mean or osculating) - * @param model underlying raw mathematical model - * @param unmanaged unmanaged additional states that must be simply copied - * @param providers providers for pre-integrated states - * @param equations names of additional equations - * @deprecated as of 11.1.2, replaced by {@link #IntegratedEphemeris(AbsoluteDate, - * AbsoluteDate, AbsoluteDate, StateMapper, PropagationType, DenseOutputModel, - * DoubleArrayDictionary, List, String[], int[])} - */ - @Deprecated - public IntegratedEphemeris(final AbsoluteDate startDate, - final AbsoluteDate minDate, final AbsoluteDate maxDate, - final StateMapper mapper, final PropagationType type, - final DenseOutputModel model, - final Map unmanaged, - final List providers, - final String[] equations) { - this(startDate, minDate, maxDate, mapper, type, model, - new DoubleArrayDictionary(unmanaged), providers, equations); - } - - /** Creates a new instance of IntegratedEphemeris. - * @param startDate Start date of the integration (can be minDate or maxDate) - * @param minDate first date of the range - * @param maxDate last date of the range - * @param mapper mapper between raw double components and spacecraft state - * @param type type of orbit to output (mean or osculating) - * @param model underlying raw mathematical model - * @param unmanaged unmanaged additional states that must be simply copied - * @param providers providers for pre-integrated states - * @param equations names of additional equations - * @since 11.1 - * @deprecated as of 11.1.2, replaced by {@link #IntegratedEphemeris(AbsoluteDate, - * AbsoluteDate, AbsoluteDate, StateMapper, PropagationType, DenseOutputModel, - * DoubleArrayDictionary, List, String[], int[])} - */ - @Deprecated - public IntegratedEphemeris(final AbsoluteDate startDate, - final AbsoluteDate minDate, final AbsoluteDate maxDate, - final StateMapper mapper, final PropagationType type, - final DenseOutputModel model, - final DoubleArrayDictionary unmanaged, - final List providers, - final String[] equations) { - this(startDate, minDate, maxDate, mapper, type, model, - unmanaged, providers, equations, - remainingDimensions(model, unmanaged, providers, equations)); - } - /** Creates a new instance of IntegratedEphemeris. * @param startDate Start date of the integration (can be minDate or maxDate) * @param minDate first date of the range @@ -202,32 +146,9 @@ public IntegratedEphemeris(final AbsoluteDate startDate, this.equations = equations.clone(); this.dimensions = dimensions.clone(); - } + // set up initial state + super.resetInitialState(getInitialState()); - /** Compute remaining dimensions for additional equations. - * @param model underlying raw mathematical model - * @param unmanaged unmanaged additional states that must be simply copied - * @param providers providers for pre-integrated states - * @param equations names of additional equations - * @return dimensions of additional equations - * @deprecated as of 11.1.2 this method is temporary and should be removed - * when the calling constructors are removed - * @since 11.1.2 - */ - @Deprecated - private static int[] remainingDimensions(final DenseOutputModel model, - final DoubleArrayDictionary unmanaged, - final List providers, - final String[] equations) { - final ODEStateAndDerivative osd = model.getInterpolatedState(model.getInitialTime()); - if (equations.length != osd.getNumberOfSecondaryStates()) { - throw new OrekitInternalError(null); - } - final int[] dimensions = new int[equations.length]; - for (int i = 0; i < dimensions.length; ++i) { - dimensions[i] = osd.getSecondaryStateDimension(i + 1); - } - return dimensions; } /** Interpolate the model at some date. diff --git a/src/main/java/org/orekit/propagation/integration/StateMapper.java b/src/main/java/org/orekit/propagation/integration/StateMapper.java index 360e789804..8ead080d0f 100644 --- a/src/main/java/org/orekit/propagation/integration/StateMapper.java +++ b/src/main/java/org/orekit/propagation/integration/StateMapper.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import org.orekit.attitudes.AttitudeProvider; import org.orekit.frames.Frame; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -37,7 +37,7 @@ public abstract class StateMapper { private final OrbitType orbitType; /** Position angle type. */ - private final PositionAngle angleType; + private final PositionAngleType angleType; /** Central attraction coefficient. */ private final double mu; @@ -65,7 +65,7 @@ public abstract class StateMapper { * @param frame inertial frame */ protected StateMapper(final AbsoluteDate referenceDate, final double mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { this.referenceDate = referenceDate; this.mu = mu; @@ -92,7 +92,7 @@ public OrbitType getOrbitType() { /** Get propagation parameter type. * @return angle type to use for propagation */ - public PositionAngle getPositionAngleType() { + public PositionAngleType getPositionAngleType() { return angleType; } diff --git a/src/main/java/org/orekit/propagation/integration/package-info.java b/src/main/java/org/orekit/propagation/integration/package-info.java index 8b37188e2d..b14352abd2 100644 --- a/src/main/java/org/orekit/propagation/integration/package-info.java +++ b/src/main/java/org/orekit/propagation/integration/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/numerical/AbsoluteJacobiansMapper.java b/src/main/java/org/orekit/propagation/numerical/AbsoluteJacobiansMapper.java deleted file mode 100644 index f0a91acafb..0000000000 --- a/src/main/java/org/orekit/propagation/numerical/AbsoluteJacobiansMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.numerical; - -import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.ParameterDriversList; - -/** Mapper between two-dimensional Jacobian matrices and one-dimensional {@link - * SpacecraftState#getAdditionalState(String) additional state arrays}. - *

        - * This class does not hold the states by itself. Instances of this class are guaranteed - * to be immutable. - *

        - * @author Vincent Mouraux - * @see org.orekit.propagation.numerical.NumericalPropagator - * @see SpacecraftState#getAdditionalState(String) - * @see org.orekit.propagation.AbstractPropagator - * @since 10.2 - */ -public class AbsoluteJacobiansMapper extends JacobiansMapper { - - /** State dimension, fixed to 6. */ - public static final int STATE_DIMENSION = 6; - - /** Simple constructor. - * @param name name of the State Transition Matrix additional state - * @param parameters selected parameters for Jacobian computation - */ - public AbsoluteJacobiansMapper(final String name, final ParameterDriversList parameters) { - // orbit type and angle type are not used here - super(name, parameters, null, null); - } - - /** {@inheritDoc} */ - @Override - protected double[][] getConversionJacobian(final SpacecraftState state) { - - final double[][] identity = new double[STATE_DIMENSION][STATE_DIMENSION]; - - for (int i = 0; i < STATE_DIMENSION; ++i) { - identity[i][i] = 1.0; - } - - return identity; - - } - -} diff --git a/src/main/java/org/orekit/propagation/numerical/AbsolutePartialDerivativesEquations.java b/src/main/java/org/orekit/propagation/numerical/AbsolutePartialDerivativesEquations.java deleted file mode 100644 index c7b31370e4..0000000000 --- a/src/main/java/org/orekit/propagation/numerical/AbsolutePartialDerivativesEquations.java +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.numerical; - -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.forces.ForceModel; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.integration.AdditionalDerivativesProvider; -import org.orekit.utils.ParameterDriver; - -/** {@link AdditionalDerivativesProvider derivatives provider} computing the partial derivatives - * of the state (orbit) with respect to initial state and force models parameters. - *

        - * This set of equations are automatically added to a {@link NumericalPropagator numerical propagator} - * in order to compute partial derivatives of the orbit along with the orbit itself. This is - * useful for example in orbit determination applications. - *

        - *

        - * The partial derivatives with respect to initial state can be either dimension 6 - * (orbit only) or 7 (orbit and mass). - *

        - *

        - * The partial derivatives with respect to force models parameters has a dimension - * equal to the number of selected parameters. Parameters selection is implemented at - * {@link ForceModel force models} level. Users must retrieve a {@link ParameterDriver - * parameter driver} using {@link ForceModel#getParameterDriver(String)} and then - * select it by calling {@link ParameterDriver#setSelected(boolean) setSelected(true)}. - *

        - *

        - * If several force models provide different {@link ParameterDriver drivers} for the - * same parameter name, selecting any of these drivers has the side effect of - * selecting all the drivers for this shared parameter. In this case, the partial - * derivatives will be the sum of the partial derivatives contributed by the - * corresponding force models. This case typically arises for central attraction - * coefficient, which has an influence on {@link org.orekit.forces.gravity.NewtonianAttraction - * Newtonian attraction}, {@link org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel - * gravity field}, and {@link org.orekit.forces.gravity.Relativity relativity}. - *

        - * @deprecated as of 11.1, this class is not used anymore - * @author Vincent Mouraux - * @author Bryan Cazabonne - * @since 10.2 - */ -@Deprecated -public class AbsolutePartialDerivativesEquations extends PartialDerivativesEquations { - - /** Name. */ - private final String name; - - /** Simple constructor. - *

        - * Upon construction, this set of equations is automatically added to - * the propagator by calling its {@link - * NumericalPropagator#addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} method. So - * there is no need to call this method explicitly for these equations. - *

        - * @param name name of the partial derivatives equations - * @param propagator the propagator that will handle the orbit propagation - */ - public AbsolutePartialDerivativesEquations(final String name, final NumericalPropagator propagator) { - super(name, propagator); - this.name = name; - } - - /** Get a mapper between two-dimensional Jacobians and one-dimensional additional state. - * @return a mapper between two-dimensional Jacobians and one-dimensional additional state, - * with the same name as the instance - * @see #setInitialJacobians(SpacecraftState) - * @see #setInitialJacobians(SpacecraftState, double[][], double[][]) - */ - @Override - public AbsoluteJacobiansMapper getMapper() { - if (!isInitialize()) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_INITIALIZED); - } - return new AbsoluteJacobiansMapper(name, getSelectedParameters()); - } - -} - diff --git a/src/main/java/org/orekit/propagation/numerical/EpochDerivativesEquations.java b/src/main/java/org/orekit/propagation/numerical/EpochDerivativesEquations.java index bade437a76..e1ff1d9864 100644 --- a/src/main/java/org/orekit/propagation/numerical/EpochDerivativesEquations.java +++ b/src/main/java/org/orekit/propagation/numerical/EpochDerivativesEquations.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,11 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.linear.Array2DRowRealMatrix; +import org.hipparchus.linear.DecompositionSolver; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.QRDecomposition; +import org.hipparchus.linear.RealMatrix; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.forces.ForceModel; @@ -28,15 +33,12 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AdditionalDerivativesProvider; -import org.orekit.propagation.integration.AdditionalEquations; import org.orekit.propagation.integration.CombinedDerivatives; -import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; +import org.orekit.utils.TimeSpanMap.Span; -/** This class is a copy of {@link AbsolutePartialDerivativesEquations} - * The computation of the derivatives of the acceleration due to a ThirdBodyAttraction - * has been added. +/** Computes derivatives of the acceleration, including ThirdBodyAttraction. * * {@link AdditionalDerivativesProvider Provider} computing the partial derivatives * of the state (orbit) with respect to initial state and force models parameters. @@ -70,10 +72,11 @@ * @author Luc Maisonobe * @since 10.2 */ -@SuppressWarnings("deprecation") public class EpochDerivativesEquations - implements AdditionalDerivativesProvider, - org.orekit.propagation.integration.AdditionalEquations { + implements AdditionalDerivativesProvider { + + /** State dimension, fixed to 6. */ + public static final int STATE_DIMENSION = 6; /** Propagator computing state evolution. */ private final NumericalPropagator propagator; @@ -82,19 +85,16 @@ public class EpochDerivativesEquations private ParameterDriversList selected; /** Parameters map. */ - private Map map; + private Map map; /** Name. */ private final String name; - /** Flag for Jacobian matrices initialization. */ - private boolean initialized; - /** Simple constructor. *

        * Upon construction, this set of equations is automatically added to * the propagator by calling its {@link - * NumericalPropagator#addAdditionalEquations(AdditionalEquations)} method. So + * NumericalPropagator#addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} method. So * there is no need to call this method explicitly for these equations. *

        * @param name name of the partial derivatives equations @@ -105,7 +105,6 @@ public EpochDerivativesEquations(final String name, final NumericalPropagator pr this.selected = null; this.map = null; this.propagator = propagator; - this.initialized = false; propagator.addAdditionalDerivativesProvider(this); } @@ -144,34 +143,24 @@ private void freezeParametersSelection() { // fourth pass: set up a map between parameters drivers and matrices columns map = new IdentityHashMap<>(); int parameterIndex = 0; + int previousParameterIndex = 0; for (final ParameterDriver selectedDriver : selected.getDrivers()) { for (final ForceModel provider : propagator.getAllForceModels()) { for (final ParameterDriver driver : provider.getParametersDrivers()) { if (driver.getName().equals(selectedDriver.getName())) { - map.put(driver, parameterIndex); + previousParameterIndex = parameterIndex; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + map.put(span.getData(), previousParameterIndex++); + } } } } - ++parameterIndex; + parameterIndex = previousParameterIndex; } } } - /** Get the selected parameters, in Jacobian matrix column order. - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, so the proper list is returned. - *

        - * @return selected parameters, in Jacobian matrix column order which - * is lexicographic order - */ - public ParameterDriversList getSelectedParameters() { - freezeParametersSelection(); - return selected; - } - /** Set the initial value of the Jacobian with respect to state and parameter. *

        * This method is equivalent to call {@link #setInitialJacobians(SpacecraftState, @@ -185,14 +174,12 @@ public ParameterDriversList getSelectedParameters() { *

        * @param s0 initial state * @return state with initial Jacobians added - * @see #getSelectedParameters() - * @since 9.0 */ public SpacecraftState setInitialJacobians(final SpacecraftState s0) { freezeParametersSelection(); final int epochStateDimension = 6; final double[][] dYdY0 = new double[epochStateDimension][epochStateDimension]; - final double[][] dYdP = new double[epochStateDimension][selected.getNbParams() + 6]; + final double[][] dYdP = new double[epochStateDimension][selected.getNbValuesToEstimate() + 6]; for (int i = 0; i < epochStateDimension; ++i) { dYdY0[i][i] = 1.0; } @@ -216,7 +203,6 @@ public SpacecraftState setInitialJacobians(final SpacecraftState s0) { * @param dY1dP Jacobian of current state at time t₁ with respect * to parameters (may be null if no parameters are selected) * @return state with initial Jacobians added - * @see #getSelectedParameters() */ public SpacecraftState setInitialJacobians(final SpacecraftState s1, final double[][] dY1dY0, final double[][] dY1dP) { @@ -235,54 +221,63 @@ public SpacecraftState setInitialJacobians(final SpacecraftState s1, } // store the matrices as a single dimension array - initialized = true; - final AbsoluteJacobiansMapper absoluteMapper = getMapper(); - final double[] p = new double[absoluteMapper.getAdditionalStateDimension() + 6]; - absoluteMapper.setInitialJacobians(s1, dY1dY0, dY1dP, p); + final double[] p = new double[STATE_DIMENSION * (STATE_DIMENSION + selected.getNbValuesToEstimate()) + 6]; + setInitialJacobians(s1, dY1dY0, dY1dP, p); // set value in propagator return s1.addAdditionalState(name, p); } - /** Get a mapper between two-dimensional Jacobians and one-dimensional additional state. - * @return a mapper between two-dimensional Jacobians and one-dimensional additional state, - * with the same name as the instance - * @see #setInitialJacobians(SpacecraftState) - * @see #setInitialJacobians(SpacecraftState, double[][], double[][]) + /** Set the Jacobian with respect to state into a one-dimensional additional state array. + *

        + * This method converts the Jacobians to Cartesian parameters and put the converted data + * in the one-dimensional {@code p} array. + *

        + * @param state spacecraft state + * @param dY1dY0 Jacobian of current state at time t₁ + * with respect to state at some previous time t₀ + * @param dY1dP Jacobian of current state at time t₁ + * with respect to parameters (may be null if there are no parameters) + * @param p placeholder where to put the one-dimensional additional state */ - public AbsoluteJacobiansMapper getMapper() { - if (!initialized) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_INITIALIZED); + public void setInitialJacobians(final SpacecraftState state, final double[][] dY1dY0, + final double[][] dY1dP, final double[] p) { + + // set up a converter + final RealMatrix dY1dC1 = MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION); + final DecompositionSolver solver = new QRDecomposition(dY1dC1).getSolver(); + + // convert the provided state Jacobian + final RealMatrix dC1dY0 = solver.solve(new Array2DRowRealMatrix(dY1dY0, false)); + + // map the converted state Jacobian to one-dimensional array + int index = 0; + for (int i = 0; i < STATE_DIMENSION; ++i) { + for (int j = 0; j < STATE_DIMENSION; ++j) { + p[index++] = dC1dY0.getEntry(i, j); + } } - return new AbsoluteJacobiansMapper(name, selected); - } - /** {@inheritDoc} */ - public void init(final SpacecraftState initialState, final AbsoluteDate target) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - AdditionalDerivativesProvider.super.init(initialState, target); - } + if (selected.getNbValuesToEstimate() != 0) { + // convert the provided state Jacobian + final RealMatrix dC1dP = solver.solve(new Array2DRowRealMatrix(dY1dP, false)); - /** {@inheritDoc} */ - public double[] computeDerivatives(final SpacecraftState s, final double[] pDot) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - System.arraycopy(derivatives(s), 0, pDot, 0, pDot.length); - return null; - } + // map the converted parameters Jacobian to one-dimensional array + for (int i = 0; i < STATE_DIMENSION; ++i) { + for (int j = 0; j < selected.getNbValuesToEstimate(); ++j) { + p[index++] = dC1dP.getEntry(i, j); + } + } + } - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); } /** {@inheritDoc} */ public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { // initialize acceleration Jacobians to zero - final int paramDimEpoch = selected.getNbParams() + 1; // added epoch + final int paramDimEpoch = selected.getNbValuesToEstimate() + 1; // added epoch final int dimEpoch = 3; final double[][] dAccdParam = new double[dimEpoch][paramDimEpoch]; final double[][] dAccdPos = new double[dimEpoch][dimEpoch]; @@ -295,7 +290,7 @@ public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { for (final ForceModel forceModel : propagator.getAllForceModels()) { final NumericalGradientConverter converter = forceModel.dependsOnPositionOnly() ? posOnlyConverter : fullConverter; final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] parameters = converter.getParameters(dsState, forceModel); + final Gradient[] parameters = converter.getParametersAtStateDate(dsState, forceModel); final FieldVector3D acceleration = forceModel.acceleration(dsState, parameters); final double[] derivativesX = acceleration.getX().getGradient(); @@ -310,11 +305,13 @@ public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { int index = converter.getFreeStateParameters(); for (ParameterDriver driver : forceModel.getParametersDrivers()) { if (driver.isSelected()) { - final int parameterIndex = map.get(driver); - dAccdParam[0][parameterIndex] += derivativesX[index]; - dAccdParam[1][parameterIndex] += derivativesY[index]; - dAccdParam[2][parameterIndex] += derivativesZ[index]; - ++index; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final int parameterIndex = map.get(span.getData()); + dAccdParam[0][parameterIndex] += derivativesX[index]; + dAccdParam[1][parameterIndex] += derivativesY[index]; + dAccdParam[2][parameterIndex] += derivativesZ[index]; + ++index; + } } } diff --git a/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java b/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java index 5f2116ddb3..66208e8dd3 100644 --- a/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java +++ b/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -39,18 +39,20 @@ import org.orekit.frames.Frame; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.integration.FieldAbstractIntegratedPropagator; import org.orekit.propagation.integration.FieldStateMapper; +import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldAbsolutePVCoordinates; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeStampedFieldPVCoordinates; /** This class propagates {@link org.orekit.orbits.FieldOrbit orbits} using @@ -71,12 +73,12 @@ * {@link #removeForceModels()})
      65. *
      66. the {@link OrbitType type} of orbital parameters to be used for propagation * ({@link #setOrbitType(OrbitType)}), - *
      67. the {@link PositionAngle type} of position angle to be used in orbital parameters + *
      68. the {@link PositionAngleType type} of position angle to be used in orbital parameters * to be used for propagation where it is relevant ({@link - * #setPositionAngleType(PositionAngle)}), - *
      69. whether {@link org.orekit.propagation.integration.FieldAdditionalEquations additional equations} + * #setPositionAngleType(PositionAngleType)}), + *
      70. whether {@link org.orekit.propagation.integration.FieldAdditionalDerivativesProvider additional derivatives providers} * should be propagated along with orbital state - * ({@link #addAdditionalEquations(org.orekit.propagation.integration.FieldAdditionalEquations)}), + * ({@link #addAdditionalDerivativesProvider(org.orekit.propagation.integration.FieldAdditionalDerivativesProvider)}), *
      71. the discrete events that should be triggered during propagation * ({@link #addEventDetector(FieldEventDetector)}, * {@link #clearEventsDetectors()})
      72. @@ -84,7 +86,7 @@ * *

        From these configuration parameters, only the initial state is mandatory. The default * propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial} parameters with - * {@link PositionAngle#TRUE true} longitude argument. If the central attraction coefficient + * {@link PositionAngleType#TRUE true} longitude argument. If the central attraction coefficient * is not explicitly specified, the one used to define the initial orbit will be used. * However, specifying only the initial state and perhaps the central attraction coefficient * would mean the propagator would use only Keplerian forces. In this case, the simpler {@link @@ -141,6 +143,7 @@ * @author Guylaine Prat * @author Fabien Maussion * @author Véronique Pommier-Maurussane + * @param type of the field elements */ public class FieldNumericalPropagator> extends FieldAbstractIntegratedPropagator { @@ -160,8 +163,8 @@ public class FieldNumericalPropagator> extends * called after creation, the integrated orbit will follow a Keplerian * evolution only. The defaults are {@link OrbitType#EQUINOCTIAL} * for {@link #setOrbitType(OrbitType) propagation - * orbit type} and {@link PositionAngle#TRUE} for {@link - * #setPositionAngleType(PositionAngle) position angle type}. + * orbit type} and {@link PositionAngleType#TRUE} for {@link + * #setPositionAngleType(PositionAngleType) position angle type}. * *

        This constructor uses the {@link DataContext#getDefault() default data context}. * @@ -181,8 +184,8 @@ public FieldNumericalPropagator(final Field field, final FieldODEIntegrator field, setMu(field.getZero().add(Double.NaN)); clearStepHandlers(); setOrbitType(OrbitType.EQUINOCTIAL); - setPositionAngleType(PositionAngle.TRUE); + setPositionAngleType(PositionAngleType.TRUE); } /** Set the flag to ignore or not the creation of a {@link NewtonianAttraction}. @@ -263,7 +266,14 @@ public void addForceModel(final ForceModel model) { model.getParametersDrivers().get(0).addObserver(new ParameterObserver() { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { + // mu PDriver should have only 1 span + superSetMu(field.getZero().add(driver.getValue(date))); + } + /** {@inheritDoc} */ + @Override + public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { + // mu PDriver should have only 1 span superSetMu(field.getZero().add(driver.getValue())); } }); @@ -344,14 +354,14 @@ private OrbitType superGetOrbitType() { *

        * @param positionAngleType angle type to use for propagation */ - public void setPositionAngleType(final PositionAngle positionAngleType) { + public void setPositionAngleType(final PositionAngleType positionAngleType) { super.setPositionAngleType(positionAngleType); } /** Get propagation parameter type. * @return angle type to use for propagation */ - public PositionAngle getPositionAngleType() { + public PositionAngleType getPositionAngleType() { return super.getPositionAngleType(); } @@ -378,7 +388,7 @@ public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate /** {@inheritDoc} */ protected FieldStateMapper createMapper(final FieldAbsoluteDate referenceDate, final T mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { return new FieldOsculatingMapper(referenceDate, mu, orbitType, positionAngleType, attitudeProvider, frame); } @@ -401,7 +411,7 @@ private class FieldOsculatingMapper extends FieldStateMapper { * @param frame inertial frame */ FieldOsculatingMapper(final FieldAbsoluteDate referenceDate, final T mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { super(referenceDate, mu, orbitType, positionAngleType, attitudeProvider, frame); } @@ -413,7 +423,7 @@ public FieldSpacecraftState mapArrayToState(final FieldAbsoluteDate date, final T mass = y[6]; if (mass.getReal() <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, mass); + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass); } if (superGetOrbitType() == null) { @@ -486,7 +496,7 @@ private class Main implements MainStateEquations, FieldTimeDerivativesEquatio this.yDot = MathArrays.buildArray(getField(), 7); this.jacobian = MathArrays.buildArray(getField(), 6, 6); for (final ForceModel forceModel : forceModels) { - forceModel.getFieldEventsDetectors(getField()).forEach(detector -> setUpEventDetector(integrator, detector)); + forceModel.getFieldEventDetectors(getField()).forEach(detector -> setUpEventDetector(integrator, detector)); } if (superGetOrbitType() == null) { @@ -545,7 +555,7 @@ public void addKeplerContribution(final T mu) { // if mu is neither 0 nor NaN, we want to include Newtonian acceleration if (mu.getReal() > 0) { // velocity derivative is Newtonian acceleration - final FieldVector3D position = currentState.getPVCoordinates().getPosition(); + final FieldVector3D position = currentState.getPosition(); final T r2 = position.getNormSq(); final T coeff = r2.multiply(r2.sqrt()).reciprocal().negate().multiply(mu); yDot[3] = yDot[3].add(coeff.multiply(position.getX())); @@ -661,7 +671,7 @@ public static > double[][] tolerances(final T // convert the orbit to the desired type final T[][] jacobian = MathArrays.buildArray(dP.getField(), 6, 6); final FieldOrbit converted = type.convertType(orbit); - converted.getJacobianWrtCartesian(PositionAngle.TRUE, jacobian); + converted.getJacobianWrtCartesian(PositionAngleType.TRUE, jacobian); for (int i = 0; i < 6; ++i) { final T[] row = jacobian[i]; @@ -679,7 +689,7 @@ public static > double[][] tolerances(final T } - Arrays.fill(relTol, dP.divide(orbit.getPVCoordinates().getPosition().getNormSq().sqrt()).getReal()); + Arrays.fill(relTol, dP.divide(orbit.getPosition().getNormSq().sqrt()).getReal()); return new double[][] { absTol, relTol }; diff --git a/src/main/java/org/orekit/propagation/numerical/FieldTimeDerivativesEquations.java b/src/main/java/org/orekit/propagation/numerical/FieldTimeDerivativesEquations.java index 5737351ca4..d18a2d6aa8 100644 --- a/src/main/java/org/orekit/propagation/numerical/FieldTimeDerivativesEquations.java +++ b/src/main/java/org/orekit/propagation/numerical/FieldTimeDerivativesEquations.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,6 +34,7 @@ * @author Luc Maisonobe * @author Fabien Maussion * @author Véronique Pommier-Maurussane + * @param type of the field elements */ public interface FieldTimeDerivativesEquations> { diff --git a/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java b/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java index 21f8acd2ba..1f8cb4a71c 100644 --- a/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java +++ b/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,7 +33,7 @@ import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.gnss.data.GLONASSAlmanac; @@ -182,7 +182,7 @@ public GLONASSNumericalPropagator(final ClassicalRungeKuttaIntegrator integrator setAttitudeProvider(provider); setOrbitType(OrbitType.CARTESIAN); // It is not meaningful for propagation in Cartesian parameters - setPositionAngleType(PositionAngle.TRUE); + setPositionAngleType(PositionAngleType.TRUE); setMu(GNSSConstants.GLONASS_MU); // As recommended by GLONASS ICD (2016), the direction cosines and distance @@ -575,7 +575,7 @@ private PVCoordinates getPVInInertial(final GLONASSDate date) { @Override protected StateMapper createMapper(final AbsoluteDate referenceDate, final double mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { return new Mapper(referenceDate, mu, orbitType, positionAngleType, attitudeProvider, frame); } @@ -594,7 +594,7 @@ private static class Mapper extends StateMapper { * @param frame inertial frame */ Mapper(final AbsoluteDate referenceDate, final double mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { super(referenceDate, mu, orbitType, positionAngleType, attitudeProvider, frame); } @@ -605,7 +605,7 @@ public SpacecraftState mapArrayToState(final AbsoluteDate date, final double[] y // The parameter meanOnly is ignored for the GLONASS Propagator final double mass = y[6]; if (mass <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, mass); + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass); } final Orbit orbit = getOrbitType().mapArrayToOrbit(y, yDot, getPositionAngleType(), date, getMu(), getFrame()); @@ -651,7 +651,7 @@ public double[] computeDerivatives(final SpacecraftState state) { // Position and Velocity vectors final Vector3D vel = state.getPVCoordinates().getVelocity(); - final Vector3D pos = state.getPVCoordinates().getPosition(); + final Vector3D pos = state.getPosition(); Arrays.fill(yDot, 0.0); diff --git a/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagatorBuilder.java b/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagatorBuilder.java index bb21cf590f..2ef5c7a9c4 100644 --- a/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagatorBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.InertialProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.data.DataContext; import org.orekit.frames.Frame; import org.orekit.propagation.Propagator; @@ -127,7 +127,7 @@ public GLONASSNumericalPropagatorBuilder(final ClassicalRungeKuttaIntegrator int this.mass = Propagator.DEFAULT_MASS; this.dataContext = context; this.eci = dataContext.getFrames().getEME2000(); - this.attitudeProvider = InertialProvider.of(this.eci); + this.attitudeProvider = FrameAlignedProvider.of(this.eci); } /** diff --git a/src/main/java/org/orekit/propagation/numerical/IntegrableJacobianColumnGenerator.java b/src/main/java/org/orekit/propagation/numerical/IntegrableJacobianColumnGenerator.java index c7bf643762..969edba9f9 100644 --- a/src/main/java/org/orekit/propagation/numerical/IntegrableJacobianColumnGenerator.java +++ b/src/main/java/org/orekit/propagation/numerical/IntegrableJacobianColumnGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -78,7 +78,7 @@ public int getDimension() { *

        */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !state.hasAdditionalStateDerivative(stmName); } @@ -95,13 +95,6 @@ public void partialsComputed(final SpacecraftState state, final double[] factor, pDot[5] += accelerationPartials[2]; } - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - /** {@inheritDoc} */ @Override public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { diff --git a/src/main/java/org/orekit/propagation/numerical/JacobiansMapper.java b/src/main/java/org/orekit/propagation/numerical/JacobiansMapper.java deleted file mode 100644 index fe5d62406a..0000000000 --- a/src/main/java/org/orekit/propagation/numerical/JacobiansMapper.java +++ /dev/null @@ -1,192 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.numerical; - -import org.hipparchus.linear.Array2DRowRealMatrix; -import org.hipparchus.linear.DecompositionSolver; -import org.hipparchus.linear.QRDecomposition; -import org.hipparchus.linear.RealMatrix; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.integration.AbstractJacobiansMapper; -import org.orekit.utils.ParameterDriversList; - -/** Mapper between two-dimensional Jacobian matrices and one-dimensional {@link - * SpacecraftState#getAdditionalState(String) additional state arrays}. - *

        - * This class does not hold the states by itself. Instances of this class are guaranteed - * to be immutable. - *

        - * @author Luc Maisonobe - * @see org.orekit.propagation.numerical.NumericalPropagator - * @see SpacecraftState#getAdditionalState(String) - * @see org.orekit.propagation.AbstractPropagator - */ -public class JacobiansMapper extends AbstractJacobiansMapper { - - /** State dimension, fixed to 6. - * @since 9.0 - */ - public static final int STATE_DIMENSION = 6; - - /** Name. */ - private String name; - - /** Orbit type. */ - private final OrbitType orbitType; - - /** Position angle type. */ - private final PositionAngle angleType; - - /** Simple constructor. - * @param name name of the Jacobians - * @param parameters selected parameters for Jacobian computation - * @param orbitType orbit type - * @param angleType position angle type - */ - JacobiansMapper(final String name, final ParameterDriversList parameters, - final OrbitType orbitType, final PositionAngle angleType) { - super(name, parameters); - this.orbitType = orbitType; - this.angleType = angleType; - this.name = name; - } - - /** Get the conversion Jacobian between state parameters and parameters used for derivatives. - *

        - * For DSST and TLE propagators, state parameters and parameters used for derivatives are the same, - * so the Jacobian is simply the identity. - *

        - *

        - * For Numerical propagator, parameters used for derivatives are cartesian - * and they can be different from state parameters because the numerical propagator can accept different type - * of orbits. - *

        - * @param state spacecraft state - * @return conversion Jacobian - */ - protected double[][] getConversionJacobian(final SpacecraftState state) { - - final double[][] dYdC = new double[STATE_DIMENSION][STATE_DIMENSION]; - - // make sure the state is in the desired orbit type - final Orbit orbit = orbitType.convertType(state.getOrbit()); - - // compute the Jacobian, taking the position angle type into account - orbit.getJacobianWrtCartesian(angleType, dYdC); - - return dYdC; - - } - - /** {@inheritDoc} - *

        - * This method converts the Jacobians to Cartesian parameters and put the converted data - * in the one-dimensional {@code p} array. - *

        - */ - public void setInitialJacobians(final SpacecraftState state, final double[][] dY1dY0, - final double[][] dY1dP, final double[] p) { - - // set up a converter - final RealMatrix dY1dC1 = new Array2DRowRealMatrix(getConversionJacobian(state), false); - final DecompositionSolver solver = new QRDecomposition(dY1dC1).getSolver(); - - // convert the provided state Jacobian - final RealMatrix dC1dY0 = solver.solve(new Array2DRowRealMatrix(dY1dY0, false)); - - // map the converted state Jacobian to one-dimensional array - int index = 0; - for (int i = 0; i < STATE_DIMENSION; ++i) { - for (int j = 0; j < STATE_DIMENSION; ++j) { - p[index++] = dC1dY0.getEntry(i, j); - } - } - - if (getParameters() != 0) { - // convert the provided state Jacobian - final RealMatrix dC1dP = solver.solve(new Array2DRowRealMatrix(dY1dP, false)); - - // map the converted parameters Jacobian to one-dimensional array - for (int i = 0; i < STATE_DIMENSION; ++i) { - for (int j = 0; j < getParameters(); ++j) { - p[index++] = dC1dP.getEntry(i, j); - } - } - } - - } - - /** {@inheritDoc} */ - public void getStateJacobian(final SpacecraftState state, final double[][] dYdY0) { - - // get the conversion Jacobian - final double[][] dYdC = getConversionJacobian(state); - - // extract the additional state - final double[] p = state.getAdditionalState(name); - - // compute dYdY0 = dYdC * dCdY0, without allocating new arrays - for (int i = 0; i < STATE_DIMENSION; i++) { - final double[] rowC = dYdC[i]; - final double[] rowD = dYdY0[i]; - for (int j = 0; j < STATE_DIMENSION; ++j) { - double sum = 0; - int pIndex = j; - for (int k = 0; k < STATE_DIMENSION; ++k) { - sum += rowC[k] * p[pIndex]; - pIndex += STATE_DIMENSION; - } - rowD[j] = sum; - } - } - - } - - /** {@inheritDoc} */ - public void getParametersJacobian(final SpacecraftState state, final double[][] dYdP) { - - if (getParameters() != 0) { - - // get the conversion Jacobian - final double[][] dYdC = getConversionJacobian(state); - - // extract the additional state - final double[] p = state.getAdditionalState(name); - - // compute dYdP = dYdC * dCdP, without allocating new arrays - for (int i = 0; i < STATE_DIMENSION; i++) { - final double[] rowC = dYdC[i]; - final double[] rowD = dYdP[i]; - for (int j = 0; j < getParameters(); ++j) { - double sum = 0; - int pIndex = j + STATE_DIMENSION * STATE_DIMENSION; - for (int k = 0; k < STATE_DIMENSION; ++k) { - sum += rowC[k] * p[pIndex]; - pIndex += getParameters(); - } - rowD[j] = sum; - } - } - - } - - } - -} diff --git a/src/main/java/org/orekit/propagation/numerical/NumericalGradientConverter.java b/src/main/java/org/orekit/propagation/numerical/NumericalGradientConverter.java index d7a084b515..7d9d994b34 100644 --- a/src/main/java/org/orekit/propagation/numerical/NumericalGradientConverter.java +++ b/src/main/java/org/orekit/propagation/numerical/NumericalGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,19 +16,9 @@ */ package org.orekit.propagation.numerical; -import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.analysis.differentiation.GradientField; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.FieldAttitude; -import org.orekit.orbits.FieldCartesianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AbstractGradientConverter; -import org.orekit.utils.TimeStampedFieldPVCoordinates; /** Converter for states and parameters arrays. * @author Luc Maisonobe @@ -46,54 +36,8 @@ class NumericalGradientConverter extends AbstractGradientConverter { super(freeStateParameters); - // Derivative field - final Field field = GradientField.getField(freeStateParameters); - - // position always has derivatives - final Vector3D pos = state.getPVCoordinates().getPosition(); - final FieldVector3D posG = new FieldVector3D<>(Gradient.variable(freeStateParameters, 0, pos.getX()), - Gradient.variable(freeStateParameters, 1, pos.getY()), - Gradient.variable(freeStateParameters, 2, pos.getZ())); - - // velocity may have derivatives or not - final Vector3D vel = state.getPVCoordinates().getVelocity(); - final FieldVector3D velG; - if (freeStateParameters > 3) { - velG = new FieldVector3D<>(Gradient.variable(freeStateParameters, 3, vel.getX()), - Gradient.variable(freeStateParameters, 4, vel.getY()), - Gradient.variable(freeStateParameters, 5, vel.getZ())); - } else { - velG = new FieldVector3D<>(Gradient.constant(freeStateParameters, vel.getX()), - Gradient.constant(freeStateParameters, vel.getY()), - Gradient.constant(freeStateParameters, vel.getZ())); - } - - // acceleration never has derivatives - final Vector3D acc = state.getPVCoordinates().getAcceleration(); - final FieldVector3D accG = new FieldVector3D<>(Gradient.constant(freeStateParameters, acc.getX()), - Gradient.constant(freeStateParameters, acc.getY()), - Gradient.constant(freeStateParameters, acc.getZ())); - - // mass never has derivatives - final Gradient gM = Gradient.constant(freeStateParameters, state.getMass()); - - final Gradient gMu = Gradient.constant(freeStateParameters, state.getMu()); - - final FieldOrbit gOrbit = - new FieldCartesianOrbit<>(new TimeStampedFieldPVCoordinates<>(state.getDate(), posG, velG, accG), - state.getFrame(), gMu); - - final FieldAttitude gAttitude; - if (freeStateParameters > 3) { - // compute attitude partial derivatives with respect to position/velocity - gAttitude = provider.getAttitude(gOrbit, gOrbit.getDate(), gOrbit.getFrame()); - } else { - // force model does not depend on attitude, don't bother recomputing it - gAttitude = new FieldAttitude<>(field, state.getAttitude()); - } - // initialize the list with the state having 0 force model parameters - initStates(new FieldSpacecraftState<>(gOrbit, gAttitude, gM)); + initStates(buildBasicGradientSpacecraftState(state, freeStateParameters, provider)); } diff --git a/src/main/java/org/orekit/propagation/numerical/NumericalPropagationHarvester.java b/src/main/java/org/orekit/propagation/numerical/NumericalPropagationHarvester.java index f7f174500e..fda760be46 100644 --- a/src/main/java/org/orekit/propagation/numerical/NumericalPropagationHarvester.java +++ b/src/main/java/org/orekit/propagation/numerical/NumericalPropagationHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,6 +20,8 @@ import org.hipparchus.linear.RealMatrix; import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.SpacecraftState; import org.orekit.utils.DoubleArrayDictionary; @@ -40,7 +42,7 @@ class NumericalPropagationHarvester extends AbstractMatricesHarvester { /** Simple constructor. *

        * The arguments for initial matrices must be compatible with the {@link org.orekit.orbits.OrbitType orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name @@ -92,4 +94,16 @@ public List getJacobiansColumnsNames() { return columnsNames == null ? propagator.getJacobiansColumnsNames() : columnsNames; } + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + return propagator.getOrbitType(); + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + return propagator.getPositionAngleType(); + } + } diff --git a/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java b/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java index 37a6b6ea16..dcf6df0378 100644 --- a/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java +++ b/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,12 +44,11 @@ import org.orekit.forces.maneuvers.jacobians.Duration; import org.orekit.forces.maneuvers.jacobians.MedianDate; import org.orekit.forces.maneuvers.jacobians.TriggerDate; -import org.orekit.forces.maneuvers.trigger.AbstractManeuverTriggers; import org.orekit.forces.maneuvers.trigger.ManeuverTriggers; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.AdditionalStateProvider; import org.orekit.propagation.MatricesHarvester; @@ -68,7 +67,9 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeStampedPVCoordinates; /** This class propagates {@link org.orekit.orbits.Orbit orbits} using @@ -89,9 +90,9 @@ * {@link #removeForceModels()}) *
      73. the {@link OrbitType type} of orbital parameters to be used for propagation * ({@link #setOrbitType(OrbitType)}),
      74. - *
      75. the {@link PositionAngle type} of position angle to be used in orbital parameters + *
      76. the {@link PositionAngleType type} of position angle to be used in orbital parameters * to be used for propagation where it is relevant ({@link - * #setPositionAngleType(PositionAngle)}),
      77. + * #setPositionAngleType(PositionAngleType)}), *
      78. whether {@link MatricesHarvester state transition matrices and Jacobians matrices} * should be propagated along with orbital state ({@link * #setupMatricesComputation(String, RealMatrix, DoubleArrayDictionary)}),
      79. @@ -105,7 +106,7 @@ * *

        From these configuration parameters, only the initial state is mandatory. The default * propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial} parameters with - * {@link PositionAngle#TRUE true} longitude argument. If the central attraction coefficient + * {@link PositionAngleType#TRUE true} longitude argument. If the central attraction coefficient * is not explicitly specified, the one used to define the initial orbit will be used. * However, specifying only the initial state and perhaps the central attraction coefficient * would mean the propagator would use only Keplerian forces. In this case, the simpler {@link @@ -187,8 +188,8 @@ public class NumericalPropagator extends AbstractIntegratedPropagator { * called after creation, the integrated orbit will follow a Keplerian * evolution only. The defaults are {@link OrbitType#EQUINOCTIAL} * for {@link #setOrbitType(OrbitType) propagation - * orbit type} and {@link PositionAngle#TRUE} for {@link - * #setPositionAngleType(PositionAngle) position angle type}. + * orbit type} and {@link PositionAngleType#TRUE} for {@link + * #setPositionAngleType(PositionAngleType) position angle type}. * *

        This constructor uses the {@link DataContext#getDefault() default data context}. * @@ -208,8 +209,8 @@ public NumericalPropagator(final ODEIntegrator integrator) { * called after creation, the integrated orbit will follow a Keplerian * evolution only. The defaults are {@link OrbitType#EQUINOCTIAL} * for {@link #setOrbitType(OrbitType) propagation - * orbit type} and {@link PositionAngle#TRUE} for {@link - * #setPositionAngleType(PositionAngle) position angle type}. + * orbit type} and {@link PositionAngleType#TRUE} for {@link + * #setPositionAngleType(PositionAngleType) position angle type}. * @param integrator numerical integrator to use for propagation. * @param attitudeProvider the attitude law. * @since 10.1 @@ -223,7 +224,7 @@ public NumericalPropagator(final ODEIntegrator integrator, setAttitudeProvider(attitudeProvider); clearStepHandlers(); setOrbitType(OrbitType.EQUINOCTIAL); - setPositionAngleType(PositionAngle.TRUE); + setPositionAngleType(PositionAngleType.TRUE); } /** Set the flag to ignore or not the creation of a {@link NewtonianAttraction}. @@ -249,6 +250,7 @@ public void setMu(final double mu) { superSetMu(mu); } else { addForceModel(new NewtonianAttraction(mu)); + superSetMu(mu); } } @@ -288,7 +290,12 @@ public void addForceModel(final ForceModel model) { model.getParametersDrivers().get(0).addObserver(new ParameterObserver() { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { + superSetMu(driver.getValue()); + } + /** {@inheritDoc} */ + @Override + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { superSetMu(driver.getValue()); } }); @@ -371,14 +378,14 @@ public OrbitType getOrbitType() { *

        * @param positionAngleType angle type to use for propagation */ - public void setPositionAngleType(final PositionAngle positionAngleType) { + public void setPositionAngleType(final PositionAngleType positionAngleType) { super.setPositionAngleType(positionAngleType); } /** Get propagation parameter type. * @return angle type to use for propagation */ - public PositionAngle getPositionAngleType() { + public PositionAngleType getPositionAngleType() { return super.getPositionAngleType(); } @@ -406,8 +413,12 @@ List getJacobiansColumnsNames() { final List columnsNames = new ArrayList<>(); for (final ForceModel forceModel : getAllForceModels()) { for (final ParameterDriver driver : forceModel.getParametersDrivers()) { - if (driver.isSelected() && !columnsNames.contains(driver.getName())) { - columnsNames.add(driver.getName()); + if (driver.isSelected() && !columnsNames.contains(driver.getNamesSpanMap().getFirstSpan().getData())) { + // As driver with same name should have same NamesSpanMap we only check if the first span is present, + // if not we add all span names to columnsNames + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + columnsNames.add(span.getData()); + } } } } @@ -489,42 +500,74 @@ private List setUpTriggerDatesJacobiansColumns(final String stmName) { final List names = new ArrayList<>(); for (final ForceModel forceModel : getAllForceModels()) { if (forceModel instanceof Maneuver) { - final Maneuver maneuver = (Maneuver) forceModel; + final Maneuver maneuver = (Maneuver) forceModel; final ManeuverTriggers maneuverTriggers = maneuver.getManeuverTriggers(); - if (maneuverTriggers instanceof AbstractManeuverTriggers) { - - // FIXME: when issue https://gitlab.orekit.org/orekit/orekit/-/issues/854 is solved - // the previous if statement and the following cast should be removed as the following - // code should really be done for all ManeuverTriggers and not only AbstractManeuverTriggers - final AbstractManeuverTriggers amt = (AbstractManeuverTriggers) maneuverTriggers; - amt.getEventsDetectors(). + maneuverTriggers.getEventDetectors(). filter(d -> d instanceof ParameterDrivenDateIntervalDetector). - map (d -> (ParameterDrivenDateIntervalDetector) d). + map(d -> (ParameterDrivenDateIntervalDetector) d). forEach(d -> { + TriggerDate start; + TriggerDate stop; + if (d.getStartDriver().isSelected() || d.getMedianDriver().isSelected() || d.getDurationDriver().isSelected()) { - final TriggerDate start = - manageTriggerDate(stmName, maneuver, amt, d.getStartDriver().getName(), true, d.getThreshold()); - names.add(start.getName()); + // normally datedriver should have only 1 span but just in case the user defines several span, there will + // be no problem here + for (Span span = d.getStartDriver().getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + start = manageTriggerDate(stmName, maneuver, maneuverTriggers, span.getData(), true, d.getThreshold()); + names.add(start.getName()); + start = null; + } } if (d.getStopDriver().isSelected() || d.getMedianDriver().isSelected() || d.getDurationDriver().isSelected()) { - final TriggerDate stop = - manageTriggerDate(stmName, maneuver, amt, d.getStopDriver().getName(), false, d.getThreshold()); - names.add(stop.getName()); + // normally datedriver should have only 1 span but just in case the user defines several span, there will + // be no problem here + for (Span span = d.getStopDriver().getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + stop = manageTriggerDate(stmName, maneuver, maneuverTriggers, span.getData(), false, d.getThreshold()); + names.add(stop.getName()); + stop = null; + } } if (d.getMedianDriver().isSelected()) { - final MedianDate median = - manageMedianDate(d.getStartDriver().getName(), d.getStopDriver().getName(), d.getMedianDriver().getName()); + // for first span + Span currentMedianNameSpan = d.getMedianDriver().getNamesSpanMap().getFirstSpan(); + MedianDate median = + manageMedianDate(d.getStartDriver().getNamesSpanMap().getFirstSpan().getData(), + d.getStopDriver().getNamesSpanMap().getFirstSpan().getData(), currentMedianNameSpan.getData()); names.add(median.getName()); + // for all span + // normally datedriver should have only 1 span but just in case the user defines several span, there will + // be no problem here. /!\ medianDate driver, startDate driver and stopDate driver must have same span number + for (int spanNumber = 1; spanNumber < d.getMedianDriver().getNamesSpanMap().getSpansNumber(); ++spanNumber) { + currentMedianNameSpan = d.getMedianDriver().getNamesSpanMap().getSpan(currentMedianNameSpan.getEnd()); + median = + manageMedianDate(d.getStartDriver().getNamesSpanMap().getSpan(currentMedianNameSpan.getStart()).getData(), + d.getStopDriver().getNamesSpanMap().getSpan(currentMedianNameSpan.getStart()).getData(), + currentMedianNameSpan.getData()); + names.add(median.getName()); + + } + } if (d.getDurationDriver().isSelected()) { - final Duration duration = - manageManeuverDuration(d.getStartDriver().getName(), d.getStopDriver().getName(), d.getDurationDriver().getName()); + // for first span + Span currentDurationNameSpan = d.getDurationDriver().getNamesSpanMap().getFirstSpan(); + Duration duration = + manageManeuverDuration(d.getStartDriver().getNamesSpanMap().getFirstSpan().getData(), + d.getStopDriver().getNamesSpanMap().getFirstSpan().getData(), currentDurationNameSpan.getData()); names.add(duration.getName()); + // for all span + for (int spanNumber = 1; spanNumber < d.getDurationDriver().getNamesSpanMap().getSpansNumber(); ++spanNumber) { + currentDurationNameSpan = d.getDurationDriver().getNamesSpanMap().getSpan(currentDurationNameSpan.getEnd()); + duration = + manageManeuverDuration(d.getStartDriver().getNamesSpanMap().getSpan(currentDurationNameSpan.getStart()).getData(), + d.getStopDriver().getNamesSpanMap().getSpan(currentDurationNameSpan.getStart()).getData(), + currentDurationNameSpan.getData()); + names.add(duration.getName()); + + } } }); - - } } } @@ -535,7 +578,7 @@ private List setUpTriggerDatesJacobiansColumns(final String stmName) { /** Manage a maneuver trigger date. * @param stmName name of the State Transition Matrix state * @param maneuver maneuver force model - * @param amt trigger to which the driver is bound + * @param mt trigger to which the driver is bound * @param driverName name of the date driver * @param start if true, the driver is a maneuver start * @param threshold event detector threshold @@ -544,7 +587,7 @@ private List setUpTriggerDatesJacobiansColumns(final String stmName) { */ private TriggerDate manageTriggerDate(final String stmName, final Maneuver maneuver, - final AbstractManeuverTriggers amt, + final ManeuverTriggers mt, final String driverName, final boolean start, final double threshold) { @@ -564,7 +607,7 @@ private TriggerDate manageTriggerDate(final String stmName, if (triggerGenerator == null) { // this is the first time we need the Jacobian column generator, create it triggerGenerator = new TriggerDate(stmName, driverName, start, maneuver, threshold); - amt.addResetter(triggerGenerator); + mt.addResetter(triggerGenerator); addAdditionalDerivativesProvider(triggerGenerator.getMassDepletionDelay()); addAdditionalStateProvider(triggerGenerator); } @@ -666,7 +709,9 @@ private void setUpRegularParametersJacobiansColumns(final StateTransitionMatrixG final ParameterDriversList selected = new ParameterDriversList(); for (final ForceModel forceModel : getAllForceModels()) { for (final ParameterDriver driver : forceModel.getParametersDrivers()) { - if (!triggerDates.contains(driver.getName())) { + if (!triggerDates.contains(driver.getNamesSpanMap().getFirstSpan().getData())) { + // if the first span is not in triggerdate means that the driver is not a trigger + // date and can be selected here selected.add(driver); } } @@ -680,30 +725,35 @@ private void setUpRegularParametersJacobiansColumns(final StateTransitionMatrixG selected.sort(); // add the Jacobians column generators corresponding to parameters, and setup state accordingly + // a new column is needed for each value estimated so for each span of the parameterDriver for (final DelegatingDriver driver : selected.getDrivers()) { - IntegrableJacobianColumnGenerator generator = null; + for (Span currentNameSpan = driver.getNamesSpanMap().getFirstSpan(); currentNameSpan != null; currentNameSpan = currentNameSpan.next()) { + + IntegrableJacobianColumnGenerator generator = null; + // check if we already have set up the providers + for (final AdditionalDerivativesProvider provider : getAdditionalDerivativesProviders()) { + if (provider instanceof IntegrableJacobianColumnGenerator && + provider.getName().equals(currentNameSpan.getData())) { + // the Jacobian column generator has already been set up in a previous propagation + generator = (IntegrableJacobianColumnGenerator) provider; + break; + } - // check if we already have set up the providers - for (final AdditionalDerivativesProvider provider : getAdditionalDerivativesProviders()) { - if (provider instanceof IntegrableJacobianColumnGenerator && - provider.getName().equals(driver.getName())) { - // the Jacobian column generator has already been set up in a previous propagation - generator = (IntegrableJacobianColumnGenerator) provider; - break; } - } - if (generator == null) { - // this is the first time we need the Jacobian column generator, create it - generator = new IntegrableJacobianColumnGenerator(stmGenerator, driver.getName()); - addAdditionalDerivativesProvider(generator); - } + if (generator == null) { + // this is the first time we need the Jacobian column generator, create it + generator = new IntegrableJacobianColumnGenerator(stmGenerator, currentNameSpan.getData()); + addAdditionalDerivativesProvider(generator); + } + + if (!getInitialIntegrationState().hasAdditionalState(currentNameSpan.getData())) { + // add the initial Jacobian column if it is not already there + // (perhaps due to a previous propagation) + setInitialColumn(currentNameSpan.getData(), getHarvester().getInitialJacobianColumn(currentNameSpan.getData())); + } - if (!getInitialIntegrationState().hasAdditionalState(driver.getName())) { - // add the initial Jacobian column if it is not already there - // (perhaps due to a previous propagation) - setInitialColumn(driver.getName(), getHarvester().getInitialJacobianColumn(driver.getName())); } } @@ -750,7 +800,7 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final /** {@inheritDoc} */ @Override protected StateMapper createMapper(final AbsoluteDate referenceDate, final double mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { return new OsculatingMapper(referenceDate, mu, orbitType, positionAngleType, attitudeProvider, frame); } @@ -773,7 +823,7 @@ private static class OsculatingMapper extends StateMapper { * @param frame inertial frame */ OsculatingMapper(final AbsoluteDate referenceDate, final double mu, - final OrbitType orbitType, final PositionAngle positionAngleType, + final OrbitType orbitType, final PositionAngleType positionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { super(referenceDate, mu, orbitType, positionAngleType, attitudeProvider, frame); } @@ -785,7 +835,7 @@ public SpacecraftState mapArrayToState(final AbsoluteDate date, final double[] y final double mass = y[6]; if (mass <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, mass); + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass); } if (getOrbitType() == null) { @@ -861,7 +911,7 @@ private class Main implements MainStateEquations, TimeDerivativesEquations { this.jacobian = new double[6][6]; for (final ForceModel forceModel : forceModels) { - forceModel.getEventsDetectors().forEach(detector -> setUpEventDetector(integrator, detector)); + forceModel.getEventDetectors().forEach(detector -> setUpEventDetector(integrator, detector)); } if (getOrbitType() == null) { @@ -917,11 +967,10 @@ public double[] computeDerivatives(final SpacecraftState state) { @Override public void addKeplerContribution(final double mu) { if (getOrbitType() == null) { - // if mu is neither 0 nor NaN, we want to include Newtonian acceleration if (mu > 0) { // velocity derivative is Newtonian acceleration - final Vector3D position = currentState.getPVCoordinates().getPosition(); + final Vector3D position = currentState.getPosition(); final double r2 = position.getNormSq(); final double coeff = -mu / (r2 * FastMath.sqrt(r2)); yDot[3] += coeff * position.getX(); @@ -1067,7 +1116,7 @@ public static double[][] tolerances(final double dP, final double dV, // convert the orbit to the desired type final double[][] jacobian = new double[6][6]; final Orbit converted = type.convertType(orbit); - converted.getJacobianWrtCartesian(PositionAngle.TRUE, jacobian); + converted.getJacobianWrtCartesian(PositionAngleType.TRUE, jacobian); for (int i = 0; i < 6; ++i) { final double[] row = jacobian[i]; @@ -1084,7 +1133,7 @@ public static double[][] tolerances(final double dP, final double dV, } - Arrays.fill(relTol, dP / FastMath.sqrt(orbit.getPVCoordinates().getPosition().getNormSq())); + Arrays.fill(relTol, dP / FastMath.sqrt(orbit.getPosition().getNormSq())); return new double[][] { absTol, relTol diff --git a/src/main/java/org/orekit/propagation/numerical/PartialDerivativesEquations.java b/src/main/java/org/orekit/propagation/numerical/PartialDerivativesEquations.java deleted file mode 100644 index af27487ac1..0000000000 --- a/src/main/java/org/orekit/propagation/numerical/PartialDerivativesEquations.java +++ /dev/null @@ -1,444 +0,0 @@ -/* Copyright 2010-2011 Centre National d'Études Spatiales - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.numerical; - -import java.util.IdentityHashMap; -import java.util.Map; - -import org.hipparchus.analysis.differentiation.Gradient; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.forces.ForceModel; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.integration.AdditionalDerivativesProvider; -import org.orekit.propagation.integration.CombinedDerivatives; -import org.orekit.time.AbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; - -/** {@link AdditionalDerivativesProvider derivatives provider} computing the partial derivatives - * of the state (orbit) with respect to initial state and force models parameters. - *

        - * This set of equations are automatically added to a {@link NumericalPropagator numerical propagator} - * in order to compute partial derivatives of the orbit along with the orbit itself. This is - * useful for example in orbit determination applications. - *

        - *

        - * The partial derivatives with respect to initial state can be either dimension 6 - * (orbit only) or 7 (orbit and mass). - *

        - *

        - * The partial derivatives with respect to force models parameters has a dimension - * equal to the number of selected parameters. Parameters selection is implemented at - * {@link ForceModel force models} level. Users must retrieve a {@link ParameterDriver - * parameter driver} using {@link ForceModel#getParameterDriver(String)} and then - * select it by calling {@link ParameterDriver#setSelected(boolean) setSelected(true)}. - *

        - *

        - * If several force models provide different {@link ParameterDriver drivers} for the - * same parameter name, selecting any of these drivers has the side effect of - * selecting all the drivers for this shared parameter. In this case, the partial - * derivatives will be the sum of the partial derivatives contributed by the - * corresponding force models. This case typically arises for central attraction - * coefficient, which has an influence on {@link org.orekit.forces.gravity.NewtonianAttraction - * Newtonian attraction}, {@link org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel - * gravity field}, and {@link org.orekit.forces.gravity.Relativity relativity}. - *

        - * @author Véronique Pommier-Maurussane - * @author Luc Maisonobe - * @deprecated as of 11.1, replaced by {@link - * org.orekit.propagation.Propagator#setupMatricesComputation(String, - * org.hipparchus.linear.RealMatrix, org.orekit.utils.DoubleArrayDictionary)} - */ -@Deprecated -public class PartialDerivativesEquations - implements AdditionalDerivativesProvider, - org.orekit.propagation.integration.AdditionalEquations { - - /** Propagator computing state evolution. */ - private final NumericalPropagator propagator; - - /** Selected parameters for Jacobian computation. */ - private ParameterDriversList selected; - - /** Parameters map. */ - private Map map; - - /** Name. */ - private final String name; - - /** Flag for Jacobian matrices initialization. */ - private boolean initialized; - - /** Simple constructor. - *

        - * Upon construction, this set of equations is automatically added to - * the propagator by calling its {@link - * NumericalPropagator#addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} method. So - * there is no need to call this method explicitly for these equations. - *

        - * @param name name of the partial derivatives equations - * @param propagator the propagator that will handle the orbit propagation - */ - public PartialDerivativesEquations(final String name, final NumericalPropagator propagator) { - this.name = name; - this.selected = null; - this.map = null; - this.propagator = propagator; - this.initialized = false; - propagator.addAdditionalDerivativesProvider(this); - } - - /** {@inheritDoc} */ - public String getName() { - return name; - } - - /** {@inheritDoc} */ - @Override - public int getDimension() { - freezeParametersSelection(); - return 6 * (6 + selected.getNbParams()); - } - - /** Freeze the selected parameters from the force models. - */ - private void freezeParametersSelection() { - if (selected == null) { - - // first pass: gather all parameters, binding similar names together - selected = new ParameterDriversList(); - for (final ForceModel provider : propagator.getAllForceModels()) { - for (final ParameterDriver driver : provider.getParametersDrivers()) { - selected.add(driver); - } - } - - // second pass: now that shared parameter names are bound together, - // their selections status have been synchronized, we can filter them - selected.filter(true); - - // third pass: sort parameters lexicographically - selected.sort(); - - // fourth pass: set up a map between parameters drivers and matrices columns - map = new IdentityHashMap(); - int parameterIndex = 0; - for (final ParameterDriver selectedDriver : selected.getDrivers()) { - for (final ForceModel provider : propagator.getAllForceModels()) { - for (final ParameterDriver driver : provider.getParametersDrivers()) { - if (driver.getName().equals(selectedDriver.getName())) { - map.put(driver, parameterIndex); - } - } - } - ++parameterIndex; - } - - } - } - - /** Get the selected parameters, in Jacobian matrix column order. - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, so the proper list is returned. - *

        - * @return selected parameters, in Jacobian matrix column order which - * is lexicographic order - */ - public ParameterDriversList getSelectedParameters() { - freezeParametersSelection(); - return selected; - } - - /** Set the initial value of the Jacobian with respect to state and parameter. - *

        - * This method is equivalent to call {@link #setInitialJacobians(SpacecraftState, - * double[][], double[][])} with dYdY0 set to the identity matrix and dYdP set - * to a zero matrix. - *

        - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, so proper matrices dimensions are used. - *

        - * @param s0 initial state - * @return state with initial Jacobians added - * @see #getSelectedParameters() - * @since 9.0 - */ - public SpacecraftState setInitialJacobians(final SpacecraftState s0) { - freezeParametersSelection(); - final int stateDimension = 6; - final double[][] dYdY0 = new double[stateDimension][stateDimension]; - final double[][] dYdP = new double[stateDimension][selected.getNbParams()]; - for (int i = 0; i < stateDimension; ++i) { - dYdY0[i][i] = 1.0; - } - return setInitialJacobians(s0, dYdY0, dYdP); - } - - /** Set the initial value of the Jacobian with respect to state and parameter. - *

        - * The returned state must be added to the propagator (it is not done - * automatically, as the user may need to add more states to it). - *

        - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, and the {@code dY1dP} matrix dimension must - * be consistent with the selection. - *

        - * @param s1 current state - * @param dY1dY0 Jacobian of current state at time t₁ with respect - * to state at some previous time t₀ (must be 6x6) - * @param dY1dP Jacobian of current state at time t₁ with respect - * to parameters (may be null if no parameters are selected) - * @return state with initial Jacobians added - * @see #getSelectedParameters() - */ - public SpacecraftState setInitialJacobians(final SpacecraftState s1, - final double[][] dY1dY0, final double[][] dY1dP) { - - freezeParametersSelection(); - - // Check dimensions - final int stateDim = dY1dY0.length; - if (stateDim != 6 || stateDim != dY1dY0[0].length) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_6X6, - stateDim, dY1dY0[0].length); - } - if (dY1dP != null && stateDim != dY1dP.length) { - throw new OrekitException(OrekitMessages.STATE_AND_PARAMETERS_JACOBIANS_ROWS_MISMATCH, - stateDim, dY1dP.length); - } - if (dY1dP == null && selected.getNbParams() != 0 || - dY1dP != null && selected.getNbParams() != dY1dP[0].length) { - throw new OrekitException(new OrekitException(OrekitMessages.INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH, - dY1dP == null ? 0 : dY1dP[0].length, selected.getNbParams())); - } - - // store the matrices as a single dimension array - initialized = true; - final JacobiansMapper mapper = getMapper(); - final double[] p = new double[mapper.getAdditionalStateDimension()]; - mapper.setInitialJacobians(s1, dY1dY0, dY1dP, p); - - // set value in propagator - return s1.addAdditionalState(name, p); - - } - - /** Get a mapper between two-dimensional Jacobians and one-dimensional additional state. - * @return a mapper between two-dimensional Jacobians and one-dimensional additional state, - * with the same name as the instance - * @see #setInitialJacobians(SpacecraftState) - * @see #setInitialJacobians(SpacecraftState, double[][], double[][]) - */ - public JacobiansMapper getMapper() { - if (!initialized) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_INITIALIZED); - } - return new JacobiansMapper(name, selected, - propagator.getOrbitType(), - propagator.getPositionAngleType()); - } - - /** {@inheritDoc} */ - public void init(final SpacecraftState initialState, final AbsoluteDate target) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - AdditionalDerivativesProvider.super.init(initialState, target); - } - - /** {@inheritDoc} */ - public double[] computeDerivatives(final SpacecraftState s, final double[] pDot) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - System.arraycopy(derivatives(s), 0, pDot, 0, pDot.length); - return null; - } - - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - - /** {@inheritDoc} */ - public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { - - // initialize acceleration Jacobians to zero - final int paramDim = selected.getNbParams(); - final int dim = 3; - final double[][] dAccdParam = new double[dim][paramDim]; - final double[][] dAccdPos = new double[dim][dim]; - final double[][] dAccdVel = new double[dim][dim]; - - final NumericalGradientConverter fullConverter = new NumericalGradientConverter(s, 6, propagator.getAttitudeProvider()); - final NumericalGradientConverter posOnlyConverter = new NumericalGradientConverter(s, 3, propagator.getAttitudeProvider()); - - // compute acceleration Jacobians, finishing with the largest force: Newtonian attraction - for (final ForceModel forceModel : propagator.getAllForceModels()) { - - final NumericalGradientConverter converter = forceModel.dependsOnPositionOnly() ? posOnlyConverter : fullConverter; - final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] parameters = converter.getParameters(dsState, forceModel); - - final FieldVector3D acceleration = forceModel.acceleration(dsState, parameters); - final double[] derivativesX = acceleration.getX().getGradient(); - final double[] derivativesY = acceleration.getY().getGradient(); - final double[] derivativesZ = acceleration.getZ().getGradient(); - - // update Jacobians with respect to state - addToRow(derivativesX, 0, converter.getFreeStateParameters(), dAccdPos, dAccdVel); - addToRow(derivativesY, 1, converter.getFreeStateParameters(), dAccdPos, dAccdVel); - addToRow(derivativesZ, 2, converter.getFreeStateParameters(), dAccdPos, dAccdVel); - - int index = converter.getFreeStateParameters(); - for (ParameterDriver driver : forceModel.getParametersDrivers()) { - if (driver.isSelected()) { - final int parameterIndex = map.get(driver); - dAccdParam[0][parameterIndex] += derivativesX[index]; - dAccdParam[1][parameterIndex] += derivativesY[index]; - dAccdParam[2][parameterIndex] += derivativesZ[index]; - ++index; - } - } - - } - - // the variational equations of the complete state Jacobian matrix have the following form: - - // [ | ] [ | ] [ | ] - // [ Adot | Bdot ] [ dVel/dPos = 0 | dVel/dVel = Id ] [ A | B ] - // [ | ] [ | ] [ | ] - // ---------+--------- ------------------+------------------- * ------+------ - // [ | ] [ | ] [ | ] - // [ Cdot | Ddot ] = [ dAcc/dPos | dAcc/dVel ] [ C | D ] - // [ | ] [ | ] [ | ] - - // The A, B, C and D sub-matrices and their derivatives (Adot ...) are 3x3 matrices - - // The expanded multiplication above can be rewritten to take into account - // the fixed values found in the sub-matrices in the left factor. This leads to: - - // [ Adot ] = [ C ] - // [ Bdot ] = [ D ] - // [ Cdot ] = [ dAcc/dPos ] * [ A ] + [ dAcc/dVel ] * [ C ] - // [ Ddot ] = [ dAcc/dPos ] * [ B ] + [ dAcc/dVel ] * [ D ] - - // The following loops compute these expressions taking care of the mapping of the - // (A, B, C, D) matrices into the single dimension array p and of the mapping of the - // (Adot, Bdot, Cdot, Ddot) matrices into the single dimension array pDot. - - // copy C and E into Adot and Bdot - final int stateDim = 6; - final double[] p = s.getAdditionalState(getName()); - final double[] pDot = new double[p.length]; - System.arraycopy(p, dim * stateDim, pDot, 0, dim * stateDim); - - // compute Cdot and Ddot - for (int i = 0; i < dim; ++i) { - final double[] dAdPi = dAccdPos[i]; - final double[] dAdVi = dAccdVel[i]; - for (int j = 0; j < stateDim; ++j) { - pDot[(dim + i) * stateDim + j] = - dAdPi[0] * p[j] + dAdPi[1] * p[j + stateDim] + dAdPi[2] * p[j + 2 * stateDim] + - dAdVi[0] * p[j + 3 * stateDim] + dAdVi[1] * p[j + 4 * stateDim] + dAdVi[2] * p[j + 5 * stateDim]; - } - } - - for (int k = 0; k < paramDim; ++k) { - // the variational equations of the parameters Jacobian matrix are computed - // one column at a time, they have the following form: - // [ ] [ | ] [ ] [ ] - // [ Edot ] [ dVel/dPos = 0 | dVel/dVel = Id ] [ E ] [ dVel/dParam = 0 ] - // [ ] [ | ] [ ] [ ] - // -------- ------------------+------------------- * ----- + -------------------- - // [ ] [ | ] [ ] [ ] - // [ Fdot ] = [ dAcc/dPos | dAcc/dVel ] [ F ] [ dAcc/dParam ] - // [ ] [ | ] [ ] [ ] - - // The E and F sub-columns and their derivatives (Edot, Fdot) are 3 elements columns. - - // The expanded multiplication and addition above can be rewritten to take into - // account the fixed values found in the sub-matrices in the left factor. This leads to: - - // [ Edot ] = [ F ] - // [ Fdot ] = [ dAcc/dPos ] * [ E ] + [ dAcc/dVel ] * [ F ] + [ dAcc/dParam ] - - // The following loops compute these expressions taking care of the mapping of the - // (E, F) columns into the single dimension array p and of the mapping of the - // (Edot, Fdot) columns into the single dimension array pDot. - - // copy F into Edot - final int columnTop = stateDim * stateDim + k; - pDot[columnTop] = p[columnTop + 3 * paramDim]; - pDot[columnTop + paramDim] = p[columnTop + 4 * paramDim]; - pDot[columnTop + 2 * paramDim] = p[columnTop + 5 * paramDim]; - - // compute Fdot - for (int i = 0; i < dim; ++i) { - final double[] dAdPi = dAccdPos[i]; - final double[] dAdVi = dAccdVel[i]; - pDot[columnTop + (dim + i) * paramDim] = - dAccdParam[i][k] + - dAdPi[0] * p[columnTop] + dAdPi[1] * p[columnTop + paramDim] + dAdPi[2] * p[columnTop + 2 * paramDim] + - dAdVi[0] * p[columnTop + 3 * paramDim] + dAdVi[1] * p[columnTop + 4 * paramDim] + dAdVi[2] * p[columnTop + 5 * paramDim]; - } - - } - - return new CombinedDerivatives(pDot, null); - - } - - /** Get the flag for the initialization of the state jacobian. - * @return true if the state jacobian is initialized - * @since 10.2 - */ - public boolean isInitialize() { - return initialized; - } - - /** Fill Jacobians rows. - * @param derivatives derivatives of a component of acceleration (along either x, y or z) - * @param index component index (0 for x, 1 for y, 2 for z) - * @param freeStateParameters number of free parameters, either 3 (position), - * 6 (position-velocity) or 7 (position-velocity-mass) - * @param dAccdPos Jacobian of acceleration with respect to spacecraft position - * @param dAccdVel Jacobian of acceleration with respect to spacecraft velocity - */ - private void addToRow(final double[] derivatives, final int index, final int freeStateParameters, - final double[][] dAccdPos, final double[][] dAccdVel) { - - for (int i = 0; i < 3; ++i) { - dAccdPos[index][i] += derivatives[i]; - } - if (freeStateParameters > 3) { - for (int i = 0; i < 3; ++i) { - dAccdVel[index][i] += derivatives[i + 3]; - } - } - - } - -} - diff --git a/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java b/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java index 7966954c85..7659844d42 100644 --- a/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java +++ b/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,16 +31,18 @@ import org.orekit.errors.OrekitException; import org.orekit.forces.ForceModel; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AdditionalDerivativesProvider; import org.orekit.propagation.integration.CombinedDerivatives; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; /** Generator for State Transition Matrix. * @author Luc Maisonobe + * @author Melina Vanel * @since 11.1 */ class StateTransitionMatrixGenerator implements AdditionalDerivativesProvider { @@ -106,7 +108,7 @@ public int getDimension() { /** {@inheritDoc} */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !state.hasAdditionalState(getName()); } @@ -118,13 +120,13 @@ public boolean yield(final SpacecraftState state) { * @param dYdY0 initial State Transition Matrix ∂Y/∂Y₀, * if null (which is the most frequent case), assumed to be 6x6 identity * @param orbitType orbit type used for states Y and Y₀ in {@code dYdY0} - * @param positionAngle position angle used states Y and Y₀ in {@code dYdY0} + * @param positionAngleType position angle used states Y and Y₀ in {@code dYdY0} * @return state with initial STM (converted to Cartesian ∂C/∂Y₀) added */ SpacecraftState setInitialStateTransitionMatrix(final SpacecraftState state, final RealMatrix dYdY0, final OrbitType orbitType, - final PositionAngle positionAngle) { + final PositionAngleType positionAngleType) { final RealMatrix nonNullDYdY0; if (dYdY0 == null) { @@ -143,7 +145,7 @@ SpacecraftState setInitialStateTransitionMatrix(final SpacecraftState state, final RealMatrix dCdY0; if (state.isOrbitDefined()) { final double[][] dYdC = new double[STATE_DIMENSION][STATE_DIMENSION]; - orbitType.convertType(state.getOrbit()).getJacobianWrtCartesian(positionAngle, dYdC); + orbitType.convertType(state.getOrbit()).getJacobianWrtCartesian(positionAngleType, dYdC); dCdY0 = new QRDecomposition(MatrixUtils.createRealMatrix(dYdC), THRESHOLD).getSolver().solve(nonNullDYdY0); } else { dCdY0 = nonNullDYdY0; @@ -163,13 +165,6 @@ SpacecraftState setInitialStateTransitionMatrix(final SpacecraftState state, } - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - /** {@inheritDoc} */ public CombinedDerivatives combinedDerivatives(final SpacecraftState state) { @@ -247,11 +242,12 @@ private double[] computePartials(final SpacecraftState state) { // evaluate contribution of all force models final NumericalGradientConverter fullConverter = new NumericalGradientConverter(state, STATE_DIMENSION, attitudeProvider); final NumericalGradientConverter posOnlyConverter = new NumericalGradientConverter(state, SPACE_DIMENSION, attitudeProvider); + for (final ForceModel forceModel : forceModels) { final NumericalGradientConverter converter = forceModel.dependsOnPositionOnly() ? posOnlyConverter : fullConverter; final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] parameters = converter.getParameters(dsState, forceModel); + final Gradient[] parameters = converter.getParametersAtStateDate(dsState, forceModel); final FieldVector3D acceleration = forceModel.acceleration(dsState, parameters); final double[] gradX = acceleration.getX().getGradient(); final double[] gradY = acceleration.getY().getGradient(); @@ -286,20 +282,23 @@ private double[] computePartials(final SpacecraftState state) { for (ParameterDriver driver : forceModel.getParametersDrivers()) { if (driver.isSelected()) { - // get the partials derivatives for this driver - DoubleArrayDictionary.Entry entry = accelerationPartials.getEntry(driver.getName()); - if (entry == null) { - // create an entry filled with zeroes - accelerationPartials.put(driver.getName(), new double[SPACE_DIMENSION]); - entry = accelerationPartials.getEntry(driver.getName()); + // for each span (for each estimated value) corresponding name is added + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // get the partials derivatives for this driver + DoubleArrayDictionary.Entry entry = accelerationPartials.getEntry(span.getData()); + if (entry == null) { + // create an entry filled with zeroes + accelerationPartials.put(span.getData(), new double[SPACE_DIMENSION]); + entry = accelerationPartials.getEntry(span.getData()); + } + + // add the contribution of the current force model + entry.increment(new double[] { + gradX[paramsIndex], gradY[paramsIndex], gradZ[paramsIndex] + }); + ++paramsIndex; } - - // add the contribution of the current force model - entry.increment(new double[] { - gradX[paramsIndex], gradY[paramsIndex], gradZ[paramsIndex] - }); - ++paramsIndex; - } } diff --git a/src/main/java/org/orekit/propagation/numerical/TimeDerivativesEquations.java b/src/main/java/org/orekit/propagation/numerical/TimeDerivativesEquations.java index 6b25700c8a..573eaa8cda 100644 --- a/src/main/java/org/orekit/propagation/numerical/TimeDerivativesEquations.java +++ b/src/main/java/org/orekit/propagation/numerical/TimeDerivativesEquations.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPConstants.java b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPConstants.java index 2cb13a4862..e6551c36a6 100644 --- a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPConstants.java +++ b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPConstants.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java index 43000d8d64..33b57198af 100644 --- a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java +++ b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,9 +18,7 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.DSFactory; import org.hipparchus.analysis.differentiation.DerivativeStructure; @@ -30,11 +28,9 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.bodies.CR3BPSystem; -import org.orekit.forces.AbstractForceModel; +import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.utils.ParameterDriver; /** Class calculating the acceleration induced by CR3BP model. @@ -42,7 +38,7 @@ * @author Vincent Mouraux * @since 10.2 */ -public class CR3BPForceModel extends AbstractForceModel { +public class CR3BPForceModel implements ForceModel { /** Suffix for parameter name for Mass Ratio enabling Jacobian processing. */ public static final String MASS_RATIO_SUFFIX = "CR3BP System Mass Ratio"; @@ -133,9 +129,9 @@ public > FieldVector3D acceleration(final F public DerivativeStructure getPotential(final SpacecraftState s) { // Spacecraft Position - final double x = s.getPVCoordinates().getPosition().getX(); - final double y = s.getPVCoordinates().getPosition().getY(); - final double z = s.getPVCoordinates().getPosition().getZ(); + final double x = s.getPosition().getX(); + final double y = s.getPosition().getY(); + final double z = s.getPosition().getZ(); final DSFactory factoryP = new DSFactory(3, 2); final DerivativeStructure fpx = factoryP.variable(0, x); @@ -145,7 +141,9 @@ public DerivativeStructure getPotential(final SpacecraftState s) { final DerivativeStructure zero = fpx.getField().getZero(); // Get CR3BP System mass ratio - final DerivativeStructure mu = zero.add(muParameterDriver.getValue()); + // By construction, mudriver has 1 value for the all time period that is why + // the getValue can be called with any date argument or null argument + final DerivativeStructure mu = zero.add(muParameterDriver.getValue(s.getDate())); // Normalized distances between primaries and barycenter in CR3BP final DerivativeStructure d1 = mu; @@ -175,9 +173,9 @@ public DerivativeStructure getPotential(final SpacecraftState s) { public > FieldDerivativeStructure getPotential(final FieldSpacecraftState s) { // Spacecraft Position - final T x = s.getPVCoordinates().getPosition().getX(); - final T y = s.getPVCoordinates().getPosition().getY(); - final T z = s.getPVCoordinates().getPosition().getZ(); + final T x = s.getPosition().getX(); + final T y = s.getPosition().getY(); + final T z = s.getPosition().getZ(); final FDSFactory factoryP = new FDSFactory<>(s.getDate().getField(), 3, 2); final FieldDerivativeStructure fpx = factoryP.variable(0, x); @@ -186,7 +184,9 @@ public > FieldDerivativeStructure getPotent final FieldDerivativeStructure zero = fpx.getField().getZero(); // Get CR3BP System mass ratio - final FieldDerivativeStructure mu = zero.add(muParameterDriver.getValue()); + // By construction, mudriver has 1 value for the all time period that is why + // the getValue can be called with any date argument or null argument + final FieldDerivativeStructure mu = zero.add(muParameterDriver.getValue(s.getDate().toAbsoluteDate())); // Normalized distances between primaries and barycenter in CR3BP final FieldDerivativeStructure d1 = mu; @@ -207,17 +207,6 @@ public > FieldDerivativeStructure getPotent .add(fpx.multiply(fpx).add(fpy.multiply(fpy)).multiply(0.5)).add(d1.multiply(d2).multiply(0.5)); } - /** {@inheritDoc} */ - public Stream getEventsDetectors() { - return Stream.empty(); - } - - @Override - /** {@inheritDoc} */ - public > Stream> getFieldEventsDetectors(final Field field) { - return Stream.empty(); - } - /** {@inheritDoc} */ public List getParametersDrivers() { return Collections.singletonList(muParameterDriver); diff --git a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPMultipleShooter.java b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPMultipleShooter.java index f950b0d7d0..d5a6e5f9bf 100644 --- a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPMultipleShooter.java +++ b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPMultipleShooter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,8 +18,9 @@ import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.utils.AbsolutePVCoordinates; @@ -29,6 +30,7 @@ * Multiple shooting method applicable for orbits, either propagation in CR3BP, or in an ephemeris model. * @see "TRAJECTORY DESIGN AND ORBIT MAINTENANCE STRATEGIES IN MULTI-BODY DYNAMICAL REGIMES by Thomas A. Pavlak, Purdue University" * @author William Desprats + * @author Alberto Fossà */ public class CR3BPMultipleShooter extends AbstractMultipleShooting { @@ -40,42 +42,23 @@ public class CR3BPMultipleShooter extends AbstractMultipleShooting { */ private final List stmEquations; - /** Number of patch points. */ - private int npoints; + /** True if orbit is closed. */ + private boolean isClosedOrbit; /** Simple Constructor. - *

        Standard constructor for multiple shooting which can be used with the CR3BP model.

        - * @param initialGuessList initial patch points to be corrected. - * @param propagatorList list of propagators associated to each patch point. - * @param additionalEquations list of additional equations linked to propagatorList. - * @param arcDuration initial guess of the duration of each arc. - * @param tolerance convergence tolerance on the constraint vector - * @deprecated as of 11.1, replaced by {@link #CR3BPMultipleShooter(List, List, List, double, double, int)} - */ - @Deprecated - public CR3BPMultipleShooter(final List initialGuessList, final List propagatorList, - final List additionalEquations, - final double arcDuration, final double tolerance) { - super(initialGuessList, propagatorList, additionalEquations, arcDuration, tolerance, STM); - stmEquations = additionalEquations.stream().map(ae -> (STMEquations) ae).collect(Collectors.toList()); - npoints = initialGuessList.size(); - } - - /** Simple Constructor. - *

        Standard constructor for multiple shooting which can be used with the CR3BP model.

        - * @param initialGuessList initial patch points to be corrected. - * @param propagatorList list of propagators associated to each patch point. - * @param stmEquations list of additional derivatives providers linked to propagatorList. - * @param arcDuration initial guess of the duration of each arc. + *

        Standard constructor for multiple shooting which can be used with the CR3BP model.

        + * @param initialGuessList initial patch points to be corrected + * @param propagatorList list of propagators associated to each patch point + * @param stmEquations list of additional derivatives providers linked to propagatorList * @param tolerance convergence tolerance on the constraint vector * @param maxIter maximum number of iterations */ - public CR3BPMultipleShooter(final List initialGuessList, final List propagatorList, - final List stmEquations, final double arcDuration, + public CR3BPMultipleShooter(final List initialGuessList, + final List propagatorList, + final List stmEquations, final double tolerance, final int maxIter) { - super(initialGuessList, propagatorList, arcDuration, tolerance, maxIter, STM); + super(initialGuessList, propagatorList, tolerance, maxIter, true, STM); this.stmEquations = stmEquations; - this.npoints = initialGuessList.size(); } /** {@inheritDoc} */ @@ -84,54 +67,81 @@ protected SpacecraftState getAugmentedInitialState(final int i) { } /** {@inheritDoc} */ + @Override + public void setEpochFreedom(final int patchIndex, final boolean isFree) { + throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED); + } + + /** {@inheritDoc} */ + @Override + public void setScaleLength(final double scaleLength) { + throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED); + } + + /** {@inheritDoc} */ + @Override + public void setScaleTime(final double scaleTime) { + throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED); + } + + /** Set the constraint of a closed orbit or not. + * @param isClosed true if orbit should be closed + */ + public void setClosedOrbitConstraint(final boolean isClosed) { + this.isClosedOrbit = isClosed; + } + + /** {@inheritDoc} */ + @Override protected double[][] computeAdditionalJacobianMatrix(final List propagatedSP) { final Map mapConstraints = getConstraintsMap(); + final boolean[] freeCompsMap = getFreeCompsMap(); // Number of additional constraints - final int n = mapConstraints.size() + (isClosedOrbit() ? 6 : 0); + final int nRows = mapConstraints.size() + (isClosedOrbit ? 6 : 0); + final int nCols = getNumberOfFreeComponents(); - final int ncolumns = getNumberOfFreeVariables() - 1; + final double[][] M = new double[nRows][nCols]; - final double[][] M = new double[n][ncolumns]; - - int k = 0; - if (isClosedOrbit()) { + int j = 0; + if (isClosedOrbit) { // The Jacobian matrix has the following form: // // [-1 0 0 ... 1 0 ] // [ 0 -1 0 0 ... 1 0 ] - // C = [ 0 -1 0 0 ... 1 0 ] + // F = [ 0 -1 0 0 ... 1 0 ] // [ 0 -1 0 0 ... 1 0 ] - // [ 0 -1 0 0 ... 1 0 ] + // [ 0 -1 0 0 ... 1 0 ] // [ 0 0 -1 0 ... 0 1 ] + int index = 0; for (int i = 0; i < 6; i++) { - M[i][i] = -1; - M[i][ncolumns - 6 + i] = 1; + if (freeCompsMap[i]) { + M[i][index] = -1.0; + index++; + } } - k = 6; + index = nCols - 6; + for (int i = 0; i < 6; i++) { + if (freeCompsMap[nCols - 6 + i]) { + M[i][index] = 1.0; + index++; + } + } + j = 6; } - for (int index : mapConstraints.keySet()) { - M[k][index] = 1; - k++; + for (int k : mapConstraints.keySet()) { + M[j][k] = 1.0; + j++; } + return M; } /** {@inheritDoc} */ @Override - protected double[][] computeEpochJacobianMatrix(final List propagatedSP) { - final int nFreeEpoch = getNumberOfFreeEpoch(); - // Rows and columns dimensions - final int ncolumns = 1 + nFreeEpoch; - final int nrows = npoints - 1; - // Return an empty array - return new double[nrows][ncolumns]; - } - - /** {@inheritDoc} */ protected double[] computeAdditionalConstraints(final List propagatedSP) { // The additional constraint vector has the following form : @@ -146,33 +156,36 @@ protected double[] computeAdditionalConstraints(final List prop // [ ... ] | a patch point equals to a // [vz2i - vz2d]---- desired value) - final Map mapConstraints = getConstraintsMap(); - // Number of additional constraints - final int n = mapConstraints.size() + (isClosedOrbit() ? 6 : 0); - + final Map mapConstraints = getConstraintsMap(); final List patchedSpacecraftStates = getPatchedSpacecraftState(); - final double[] fxAdditionnal = new double[n]; + final double[] fxAdditional = new double[mapConstraints.size() + (isClosedOrbit ? 6 : 0)]; int i = 0; - if (isClosedOrbit()) { + if (isClosedOrbit) { final AbsolutePVCoordinates apv1i = patchedSpacecraftStates.get(0).getAbsPVA(); - final AbsolutePVCoordinates apvni = patchedSpacecraftStates.get(npoints - 1).getAbsPVA(); + final AbsolutePVCoordinates apvni = patchedSpacecraftStates.get(patchedSpacecraftStates.size() - 1).getAbsPVA(); - fxAdditionnal[0] = apvni.getPosition().getX() - apv1i.getPosition().getX(); - fxAdditionnal[1] = apvni.getPosition().getY() - apv1i.getPosition().getY(); - fxAdditionnal[2] = apvni.getPosition().getZ() - apv1i.getPosition().getZ(); - fxAdditionnal[3] = apvni.getVelocity().getX() - apv1i.getVelocity().getX(); - fxAdditionnal[4] = apvni.getVelocity().getY() - apv1i.getVelocity().getY(); - fxAdditionnal[5] = apvni.getVelocity().getZ() - apv1i.getVelocity().getZ(); + fxAdditional[0] = apvni.getPosition().getX() - apv1i.getPosition().getX(); + fxAdditional[1] = apvni.getPosition().getY() - apv1i.getPosition().getY(); + fxAdditional[2] = apvni.getPosition().getZ() - apv1i.getPosition().getZ(); + fxAdditional[3] = apvni.getVelocity().getX() - apv1i.getVelocity().getX(); + fxAdditional[4] = apvni.getVelocity().getY() - apv1i.getVelocity().getY(); + fxAdditional[5] = apvni.getVelocity().getZ() - apv1i.getVelocity().getZ(); i = 6; } // Update additional constraints - updateAdditionalConstraints(i, fxAdditionnal); - return fxAdditionnal; + updateAdditionalConstraints(i, fxAdditional); + return fxAdditional; + } + + /** {@inheritDoc} */ + @Override + protected int getNumberOfConstraints() { + return super.getNumberOfConstraints() + (isClosedOrbit ? 6 : 0); } } diff --git a/src/main/java/org/orekit/propagation/numerical/cr3bp/STMEquations.java b/src/main/java/org/orekit/propagation/numerical/cr3bp/STMEquations.java index 4c68bc902c..9c227ff69e 100644 --- a/src/main/java/org/orekit/propagation/numerical/cr3bp/STMEquations.java +++ b/src/main/java/org/orekit/propagation/numerical/cr3bp/STMEquations.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,17 +25,14 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AdditionalDerivativesProvider; import org.orekit.propagation.integration.CombinedDerivatives; -import org.orekit.time.AbsoluteDate; /** Class calculating the state transition matrix coefficient for CR3BP Computation. * @see "Dynamical systems, the three-body problem, and space mission design, Koon, Lo, Marsden, Ross" * @author Vincent Mouraux * @since 10.2 */ -@SuppressWarnings("deprecation") public class STMEquations - implements AdditionalDerivativesProvider, - org.orekit.propagation.integration.AdditionalEquations { + implements AdditionalDerivativesProvider { /** Matrix Dimension. */ private static final int DIM = 6; @@ -81,26 +78,6 @@ public SpacecraftState setInitialPhi(final SpacecraftState s) { return s.addAdditionalState(name, phi); } - /** {@inheritDoc} */ - public void init(final SpacecraftState initialState, final AbsoluteDate target) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - AdditionalDerivativesProvider.super.init(initialState, target); - } - - /** {@inheritDoc} */ - public double[] computeDerivatives(final SpacecraftState s, final double[] pDot) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - System.arraycopy(derivatives(s), 0, pDot, 0, pDot.length); - return null; - } - - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - /** {@inheritDoc} */ public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { diff --git a/src/main/java/org/orekit/propagation/numerical/cr3bp/package-info.java b/src/main/java/org/orekit/propagation/numerical/cr3bp/package-info.java index 9f1458566c..6a48283158 100644 --- a/src/main/java/org/orekit/propagation/numerical/cr3bp/package-info.java +++ b/src/main/java/org/orekit/propagation/numerical/cr3bp/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/numerical/package-info.java b/src/main/java/org/orekit/propagation/numerical/package-info.java index 0bfc50ba5c..d0d9f40c08 100644 --- a/src/main/java/org/orekit/propagation/numerical/package-info.java +++ b/src/main/java/org/orekit/propagation/numerical/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/package-info.java b/src/main/java/org/orekit/propagation/package-info.java index 5f3c18f3d6..9ac8b58d83 100644 --- a/src/main/java/org/orekit/propagation/package-info.java +++ b/src/main/java/org/orekit/propagation/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/sampling/FieldOrekitFixedStepHandler.java b/src/main/java/org/orekit/propagation/sampling/FieldOrekitFixedStepHandler.java index 4a3b5b5621..6fdec77a3c 100644 --- a/src/main/java/org/orekit/propagation/sampling/FieldOrekitFixedStepHandler.java +++ b/src/main/java/org/orekit/propagation/sampling/FieldOrekitFixedStepHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,6 +26,7 @@ * href="http://commons.apache.org/math/">commons-math but provides * a space-dynamics interface to the methods.

        * @author Luc Maisonobe + * @param type of the field elements */ public interface FieldOrekitFixedStepHandler> { diff --git a/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepHandler.java b/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepHandler.java index fb43181c94..3d3f3f2281 100644 --- a/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepHandler.java +++ b/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,6 +26,7 @@ * href="http://commons.apache.org/math/"> commons-math but * provides a space-dynamics interface to the methods.

        * @author Luc Maisonobe + * @param type of the field elements */ public interface FieldOrekitStepHandler> { diff --git a/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepInterpolator.java b/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepInterpolator.java index a3461a25f6..fb55ab18bc 100644 --- a/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepInterpolator.java +++ b/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepInterpolator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,11 @@ package org.orekit.propagation.sampling; import org.hipparchus.CalculusFieldElement; +import org.orekit.frames.Frame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; /** This interface is a space-dynamics aware step interpolator. * @@ -26,8 +29,9 @@ * href="http://commons.apache.org/math/"> commons-math but * provides a space-dynamics interface to the methods.

        * @author Luc Maisonobe + * @param type of the field elements */ -public interface FieldOrekitStepInterpolator> { +public interface FieldOrekitStepInterpolator> extends FieldPVCoordinatesProvider { /** * Get the state at previous grid point date. @@ -66,4 +70,12 @@ public interface FieldOrekitStepInterpolator> */ FieldOrekitStepInterpolator restrictStep(FieldSpacecraftState newPreviousState, FieldSpacecraftState newCurrentState); + /** {@inheritDoc} + * @since 12.0 + */ + @Override + default TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { + return getInterpolatedState(date).getPVCoordinates(frame); + } + } diff --git a/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepNormalizer.java b/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepNormalizer.java index 083998b212..e39bb0a297 100644 --- a/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepNormalizer.java +++ b/src/main/java/org/orekit/propagation/sampling/FieldOrekitStepNormalizer.java @@ -28,6 +28,7 @@ * href="http://commons.apache.org/math/">commons-math but * provides a space-dynamics interface to the methods.

        * @author Luc Maisonobe + * @param type of the field elements */ public class FieldOrekitStepNormalizer > implements FieldOrekitStepHandler { @@ -106,7 +107,7 @@ public void handleStep(final FieldOrekitStepInterpolator interpolator) { // use the interpolator to push fixed steps events to the underlying handler FieldAbsoluteDate nextTime = lastState.getDate().shiftedBy(step); - boolean nextInStep = forward ^ (nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0); + boolean nextInStep = forward ^ nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0; while (nextInStep) { // output the stored previous step @@ -117,7 +118,7 @@ public void handleStep(final FieldOrekitStepInterpolator interpolator) { // prepare next iteration nextTime = nextTime.shiftedBy(step); - nextInStep = forward ^ (nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0); + nextInStep = forward ^ nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0; } } diff --git a/src/main/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexer.java b/src/main/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexer.java index 014fad8ab9..b8b7747c54 100644 --- a/src/main/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexer.java +++ b/src/main/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,7 @@ /** This class gathers several {@link OrekitStepHandler} instances into one. * * @author Luc Maisonobe + * @param type of the field elements */ public class FieldStepHandlerMultiplexer> implements FieldOrekitStepHandler { diff --git a/src/main/java/org/orekit/propagation/sampling/MultiSatFixedStepHandler.java b/src/main/java/org/orekit/propagation/sampling/MultiSatFixedStepHandler.java new file mode 100644 index 0000000000..7a73638376 --- /dev/null +++ b/src/main/java/org/orekit/propagation/sampling/MultiSatFixedStepHandler.java @@ -0,0 +1,74 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.sampling; + +import java.util.List; + +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; + +/** This interface is a space-dynamics aware fixed step handler for {@link + * org.orekit.propagation.PropagatorsParallelizer multi-sat propagation}. + * + *

        It is a multi-satellite version of the {@link OrekitFixedStepHandler}.

        + * @author Luc Maisonobe + * @since 12.0 + */ +public interface MultiSatFixedStepHandler { + + /** Initialize step handler at the start of a propagation. + *

        + * This method is called once at the start of the propagation. It + * may be used by the step handler to initialize some internal data + * if needed. + *

        + *

        + * The default method does nothing + *

        + * @param states0 initial states, one for each satellite in the same order + * used to {@link org.orekit.propagation.PropagatorsParallelizer#PropagatorsParallelizer(List, + * double, MultiSatFixedStepHandler) build} the {@link org.orekit.propagation.PropagatorsParallelizer + * multi-sat propagator}. + * @param t target time for the integration + * @param step the duration in seconds of the fixed step. This value is + * positive even if propagation is backwards. + */ + default void init(final List states0, final AbsoluteDate t, final double step) { + // nothing by default + } + + /** Handle the current step. + *

        + * When called by {@link org.orekit.propagation.PropagatorsParallelizer PropagatorsParallelizer}, + * all states have the same date. + *

        + * @param states states in the same order + * used to {@link org.orekit.propagation.PropagatorsParallelizer#PropagatorsParallelizer(List, + * double, MultiSatFixedStepHandler) build} the {@link org.orekit.propagation.PropagatorsParallelizer + * multi-sat propagator} + */ + void handleStep(List states); + + /** + * Finalize propagation. + * @param finalStates states at propagation end + */ + default void finish(final List finalStates) { + // nothing by default + } + +} diff --git a/src/main/java/org/orekit/propagation/sampling/MultiSatStepHandler.java b/src/main/java/org/orekit/propagation/sampling/MultiSatStepHandler.java index f183a55a42..8f0601d495 100644 --- a/src/main/java/org/orekit/propagation/sampling/MultiSatStepHandler.java +++ b/src/main/java/org/orekit/propagation/sampling/MultiSatStepHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/sampling/MultisatStepNormalizer.java b/src/main/java/org/orekit/propagation/sampling/MultisatStepNormalizer.java new file mode 100644 index 0000000000..12f0b5c3e1 --- /dev/null +++ b/src/main/java/org/orekit/propagation/sampling/MultisatStepNormalizer.java @@ -0,0 +1,132 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.sampling; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.hipparchus.util.FastMath; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; + +/** + * This class wraps an object implementing {@link MultiSatFixedStepHandler} + * into a {@link MultiSatStepHandler}. + + *

        It mirrors the StepNormalizer interface from Hipparchus but + * provides a space-dynamics interface to the methods.

        + * @author Luc Maisonobe + * @since 12.0 + */ +public class MultisatStepNormalizer implements MultiSatStepHandler { + + /** Fixed time step. */ + private double h; + + /** Underlying fixed step handler. */ + private MultiSatFixedStepHandler handler; + + /** Last State vectors. */ + private List lastStates; + + /** Integration direction indicator. */ + private boolean forward; + + /** Simple constructor. + * @param h fixed time step (sign is not used) + * @param handler fixed time step handler to wrap + */ + public MultisatStepNormalizer(final double h, final MultiSatFixedStepHandler handler) { + this.h = FastMath.abs(h); + this.handler = handler; + this.lastStates = null; + this.forward = true; + } + + /** Get the fixed time step. + * @return fixed time step + */ + public double getFixedTimeStep() { + return h; + } + + /** Get the underlying fixed step handler. + * @return underlying fixed step handler + */ + public MultiSatFixedStepHandler getFixedStepHandler() { + return handler; + } + + /** {@inheritDoc} */ + public void init(final List s0, final AbsoluteDate t) { + lastStates = new ArrayList<>(s0); + forward = true; + handler.init(s0, t, h); + } + + /** {@inheritDoc} */ + public void handleStep(final List interpolators) { + + if (lastStates == null) { + // initialize lastState in the first step case + lastStates = interpolators.stream().map(i -> i.getPreviousState()).collect(Collectors.toList()); + } + + // take the propagation direction into account + double step = h; + forward = interpolators.get(0).isForward(); + if (!forward) { + step = -h; + } + + + // use the interpolator to push fixed steps events to the underlying handler + AbsoluteDate nextTime = lastStates.get(0).getDate().shiftedBy(step); + boolean nextInStep = forward ^ nextTime.compareTo(interpolators.get(0).getCurrentState().getDate()) > 0; + while (nextInStep) { + + // output the stored previous step + handler.handleStep(lastStates); + + // store the next step + final AbsoluteDate time = nextTime; + lastStates = interpolators.stream().map(i -> i.getInterpolatedState(time)).collect(Collectors.toList()); + + // prepare next iteration + nextTime = nextTime.shiftedBy(step); + nextInStep = forward ^ nextTime.compareTo(interpolators.get(0).getCurrentState().getDate()) > 0; + + } + + } + + /** {@inheritDoc} */ + @Override + public void finish(final List finalStates) { + + // there will be no more steps, + // the stored one should be handled now + handler.handleStep(lastStates); + + // and the final state handled too + handler.finish(finalStates); + + } + +} diff --git a/src/main/java/org/orekit/propagation/sampling/OrekitFixedStepHandler.java b/src/main/java/org/orekit/propagation/sampling/OrekitFixedStepHandler.java index 67fe5c1185..12c36f94ab 100644 --- a/src/main/java/org/orekit/propagation/sampling/OrekitFixedStepHandler.java +++ b/src/main/java/org/orekit/propagation/sampling/OrekitFixedStepHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/sampling/OrekitStepHandler.java b/src/main/java/org/orekit/propagation/sampling/OrekitStepHandler.java index df39cb5add..387ee8a18e 100644 --- a/src/main/java/org/orekit/propagation/sampling/OrekitStepHandler.java +++ b/src/main/java/org/orekit/propagation/sampling/OrekitStepHandler.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/sampling/OrekitStepInterpolator.java b/src/main/java/org/orekit/propagation/sampling/OrekitStepInterpolator.java index 60630102e6..b7a193119c 100644 --- a/src/main/java/org/orekit/propagation/sampling/OrekitStepInterpolator.java +++ b/src/main/java/org/orekit/propagation/sampling/OrekitStepInterpolator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,11 @@ */ package org.orekit.propagation.sampling; +import org.orekit.frames.Frame; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedPVCoordinates; /** This interface is a space-dynamics aware step interpolator. * @@ -26,7 +29,7 @@ * provides a space-dynamics interface to the methods.

        * @author Luc Maisonobe */ -public interface OrekitStepInterpolator { +public interface OrekitStepInterpolator extends PVCoordinatesProvider { /** * Get the state at previous grid point date. @@ -92,4 +95,12 @@ public interface OrekitStepInterpolator { */ OrekitStepInterpolator restrictStep(SpacecraftState newPreviousState, SpacecraftState newCurrentState); + /** {@inheritDoc} + * @since 12.0 + */ + @Override + default TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { + return getInterpolatedState(date).getPVCoordinates(frame); + } + } diff --git a/src/main/java/org/orekit/propagation/sampling/OrekitStepNormalizer.java b/src/main/java/org/orekit/propagation/sampling/OrekitStepNormalizer.java index eed6ec4034..b245cffe9a 100644 --- a/src/main/java/org/orekit/propagation/sampling/OrekitStepNormalizer.java +++ b/src/main/java/org/orekit/propagation/sampling/OrekitStepNormalizer.java @@ -70,16 +70,6 @@ public OrekitFixedStepHandler getFixedStepHandler() { return handler; } - /** Determines whether this handler needs dense output. - * This handler needs dense output in order to provide data at - * regularly spaced steps regardless of the steps the propagator - * uses, so this method always returns true. - * @return always true - */ - public boolean requiresDenseOutput() { - return true; - } - /** {@inheritDoc} */ public void init(final SpacecraftState s0, final AbsoluteDate t) { lastState = null; @@ -114,7 +104,7 @@ public void handleStep(final OrekitStepInterpolator interpolator) { // use the interpolator to push fixed steps events to the underlying handler AbsoluteDate nextTime = lastState.getDate().shiftedBy(step); - boolean nextInStep = forward ^ (nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0); + boolean nextInStep = forward ^ nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0; while (nextInStep) { // output the stored previous step @@ -125,7 +115,7 @@ public void handleStep(final OrekitStepInterpolator interpolator) { // prepare next iteration nextTime = nextTime.shiftedBy(step); - nextInStep = forward ^ (nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0); + nextInStep = forward ^ nextTime.compareTo(interpolator.getCurrentState().getDate()) > 0; } } diff --git a/src/main/java/org/orekit/propagation/sampling/StepHandlerMultiplexer.java b/src/main/java/org/orekit/propagation/sampling/StepHandlerMultiplexer.java index b489cad412..6147051ca8 100644 --- a/src/main/java/org/orekit/propagation/sampling/StepHandlerMultiplexer.java +++ b/src/main/java/org/orekit/propagation/sampling/StepHandlerMultiplexer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/sampling/package-info.java b/src/main/java/org/orekit/propagation/sampling/package-info.java index d5c0f61ba8..4a1d0f79ed 100644 --- a/src/main/java/org/orekit/propagation/sampling/package-info.java +++ b/src/main/java/org/orekit/propagation/sampling/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTGradientConverter.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTGradientConverter.java index e47bd1c023..f3c960d4b8 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTGradientConverter.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTGradientConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,7 +21,7 @@ import org.orekit.attitudes.FieldAttitude; import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.integration.AbstractGradientConverter; @@ -65,7 +65,7 @@ class DSSTGradientConverter extends AbstractGradientConverter { final FieldOrbit gOrbit = new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, l, - PositionAngle.MEAN, + PositionAngleType.MEAN, state.getFrame(), dateField, gMu); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java index 17987ba847..5eab3b4fb9 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTHarvester.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,6 +23,8 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; @@ -32,6 +34,8 @@ import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** Harvester between two-dimensional Jacobian matrices and one-dimensional {@link * SpacecraftState#getAdditionalState(String) additional state arrays}. @@ -75,7 +79,7 @@ public class DSSTHarvester extends AbstractMatricesHarvester { *

        * The arguments for initial matrices must be compatible with the * {@link org.orekit.orbits.OrbitType#EQUINOCTIAL equinoctial orbit type} - * and {@link org.orekit.orbits.PositionAngle position angle} that will be used by propagator + * and {@link PositionAngleType position angle} that will be used by propagator *

        * @param propagator propagator bound to this harvester * @param stmName State Transition Matrix state name @@ -237,7 +241,7 @@ public void initializeFieldShortPeriodTerms(final SpacecraftState reference) { // Convert to Gradient final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] dsParameters = converter.getParameters(dsState, forceModel); + final Gradient[] dsParameters = converter.getParametersAtStateDate(dsState, forceModel); final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(dsState.getOrbit(), I); // Initialize the "Field" short periodic terms in OSCULATING mode @@ -316,21 +320,25 @@ public void setReferenceState(final SpacecraftState reference) { for (ParameterDriver driver : forceModel.getParametersDrivers()) { if (driver.isSelected()) { - // get the partials derivatives for this driver - DoubleArrayDictionary.Entry entry = shortPeriodDerivativesJacobianColumns.getEntry(driver.getName()); - if (entry == null) { - // create an entry filled with zeroes - shortPeriodDerivativesJacobianColumns.put(driver.getName(), new double[STATE_DIMENSION]); - entry = shortPeriodDerivativesJacobianColumns.getEntry(driver.getName()); + final TimeSpanMap driverNameSpanMap = driver.getNamesSpanMap(); + // for each span (for each estimated value) corresponding name is added + + for (Span span = driverNameSpanMap.getFirstSpan(); span != null; span = span.next()) { + // get the partials derivatives for this driver + DoubleArrayDictionary.Entry entry = shortPeriodDerivativesJacobianColumns.getEntry(span.getData()); + if (entry == null) { + // create an entry filled with zeroes + shortPeriodDerivativesJacobianColumns.put(span.getData(), new double[STATE_DIMENSION]); + entry = shortPeriodDerivativesJacobianColumns.getEntry(span.getData()); + } + + // add the contribution of the current force model + entry.increment(new double[] { + derivativesASP[paramsIndex], derivativesExSP[paramsIndex], derivativesEySP[paramsIndex], + derivativesHxSP[paramsIndex], derivativesHySP[paramsIndex], derivativesLSP[paramsIndex] + }); + ++paramsIndex; } - - // add the contribution of the current force model - entry.increment(new double[] { - derivativesASP[paramsIndex], derivativesExSP[paramsIndex], derivativesEySP[paramsIndex], - derivativesHxSP[paramsIndex], derivativesHySP[paramsIndex], derivativesLSP[paramsIndex] - }); - ++paramsIndex; - } } } @@ -347,4 +355,16 @@ private void addToRow(final double[] derivatives, final int index) { } } + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + return propagator.getOrbitType(); + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + return propagator.getPositionAngleType(); + } + } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTIntegrableJacobianColumnGenerator.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTIntegrableJacobianColumnGenerator.java index cc2af9fce5..0b655ed2a4 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTIntegrableJacobianColumnGenerator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTIntegrableJacobianColumnGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -79,7 +79,7 @@ public int getDimension() { *

        */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !state.hasAdditionalStateDerivative(stmName); } @@ -97,13 +97,6 @@ public void partialsComputed(final SpacecraftState state, final RealMatrix facto } - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - /** {@inheritDoc} */ @Override public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTJacobiansMapper.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTJacobiansMapper.java deleted file mode 100644 index 67a991d54d..0000000000 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTJacobiansMapper.java +++ /dev/null @@ -1,288 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.semianalytical.dsst; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.errors.OrekitInternalError; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.PropagationType; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.integration.AbstractJacobiansMapper; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; -import org.orekit.propagation.semianalytical.dsst.forces.FieldShortPeriodTerms; -import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; - -/** Mapper between two-dimensional Jacobian matrices and one-dimensional {@link - * SpacecraftState#getAdditionalState(String) additional state arrays}. - *

        - * This class does not hold the states by itself. Instances of this class are guaranteed - * to be immutable. - *

        - * @author Luc Maisonobe - * @author Bryan Cazabonne - * @see org.orekit.propagation.semianalytical.dsst.DSSTPartialDerivativesEquations - * @see org.orekit.propagation.semianalytical.dsst.DSSTPropagator - * @see SpacecraftState#getAdditionalState(String) - * @see org.orekit.propagation.AbstractPropagator - */ -public class DSSTJacobiansMapper extends AbstractJacobiansMapper { - - /** State dimension, fixed to 6. - * @since 9.0 - */ - public static final int STATE_DIMENSION = 6; - - /** Retrograde factor I. - *

        - * DSST model needs equinoctial orbit as internal representation. - * Classical equinoctial elements have discontinuities when inclination - * is close to zero. In this representation, I = +1.
        - * To avoid this discontinuity, another representation exists and equinoctial - * elements can be expressed in a different way, called "retrograde" orbit. - * This implies I = -1.
        - * As Orekit doesn't implement the retrograde orbit, I is always set to +1. - * But for the sake of consistency with the theory, the retrograde factor - * has been kept in the formulas. - *

        - */ - private static final int I = 1; - - /** Name. */ - private String name; - - /** Selected parameters for Jacobian computation. */ - private final ParameterDriversList parameters; - - /** Parameters map. */ - private Map map; - - /** Propagator computing state evolution. */ - private final DSSTPropagator propagator; - - /** Placeholder for the derivatives of the short period terms.*/ - private double[] shortPeriodDerivatives; - - /** Type of the orbit used for the propagation.*/ - private PropagationType propagationType; - - /** Simple constructor. - * @param name name of the Jacobians - * @param parameters selected parameters for Jacobian computation - * @param propagator the propagator that will handle the orbit propagation - * @param map parameters map - * @param propagationType type of the orbit used for the propagation (mean or osculating) - */ - DSSTJacobiansMapper(final String name, - final ParameterDriversList parameters, - final DSSTPropagator propagator, - final Map map, - final PropagationType propagationType) { - - super(name, parameters); - - shortPeriodDerivatives = null; - - this.parameters = parameters; - this.name = name; - this.propagator = propagator; - this.map = map; - this.propagationType = propagationType; - - } - - /** {@inheritDoc} */ - public void setInitialJacobians(final SpacecraftState state, final double[][] dY1dY0, - final double[][] dY1dP, final double[] p) { - - // map the converted state Jacobian to one-dimensional array - int index = 0; - for (int i = 0; i < STATE_DIMENSION; ++i) { - for (int j = 0; j < STATE_DIMENSION; ++j) { - p[index++] = (i == j) ? 1.0 : 0.0; - } - } - - if (parameters.getNbParams() != 0) { - - // map the converted parameters Jacobian to one-dimensional array - for (int i = 0; i < STATE_DIMENSION; ++i) { - for (int j = 0; j < parameters.getNbParams(); ++j) { - p[index++] = dY1dP[i][j]; - } - } - } - - } - - /** {@inheritDoc} */ - public void getStateJacobian(final SpacecraftState state, final double[][] dYdY0) { - - // extract additional state - final double[] p = state.getAdditionalState(name); - - for (int i = 0; i < STATE_DIMENSION; i++) { - final double[] row = dYdY0[i]; - for (int j = 0; j < STATE_DIMENSION; j++) { - row[j] = p[i * STATE_DIMENSION + j] + shortPeriodDerivatives[i * STATE_DIMENSION + j]; - } - } - - } - - - /** {@inheritDoc} */ - public void getParametersJacobian(final SpacecraftState state, final double[][] dYdP) { - - if (parameters.getNbParams() != 0) { - - // extract the additional state - final double[] p = state.getAdditionalState(name); - - for (int i = 0; i < STATE_DIMENSION; i++) { - final double[] row = dYdP[i]; - for (int j = 0; j < parameters.getNbParams(); j++) { - row[j] = p[STATE_DIMENSION * STATE_DIMENSION + (j + parameters.getNbParams() * i)] + - shortPeriodDerivatives[STATE_DIMENSION * STATE_DIMENSION + (j + parameters.getNbParams() * i)]; - } - } - - } - - } - - /** Compute the derivatives of the short period terms related to the additional state parameters. - * @param s Current state information: date, kinematics, attitude, and additional state - * @deprecated as of 11.1, replaced by {@link #setReferenceState(SpacecraftState)} - */ - @Deprecated - public void setShortPeriodJacobians(final SpacecraftState s) { - setReferenceState(s); - } - - /** {@inheritDoc} */ - @SuppressWarnings("unchecked") - @Override - public void setReferenceState(final SpacecraftState reference) { - - final double[] p = reference.getAdditionalState(name); - if (shortPeriodDerivatives == null) { - shortPeriodDerivatives = new double[p.length]; - } - - switch (propagationType) { - case MEAN : - break; - case OSCULATING : - // initialize Jacobians to zero - final int paramDim = parameters.getNbParams(); - final int dim = 6; - final double[][] dShortPerioddState = new double[dim][dim]; - final double[][] dShortPerioddParam = new double[dim][paramDim]; - final DSSTGradientConverter converter = new DSSTGradientConverter(reference, propagator.getAttitudeProvider()); - - // Compute Jacobian - for (final DSSTForceModel forceModel : propagator.getAllForceModels()) { - - final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] dsParameters = converter.getParameters(dsState, forceModel); - final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(dsState.getOrbit(), I); - - final Gradient zero = dsState.getDate().getField().getZero(); - final List> shortPeriodTerms = new ArrayList<>(); - shortPeriodTerms.addAll(forceModel.initializeShortPeriodTerms(auxiliaryElements, propagationType, dsParameters)); - forceModel.updateShortPeriodTerms(dsParameters, dsState); - final Gradient[] shortPeriod = new Gradient[6]; - Arrays.fill(shortPeriod, zero); - for (final FieldShortPeriodTerms spt : shortPeriodTerms) { - final Gradient[] spVariation = spt.value(dsState.getOrbit()); - for (int i = 0; i < spVariation .length; i++) { - shortPeriod[i] = shortPeriod[i].add(spVariation[i]); - } - } - - final double[] derivativesASP = shortPeriod[0].getGradient(); - final double[] derivativesExSP = shortPeriod[1].getGradient(); - final double[] derivativesEySP = shortPeriod[2].getGradient(); - final double[] derivativesHxSP = shortPeriod[3].getGradient(); - final double[] derivativesHySP = shortPeriod[4].getGradient(); - final double[] derivativesLSP = shortPeriod[5].getGradient(); - - // update Jacobian with respect to state - addToRow(derivativesASP, 0, dShortPerioddState); - addToRow(derivativesExSP, 1, dShortPerioddState); - addToRow(derivativesEySP, 2, dShortPerioddState); - addToRow(derivativesHxSP, 3, dShortPerioddState); - addToRow(derivativesHySP, 4, dShortPerioddState); - addToRow(derivativesLSP, 5, dShortPerioddState); - - int index = converter.getFreeStateParameters(); - for (ParameterDriver driver : forceModel.getParametersDrivers()) { - if (driver.isSelected()) { - final int parameterIndex = map.get(driver); - dShortPerioddParam[0][parameterIndex] += derivativesASP[index]; - dShortPerioddParam[1][parameterIndex] += derivativesExSP[index]; - dShortPerioddParam[2][parameterIndex] += derivativesEySP[index]; - dShortPerioddParam[3][parameterIndex] += derivativesHxSP[index]; - dShortPerioddParam[4][parameterIndex] += derivativesHySP[index]; - dShortPerioddParam[5][parameterIndex] += derivativesLSP[index]; - ++index; - } - } - } - - // Get orbital short period derivatives with respect orbital elements. - for (int i = 0; i < dim; i++) { - for (int j = 0; j < dim; j++) { - shortPeriodDerivatives[j + dim * i] = dShortPerioddState[i][j]; - } - } - - // Get orbital short period derivatives with respect to model parameters. - final int columnTop = dim * dim; - for (int k = 0; k < paramDim; k++) { - for (int i = 0; i < dim; ++i) { - shortPeriodDerivatives[columnTop + (i + dim * k)] = dShortPerioddParam[i][k]; - } - } - break; - default: - throw new OrekitInternalError(null); - } - } - - /** Fill Jacobians rows. - * @param derivatives derivatives of a component - * @param index component index (0 for a, 1 for ex, 2 for ey, 3 for hx, 4 for hy, 5 for l) - * @param dMeanElementRatedElement Jacobian of mean elements rate with respect to mean elements - */ - private void addToRow(final double[] derivatives, final int index, - final double[][] dMeanElementRatedElement) { - - for (int i = 0; i < 6; i++) { - dMeanElementRatedElement[index][i] += derivatives[i]; - } - - } - -} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPartialDerivativesEquations.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPartialDerivativesEquations.java deleted file mode 100644 index 4a6b770239..0000000000 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPartialDerivativesEquations.java +++ /dev/null @@ -1,390 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.semianalytical.dsst; - -import java.util.IdentityHashMap; -import java.util.Map; - -import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.PropagationType; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.integration.AdditionalDerivativesProvider; -import org.orekit.propagation.integration.CombinedDerivatives; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; -import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; -import org.orekit.time.AbsoluteDate; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParameterDriversList; - -/** {@link AdditionalDerivativesProvider derivatives provider} computing the partial derivatives - * of the state (orbit) with respect to initial state and force models parameters. - *

        - * This set of equations are automatically added to a {@link DSSTPropagator DSST propagator} - * in order to compute partial derivatives of the orbit along with the orbit itself. This is - * useful for example in orbit determination applications. - *

        - *

        - * The partial derivatives with respect to initial state are dimension 6 (orbit only). - *

        - *

        - * The partial derivatives with respect to force models parameters has a dimension - * equal to the number of selected parameters. Parameters selection is implemented at - * {@link DSSTForceModel DSST force models} level. Users must retrieve a {@link ParameterDriver - * parameter driver} by looping on all drivers using {@link DSSTForceModel#getParametersDrivers()} - * and then select it by calling {@link ParameterDriver#setSelected(boolean) setSelected(true)}. - *

        - * @author Bryan Cazabonne - * @since 10.0 - * @deprecated as of 11.1, replaced by {@link - * org.orekit.propagation.Propagator#setupMatricesComputation(String, - * org.hipparchus.linear.RealMatrix, org.orekit.utils.DoubleArrayDictionary)} - */ -@Deprecated -public class DSSTPartialDerivativesEquations - implements AdditionalDerivativesProvider, - org.orekit.propagation.integration.AdditionalEquations { - - /** Retrograde factor I. - *

        - * DSST model needs equinoctial orbit as internal representation. - * Classical equinoctial elements have discontinuities when inclination - * is close to zero. In this representation, I = +1.
        - * To avoid this discontinuity, another representation exists and equinoctial - * elements can be expressed in a different way, called "retrograde" orbit. - * This implies I = -1.
        - * As Orekit doesn't implement the retrograde orbit, I is always set to +1. - * But for the sake of consistency with the theory, the retrograde factor - * has been kept in the formulas. - *

        - */ - private static final int I = 1; - - /** Propagator computing state evolution. */ - private final DSSTPropagator propagator; - - /** Selected parameters for Jacobian computation. */ - private ParameterDriversList selected; - - /** Parameters map. */ - private Map map; - - /** Name. */ - private final String name; - - /** Flag for Jacobian matrices initialization. */ - private boolean initialized; - - /** Type of the orbit used for the propagation.*/ - private PropagationType propagationType; - - /** Simple constructor. - *

        - * Upon construction, this set of equations is automatically added to - * the propagator by calling its {@link - * DSSTPropagator#addAdditionalDerivativesProvider(AdditionalDerivativesProvider)} method. So - * there is no need to call this method explicitly for these equations. - *

        - * @param name name of the partial derivatives equations - * @param propagator the propagator that will handle the orbit propagation - * @param propagationType type of the orbit used for the propagation (mean or osculating) - */ - public DSSTPartialDerivativesEquations(final String name, - final DSSTPropagator propagator, - final PropagationType propagationType) { - this.name = name; - this.selected = null; - this.map = null; - this.propagator = propagator; - this.initialized = false; - this.propagationType = propagationType; - propagator.addAdditionalDerivativesProvider(this); - } - - /** {@inheritDoc} */ - public String getName() { - return name; - } - - /** {@inheritDoc} */ - @Override - public int getDimension() { - freezeParametersSelection(); - return 6 * (6 + selected.getNbParams()); - } - - /** Freeze the selected parameters from the force models. - */ - private void freezeParametersSelection() { - if (selected == null) { - - // first pass: gather all parameters, binding similar names together - selected = new ParameterDriversList(); - for (final DSSTForceModel provider : propagator.getAllForceModels()) { - for (final ParameterDriver driver : provider.getParametersDrivers()) { - selected.add(driver); - } - } - - // second pass: now that shared parameter names are bound together, - // their selections status have been synchronized, we can filter them - selected.filter(true); - - // third pass: sort parameters lexicographically - selected.sort(); - - // fourth pass: set up a map between parameters drivers and matrices columns - map = new IdentityHashMap(); - int parameterIndex = 0; - for (final ParameterDriver selectedDriver : selected.getDrivers()) { - for (final DSSTForceModel provider : propagator.getAllForceModels()) { - for (final ParameterDriver driver : provider.getParametersDrivers()) { - if (driver.getName().equals(selectedDriver.getName())) { - map.put(driver, parameterIndex); - } - } - } - ++parameterIndex; - } - - } - } - - /** Set the initial value of the Jacobian with respect to state and parameter. - *

        - * This method is equivalent to call {@link #setInitialJacobians(SpacecraftState, - * double[][], double[][])} with dYdY0 set to the identity matrix and dYdP set - * to a zero matrix. - *

        - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, so proper matrices dimensions are used. - *

        - * @param s0 initial state - * @return state with initial Jacobians added - */ - public SpacecraftState setInitialJacobians(final SpacecraftState s0) { - freezeParametersSelection(); - final int stateDimension = 6; - final double[][] dYdY0 = new double[stateDimension][stateDimension]; - final double[][] dYdP = new double[stateDimension][selected.getNbParams()]; - for (int i = 0; i < stateDimension; ++i) { - dYdY0[i][i] = 1.0; - } - return setInitialJacobians(s0, dYdY0, dYdP); - } - - /** Set the initial value of the Jacobian with respect to state and parameter. - *

        - * The returned state must be added to the propagator (it is not done - * automatically, as the user may need to add more states to it). - *

        - *

        - * The force models parameters for which partial derivatives are desired, - * must have been {@link ParameterDriver#setSelected(boolean) selected} - * before this method is called, and the {@code dY1dP} matrix dimension must - * be consistent with the selection. - *

        - * @param s1 current state - * @param dY1dY0 Jacobian of current state at time t₁ with respect - * to state at some previous time t₀ (must be 6x6) - * @param dY1dP Jacobian of current state at time t₁ with respect - * to parameters (may be null if no parameters are selected) - * @return state with initial Jacobians added - */ - public SpacecraftState setInitialJacobians(final SpacecraftState s1, - final double[][] dY1dY0, final double[][] dY1dP) { - - freezeParametersSelection(); - - // Check dimensions - final int stateDim = dY1dY0.length; - if (stateDim != 6 || stateDim != dY1dY0[0].length) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_6X6, - stateDim, dY1dY0[0].length); - } - if (dY1dP != null && stateDim != dY1dP.length) { - throw new OrekitException(OrekitMessages.STATE_AND_PARAMETERS_JACOBIANS_ROWS_MISMATCH, - stateDim, dY1dP.length); - } - if (dY1dP == null && selected.getNbParams() != 0 || - dY1dP != null && selected.getNbParams() != dY1dP[0].length) { - throw new OrekitException(new OrekitException(OrekitMessages.INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH, - dY1dP == null ? 0 : dY1dP[0].length, selected.getNbParams())); - } - - // store the matrices as a single dimension array - initialized = true; - final DSSTJacobiansMapper mapper = getMapper(); - final double[] p = new double[mapper.getAdditionalStateDimension()]; - mapper.setInitialJacobians(s1, dY1dY0, dY1dP, p); - - // set value in propagator - return s1.addAdditionalState(name, p); - - } - - /** Get a mapper between two-dimensional Jacobians and one-dimensional additional state. - * @return a mapper between two-dimensional Jacobians and one-dimensional additional state, - * with the same name as the instance - * @see #setInitialJacobians(SpacecraftState) - * @see #setInitialJacobians(SpacecraftState, double[][], double[][]) - */ - public DSSTJacobiansMapper getMapper() { - if (!initialized) { - throw new OrekitException(OrekitMessages.STATE_JACOBIAN_NOT_INITIALIZED); - } - return new DSSTJacobiansMapper(name, selected, propagator, map, propagationType); - } - - /** {@inheritDoc} */ - public void init(final SpacecraftState initialState, final AbsoluteDate target) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - AdditionalDerivativesProvider.super.init(initialState, target); - } - - /** {@inheritDoc} */ - public double[] computeDerivatives(final SpacecraftState s, final double[] pDot) { - // FIXME: remove in 12.0 when AdditionalEquations is removed - System.arraycopy(derivatives(s), 0, pDot, 0, pDot.length); - return null; - } - - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - - /** {@inheritDoc} */ - public CombinedDerivatives combinedDerivatives(final SpacecraftState s) { - - // initialize Jacobians to zero - final int paramDim = selected.getNbParams(); - final int dim = 6; - final double[][] dMeanElementRatedParam = new double[dim][paramDim]; - final double[][] dMeanElementRatedElement = new double[dim][dim]; - final DSSTGradientConverter converter = new DSSTGradientConverter(s, propagator.getAttitudeProvider()); - - // Compute Jacobian - for (final DSSTForceModel forceModel : propagator.getAllForceModels()) { - - final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] parameters = converter.getParameters(dsState, forceModel); - final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(dsState.getOrbit(), I); - - // "field" initialization of the force model if it was not done before - forceModel.initializeShortPeriodTerms(auxiliaryElements, propagationType, parameters); - final Gradient[] meanElementRate = forceModel.getMeanElementRate(dsState, auxiliaryElements, parameters); - final double[] derivativesA = meanElementRate[0].getGradient(); - final double[] derivativesEx = meanElementRate[1].getGradient(); - final double[] derivativesEy = meanElementRate[2].getGradient(); - final double[] derivativesHx = meanElementRate[3].getGradient(); - final double[] derivativesHy = meanElementRate[4].getGradient(); - final double[] derivativesL = meanElementRate[5].getGradient(); - - // update Jacobian with respect to state - addToRow(derivativesA, 0, dMeanElementRatedElement); - addToRow(derivativesEx, 1, dMeanElementRatedElement); - addToRow(derivativesEy, 2, dMeanElementRatedElement); - addToRow(derivativesHx, 3, dMeanElementRatedElement); - addToRow(derivativesHy, 4, dMeanElementRatedElement); - addToRow(derivativesL, 5, dMeanElementRatedElement); - - int index = converter.getFreeStateParameters(); - for (ParameterDriver driver : forceModel.getParametersDrivers()) { - if (driver.isSelected()) { - final int parameterIndex = map.get(driver); - dMeanElementRatedParam[0][parameterIndex] += derivativesA[index]; - dMeanElementRatedParam[1][parameterIndex] += derivativesEx[index]; - dMeanElementRatedParam[2][parameterIndex] += derivativesEy[index]; - dMeanElementRatedParam[3][parameterIndex] += derivativesHx[index]; - dMeanElementRatedParam[4][parameterIndex] += derivativesHy[index]; - dMeanElementRatedParam[5][parameterIndex] += derivativesL[index]; - ++index; - } - } - - } - - // The variational equations of the complete state Jacobian matrix have the following form: - - // [ Adot ] = [ dMeanElementRatedElement ] * [ A ] - - // The A matrix and its derivative (Adot) are 6 * 6 matrices - - // The following loops compute these expression taking care of the mapping of the - // A matrix into the single dimension array p and of the mapping of the - // Adot matrix into the single dimension array pDot. - - final double[] p = s.getAdditionalState(getName()); - final double[] pDot = new double[p.length]; - - for (int i = 0; i < dim; i++) { - final double[] dMeanElementRatedElementi = dMeanElementRatedElement[i]; - for (int j = 0; j < dim; j++) { - pDot[j + dim * i] = - dMeanElementRatedElementi[0] * p[j] + dMeanElementRatedElementi[1] * p[j + dim] + dMeanElementRatedElementi[2] * p[j + 2 * dim] + - dMeanElementRatedElementi[3] * p[j + 3 * dim] + dMeanElementRatedElementi[4] * p[j + 4 * dim] + dMeanElementRatedElementi[5] * p[j + 5 * dim]; - } - } - - final int columnTop = dim * dim; - for (int k = 0; k < paramDim; k++) { - // the variational equations of the parameters Jacobian matrix are computed - // one column at a time, they have the following form: - - // [ Bdot ] = [ dMeanElementRatedElement ] * [ B ] + [ dMeanElementRatedParam ] - - // The B sub-columns and its derivative (Bdot) are 6 elements columns. - - // The following loops compute this expression taking care of the mapping of the - // B columns into the single dimension array p and of the mapping of the - // Bdot columns into the single dimension array pDot. - - for (int i = 0; i < dim; ++i) { - final double[] dMeanElementRatedElementi = dMeanElementRatedElement[i]; - pDot[columnTop + (i + dim * k)] = - dMeanElementRatedParam[i][k] + - dMeanElementRatedElementi[0] * p[columnTop + k] + dMeanElementRatedElementi[1] * p[columnTop + k + paramDim] + dMeanElementRatedElementi[2] * p[columnTop + k + 2 * paramDim] + - dMeanElementRatedElementi[3] * p[columnTop + k + 3 * paramDim] + dMeanElementRatedElementi[4] * p[columnTop + k + 4 * paramDim] + dMeanElementRatedElementi[5] * p[columnTop + k + 5 * paramDim]; - } - } - - return new CombinedDerivatives(pDot, null); - - } - - /** Fill Jacobians rows. - * @param derivatives derivatives of a component - * @param index component index (0 for a, 1 for ex, 2 for ey, 3 for hx, 4 for hy, 5 for l) - * @param dMeanElementRatedElement Jacobian of mean elements rate with respect to mean elements - */ - private void addToRow(final double[] derivatives, final int index, - final double[][] dMeanElementRatedElement) { - - for (int i = 0; i < 6; i++) { - dMeanElementRatedElement[index][i] += derivatives[i]; - } - - } - -} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java index 5a70c409b0..9d245f8981 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -42,13 +42,12 @@ import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.AbstractMatricesHarvester; import org.orekit.propagation.MatricesHarvester; import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.integration.AbstractIntegratedPropagator; import org.orekit.propagation.integration.AdditionalDerivativesProvider; import org.orekit.propagation.integration.StateMapper; @@ -66,6 +65,8 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; /** * This class propagates {@link org.orekit.orbits.Orbit orbits} using the DSST theory. @@ -90,7 +91,7 @@ *

        * From these configuration parameters, only the initial state is mandatory. * The default propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial} - * parameters with {@link PositionAngle#TRUE true} longitude argument. + * parameters with {@link PositionAngleType#TRUE true} longitude argument. * The central attraction coefficient used to define the initial orbit will be used. * However, specifying only the initial state would mean the propagator would use * only Keplerian forces. In this case, the simpler @@ -202,7 +203,7 @@ public DSSTPropagator(final ODEIntegrator integrator, initMapper(); // DSST uses only equinoctial orbits and mean longitude argument setOrbitType(OrbitType.EQUINOCTIAL); - setPositionAngleType(PositionAngle.MEAN); + setPositionAngleType(PositionAngleType.MEAN); setAttitudeProvider(attitudeProvider); setInterpolationGridToFixedNumberOfPoints(INTERPOLATION_POINTS_PER_STEP); } @@ -324,8 +325,12 @@ protected List getJacobiansColumnsNames() { final List columnsNames = new ArrayList<>(); for (final DSSTForceModel forceModel : getAllForceModels()) { for (final ParameterDriver driver : forceModel.getParametersDrivers()) { - if (driver.isSelected() && !columnsNames.contains(driver.getName())) { - columnsNames.add(driver.getName()); + if (driver.isSelected() && !columnsNames.contains(driver.getNamesSpanMap().getFirstSpan().getData())) { + // As driver with same name should have same NamesSpanMap we only check if the first span is present, + // if not we add all span names to columnsNames + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + columnsNames.add(span.getData()); + } } } } @@ -421,30 +426,32 @@ private void setUpRegularParametersJacobiansColumns(final DSSTStateTransitionMat // add the Jacobians column generators corresponding to parameters, and setup state accordingly for (final DelegatingDriver driver : selected.getDrivers()) { - DSSTIntegrableJacobianColumnGenerator generator = null; + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + DSSTIntegrableJacobianColumnGenerator generator = null; - // check if we already have set up the providers - for (final AdditionalDerivativesProvider provider : getAdditionalDerivativesProviders()) { - if (provider instanceof DSSTIntegrableJacobianColumnGenerator && - provider.getName().equals(driver.getName())) { - // the Jacobian column generator has already been set up in a previous propagation - generator = (DSSTIntegrableJacobianColumnGenerator) provider; - break; + // check if we already have set up the providers + for (final AdditionalDerivativesProvider provider : getAdditionalDerivativesProviders()) { + if (provider instanceof DSSTIntegrableJacobianColumnGenerator && + provider.getName().equals(span.getData())) { + // the Jacobian column generator has already been set up in a previous propagation + generator = (DSSTIntegrableJacobianColumnGenerator) provider; + break; + } } - } - if (generator == null) { - // this is the first time we need the Jacobian column generator, create it - generator = new DSSTIntegrableJacobianColumnGenerator(stmGenerator, driver.getName()); - addAdditionalDerivativesProvider(generator); - } + if (generator == null) { + // this is the first time we need the Jacobian column generator, create it + generator = new DSSTIntegrableJacobianColumnGenerator(stmGenerator, span.getData()); + addAdditionalDerivativesProvider(generator); + } - if (!getInitialIntegrationState().hasAdditionalState(driver.getName())) { - // add the initial Jacobian column if it is not already there - // (perhaps due to a previous propagation) - setInitialState(getInitialState().addAdditionalState(driver.getName(), - getHarvester().getInitialJacobianColumn(driver.getName())), - getPropagationType()); + if (!getInitialIntegrationState().hasAdditionalState(span.getData())) { + // add the initial Jacobian column if it is not already there + // (perhaps due to a previous propagation) + setInitialState(getInitialState().addAdditionalState(span.getData(), + getHarvester().getInitialJacobianColumn(span.getData())), + getPropagationType()); + } } } @@ -513,7 +520,14 @@ public void addForceModel(final DSSTForceModel force) { force.getParametersDrivers().get(0).addObserver(new ParameterObserver() { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { + // mu PDriver should have only 1 span + superSetMu(driver.getValue()); + } + /** {@inheritDoc} */ + @Override + public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { + // mu PDriver should have only 1 span superSetMu(driver.getValue()); } }); @@ -581,7 +595,7 @@ public OrbitType getOrbitType() { /** Get propagation parameter type. * @return angle type to use for propagation */ - public PositionAngle getPositionAngleType() { + public PositionAngleType getPositionAngleType() { return super.getPositionAngleType(); } @@ -612,8 +626,8 @@ public static SpacecraftState computeOsculatingState(final SpacecraftState mean, final List shortPeriodTerms = new ArrayList(); for (final DSSTForceModel force : forces) { force.registerAttitudeProvider(attitudeProvider); - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters())); - force.updateShortPeriodTerms(force.getParameters(), mean); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(mean.getDate()))); + force.updateShortPeriodTerms(force.getParametersAllValues(), mean); } final EquinoctialOrbit osculatingOrbit = computeOsculatingOrbit(mean, shortPeriodTerms); @@ -747,7 +761,7 @@ protected void beforeIntegration(final SpacecraftState initialState, // initialize all perturbing forces final List shortPeriodTerms = new ArrayList(); for (final DSSTForceModel force : forceModels) { - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters())); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(initialState.getDate()))); } mapper.setShortPeriodTerms(shortPeriodTerms); @@ -756,7 +770,7 @@ protected void beforeIntegration(final SpacecraftState initialState, final ShortPeriodicsHandler spHandler = new ShortPeriodicsHandler(forceModels); // Compute short periodic coefficients for this point for (DSSTForceModel forceModel : forceModels) { - forceModel.updateShortPeriodTerms(forceModel.getParameters(), initialState); + forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(), initialState); } final Collection stepHandlers = new ArrayList(); stepHandlers.add(spHandler); @@ -839,8 +853,8 @@ private static Orbit computeMeanOrbit(final SpacecraftState osculating, // Set the force models final List shortPeriodTerms = new ArrayList(); for (final DSSTForceModel force : forceModels) { - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters())); - force.updateShortPeriodTerms(force.getParameters(), meanState); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(meanState.getDate()))); + force.updateShortPeriodTerms(force.getParametersAllValues(), meanState); } // recompute the osculating parameters from the current mean parameters @@ -871,7 +885,7 @@ private static Orbit computeMeanOrbit(final SpacecraftState osculating, meanOrbit.getHx() + deltaHx, meanOrbit.getHy() + deltaHy, meanOrbit.getLv() + deltaLv, - PositionAngle.TRUE, meanOrbit.getFrame(), + PositionAngleType.TRUE, meanOrbit.getFrame(), meanOrbit.getDate(), meanOrbit.getMu()); } @@ -892,7 +906,7 @@ private static EquinoctialOrbit computeOsculatingOrbit(final SpacecraftState mea final double[] mean = new double[6]; final double[] meanDot = new double[6]; - OrbitType.EQUINOCTIAL.mapOrbitToArray(meanState.getOrbit(), PositionAngle.MEAN, mean, meanDot); + OrbitType.EQUINOCTIAL.mapOrbitToArray(meanState.getOrbit(), PositionAngleType.MEAN, mean, meanDot); final double[] y = mean.clone(); for (final ShortPeriodTerms spt : shortPeriodTerms) { final double[] shortPeriodic = spt.value(meanState.getOrbit()); @@ -901,7 +915,7 @@ private static EquinoctialOrbit computeOsculatingOrbit(final SpacecraftState mea } } return (EquinoctialOrbit) OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, meanDot, - PositionAngle.MEAN, meanState.getDate(), + PositionAngleType.MEAN, meanState.getDate(), meanState.getMu(), meanState.getFrame()); } @@ -921,13 +935,13 @@ protected SpacecraftState getInitialIntegrationState() { /** {@inheritDoc} *

        * Note that for DSST, orbit type is hardcoded to {@link OrbitType#EQUINOCTIAL} - * and position angle type is hardcoded to {@link PositionAngle#MEAN}, so + * and position angle type is hardcoded to {@link PositionAngleType#MEAN}, so * the corresponding parameters are ignored. *

        */ @Override protected StateMapper createMapper(final AbsoluteDate referenceDate, final double mu, - final OrbitType ignoredOrbitType, final PositionAngle ignoredPositionAngleType, + final OrbitType ignoredOrbitType, final PositionAngleType ignoredPositionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { // create a mapper with the common settings provided as arguments @@ -986,7 +1000,7 @@ private static class MeanPlusShortPeriodicMapper extends StateMapper { MeanPlusShortPeriodicMapper(final AbsoluteDate referenceDate, final double mu, final AttitudeProvider attitudeProvider, final Frame frame) { - super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngle.MEAN, attitudeProvider, frame); + super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, attitudeProvider, frame); this.selectedCoefficients = null; @@ -1011,7 +1025,7 @@ public SpacecraftState mapArrayToState(final AbsoluteDate date, if (type == PropagationType.MEAN) { coefficients = null; } else { - final Orbit meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngle.MEAN, date, getMu(), getFrame()); + final Orbit meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame()); coefficients = selectedCoefficients == null ? null : new DoubleArrayDictionary(); for (final ShortPeriodTerms spt : shortPeriodTerms) { final double[] shortPeriodic = spt.value(meanOrbit); @@ -1026,10 +1040,10 @@ public SpacecraftState mapArrayToState(final AbsoluteDate date, final double mass = elements[6]; if (mass <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, mass); + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass); } - final Orbit orbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngle.MEAN, date, getMu(), getFrame()); + final Orbit orbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame()); final Attitude attitude = getAttitudeProvider().getAttitude(orbit, date, getFrame()); if (coefficients == null) { @@ -1044,7 +1058,7 @@ public SpacecraftState mapArrayToState(final AbsoluteDate date, @Override public void mapStateToArray(final SpacecraftState state, final double[] y, final double[] yDot) { - OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngle.MEAN, y, yDot); + OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.MEAN, y, yDot); y[6] = state.getMass(); } @@ -1121,15 +1135,9 @@ private class Main implements MainStateEquations { Main(final ODEIntegrator integrator) { yDot = new double[7]; - for (final DSSTForceModel forceModel : forceModels) { - final EventDetector[] modelDetectors = forceModel.getEventsDetectors(); - if (modelDetectors != null) { - for (final EventDetector detector : modelDetectors) { - setUpEventDetector(integrator, detector); - } - } - } - + // Setup event detectors for each force model + forceModels.forEach(dsstForceModel -> dsstForceModel.getEventDetectors(). + forEach(eventDetector -> setUpEventDetector(integrator, eventDetector))); } /** {@inheritDoc} */ @@ -1149,7 +1157,7 @@ public double[] computeDerivatives(final SpacecraftState state) { // compute the contributions of all perturbing forces for (final DSSTForceModel forceModel : forceModels) { - final double[] daidt = elementRates(forceModel, state, auxiliaryElements, forceModel.getParameters()); + final double[] daidt = elementRates(forceModel, state, auxiliaryElements, forceModel.getParameters(state.getDate())); for (int i = 0; i < daidt.length; i++) { yDot[i] += daidt[i]; } @@ -1163,7 +1171,8 @@ public double[] computeDerivatives(final SpacecraftState state) { * @param forceModel force to take into account * @param state current state * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters force model parameters + * @param parameters force model parameters at state date (only 1 value for + * each parameter * @return the mean equinoctial elements rates dai / dt */ private double[] elementRates(final DSSTForceModel forceModel, @@ -1269,9 +1278,8 @@ public void handleStep(final ODEStateInterpolator interpolator) { // Computate short periodic coefficients for this step for (DSSTForceModel forceModel : forceModels) { - forceModel.updateShortPeriodTerms(forceModel.getParameters(), meanStates); + forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(), meanStates); } - } } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGenerator.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGenerator.java index f10790fc04..9741cbbce8 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGenerator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,6 +34,7 @@ import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; /** Generator for State Transition Matrix. * @author Luc Maisonobe @@ -114,7 +115,7 @@ public int getDimension() { /** {@inheritDoc} */ @Override - public boolean yield(final SpacecraftState state) { + public boolean yields(final SpacecraftState state) { return !state.hasAdditionalState(getName()); } @@ -152,13 +153,6 @@ SpacecraftState setInitialStateTransitionMatrix(final SpacecraftState state, fin } - /** {@inheritDoc} */ - @Override - @Deprecated - public double[] derivatives(final SpacecraftState state) { - return combinedDerivatives(state).getAdditionalDerivatives(); - } - /** {@inheritDoc} */ public CombinedDerivatives combinedDerivatives(final SpacecraftState state) { @@ -197,7 +191,7 @@ private RealMatrix computePartials(final SpacecraftState state) { for (final DSSTForceModel forceModel : forceModels) { final FieldSpacecraftState dsState = converter.getState(forceModel); - final Gradient[] parameters = converter.getParameters(dsState, forceModel); + final Gradient[] parameters = converter.getParametersAtStateDate(dsState, forceModel); final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(dsState.getOrbit(), I); final Gradient[] meanElementRate = forceModel.getMeanElementRate(dsState, auxiliaryElements, parameters); @@ -220,22 +214,24 @@ private RealMatrix computePartials(final SpacecraftState state) { int paramsIndex = converter.getFreeStateParameters(); for (ParameterDriver driver : forceModel.getParametersDrivers()) { if (driver.isSelected()) { - - // get the partials derivatives for this driver - DoubleArrayDictionary.Entry entry = meanElementsPartials.getEntry(driver.getName()); - if (entry == null) { - // create an entry filled with zeroes - meanElementsPartials.put(driver.getName(), new double[STATE_DIMENSION]); - entry = meanElementsPartials.getEntry(driver.getName()); + // for each span (for each estimated value) corresponding name is added + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + // get the partials derivatives for this driver + DoubleArrayDictionary.Entry entry = meanElementsPartials.getEntry(span.getData()); + if (entry == null) { + // create an entry filled with zeroes + meanElementsPartials.put(span.getData(), new double[STATE_DIMENSION]); + entry = meanElementsPartials.getEntry(span.getData()); + } + // add the contribution of the current force model + entry.increment(new double[] { + derivativesA[paramsIndex], derivativesEx[paramsIndex], derivativesEy[paramsIndex], + derivativesHx[paramsIndex], derivativesHy[paramsIndex], derivativesL[paramsIndex] + }); + ++paramsIndex; } - // add the contribution of the current force model - entry.increment(new double[] { - derivativesA[paramsIndex], derivativesEx[paramsIndex], derivativesEy[paramsIndex], - derivativesHx[paramsIndex], derivativesHy[paramsIndex], derivativesL[paramsIndex] - }); - ++paramsIndex; - } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java index 50cde05f32..67c5d0d68c 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,12 +44,11 @@ import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.integration.FieldAbstractIntegratedPropagator; import org.orekit.propagation.integration.FieldStateMapper; import org.orekit.propagation.numerical.FieldNumericalPropagator; @@ -60,10 +59,12 @@ import org.orekit.propagation.semianalytical.dsst.utilities.FieldFixedNumberInterpolationGrid; import org.orekit.propagation.semianalytical.dsst.utilities.FieldInterpolationGrid; import org.orekit.propagation.semianalytical.dsst.utilities.FieldMaxGapInterpolationGrid; +import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldArrayDictionary; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; +import org.orekit.utils.TimeSpanMap; /** * This class propagates {@link org.orekit.orbits.FieldOrbit orbits} using the DSST theory. @@ -88,7 +89,7 @@ *

        * From these configuration parameters, only the initial state is mandatory. * The default propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial} - * parameters with {@link PositionAngle#TRUE true} longitude argument. + * parameters with {@link PositionAngleType#TRUE true} longitude argument. * The central attraction coefficient used to define the initial orbit will be used. * However, specifying only the initial state would mean the propagator would use * only Keplerian forces. In this case, the simpler @@ -122,6 +123,7 @@ * @author Romain Di Costanzo * @author Pascal Parraud * @since 10.0 + * @param type of the field elements */ public class FieldDSSTPropagator> extends FieldAbstractIntegratedPropagator { @@ -209,7 +211,7 @@ public FieldDSSTPropagator(final Field field, initMapper(field); // DSST uses only equinoctial orbits and mean longitude argument setOrbitType(OrbitType.EQUINOCTIAL); - setPositionAngleType(PositionAngle.MEAN); + setPositionAngleType(PositionAngleType.MEAN); setAttitudeProvider(attitudeProvider); setInterpolationGridToFixedNumberOfPoints(INTERPOLATION_POINTS_PER_STEP); } @@ -257,7 +259,7 @@ public FieldDSSTPropagator(final Field field, initMapper(field); // DSST uses only equinoctial orbits and mean longitude argument setOrbitType(OrbitType.EQUINOCTIAL); - setPositionAngleType(PositionAngle.MEAN); + setPositionAngleType(PositionAngleType.MEAN); setAttitudeProvider(attitudeProvider); setInterpolationGridToFixedNumberOfPoints(INTERPOLATION_POINTS_PER_STEP); } @@ -415,7 +417,14 @@ public void addForceModel(final DSSTForceModel force) { force.getParametersDrivers().get(0).addObserver(new ParameterObserver() { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { + // mu PDriver should have only 1 span + superSetMu(field.getZero().add(driver.getValue())); + } + /** {@inheritDoc} */ + @Override + public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { + // mu PDriver should have only 1 span superSetMu(field.getZero().add(driver.getValue())); } }); @@ -487,7 +496,7 @@ public OrbitType getOrbitType() { /** Get propagation parameter type. * @return angle type to use for propagation */ - public PositionAngle getPositionAngleType() { + public PositionAngleType getPositionAngleType() { return super.getPositionAngleType(); } @@ -519,10 +528,9 @@ public static > FieldSpacecraftState comput // Set the force models final List> shortPeriodTerms = new ArrayList>(); for (final DSSTForceModel force : forces) { - final T[] parameters = force.getParameters(mean.getDate().getField()); force.registerAttitudeProvider(attitudeProvider); - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, parameters)); - force.updateShortPeriodTerms(parameters, mean); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(mean.getDate().getField(), mean.getDate()))); + force.updateShortPeriodTerms(force.getParametersAllValues(mean.getDate().getField()), mean); } final FieldEquinoctialOrbit osculatingOrbit = computeOsculatingOrbit(mean, shortPeriodTerms); @@ -641,7 +649,7 @@ protected void beforeIntegration(final FieldSpacecraftState initialState, // initialize all perturbing forces final List> shortPeriodTerms = new ArrayList>(); for (final DSSTForceModel force : forceModels) { - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(field))); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(field, initialState.getDate()))); } mapper.setShortPeriodTerms(shortPeriodTerms); @@ -650,7 +658,7 @@ protected void beforeIntegration(final FieldSpacecraftState initialState, final FieldShortPeriodicsHandler spHandler = new FieldShortPeriodicsHandler(forceModels); // Compute short periodic coefficients for this point for (DSSTForceModel forceModel : forceModels) { - forceModel.updateShortPeriodTerms(forceModel.getParameters(field), initialState); + forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(field), initialState); } final Collection> stepHandlers = new ArrayList>(); @@ -735,9 +743,9 @@ private static > FieldOrbit computeMeanOrbi // Set the force models final List> shortPeriodTerms = new ArrayList>(); for (final DSSTForceModel force : forceModel) { - final T[] parameters = force.getParameters(osculating.getDate().getField()); - shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, parameters)); - force.updateShortPeriodTerms(parameters, meanState); + shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, + force.getParameters(osculating.getDate().getField(), osculating.getDate()))); + force.updateShortPeriodTerms(force.getParametersAllValues(osculating.getDate().getField()), meanState); } // recompute the osculating parameters from the current mean parameters @@ -768,7 +776,7 @@ private static > FieldOrbit computeMeanOrbi meanOrbit.getHx().add(deltaHx), meanOrbit.getHy().add(deltaHy), meanOrbit.getLv().add(deltaLv), - PositionAngle.TRUE, meanOrbit.getFrame(), + PositionAngleType.TRUE, meanOrbit.getFrame(), meanOrbit.getDate(), meanOrbit.getMu()); } @@ -789,7 +797,7 @@ private static > FieldEquinoctialOrbit comp final T[] mean = MathArrays.buildArray(meanState.getDate().getField(), 6); final T[] meanDot = MathArrays.buildArray(meanState.getDate().getField(), 6); - OrbitType.EQUINOCTIAL.mapOrbitToArray(meanState.getOrbit(), PositionAngle.MEAN, mean, meanDot); + OrbitType.EQUINOCTIAL.mapOrbitToArray(meanState.getOrbit(), PositionAngleType.MEAN, mean, meanDot); final T[] y = mean.clone(); for (final FieldShortPeriodTerms spt : shortPeriodTerms) { final T[] shortPeriodic = spt.value(meanState.getOrbit()); @@ -798,7 +806,7 @@ private static > FieldEquinoctialOrbit comp } } return (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, meanDot, - PositionAngle.MEAN, meanState.getDate(), + PositionAngleType.MEAN, meanState.getDate(), meanState.getMu(), meanState.getFrame()); } @@ -818,13 +826,13 @@ protected FieldSpacecraftState getInitialIntegrationState() { /** {@inheritDoc} *

        * Note that for DSST, orbit type is hardcoded to {@link OrbitType#EQUINOCTIAL} - * and position angle type is hardcoded to {@link PositionAngle#MEAN}, so + * and position angle type is hardcoded to {@link PositionAngleType#MEAN}, so * the corresponding parameters are ignored. *

        */ @Override protected FieldStateMapper createMapper(final FieldAbsoluteDate referenceDate, final T mu, - final OrbitType ignoredOrbitType, final PositionAngle ignoredPositionAngleType, + final OrbitType ignoredOrbitType, final PositionAngleType ignoredPositionAngleType, final AttitudeProvider attitudeProvider, final Frame frame) { // create a mapper with the common settings provided as arguments @@ -864,7 +872,7 @@ private class FieldMeanPlusShortPeriodicMapper extends FieldStateMapper { FieldMeanPlusShortPeriodicMapper(final FieldAbsoluteDate referenceDate, final T mu, final AttitudeProvider attitudeProvider, final Frame frame) { - super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngle.MEAN, attitudeProvider, frame); + super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, attitudeProvider, frame); this.selectedCoefficients = null; @@ -891,7 +899,7 @@ public FieldSpacecraftState mapArrayToState(final FieldAbsoluteDate date, coefficients = null; break; case OSCULATING: - final FieldOrbit meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngle.MEAN, date, getMu(), getFrame()); + final FieldOrbit meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame()); coefficients = selectedCoefficients == null ? null : new FieldArrayDictionary<>(date.getField()); for (final FieldShortPeriodTerms spt : shortPeriodTerms) { final T[] shortPeriodic = spt.value(meanOrbit); @@ -909,10 +917,10 @@ public FieldSpacecraftState mapArrayToState(final FieldAbsoluteDate date, final T mass = elements[6]; if (mass.getReal() <= 0.0) { - throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, mass); + throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass); } - final FieldOrbit orbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngle.MEAN, date, getMu(), getFrame()); + final FieldOrbit orbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame()); final FieldAttitude attitude = getAttitudeProvider().getAttitude(orbit, date, getFrame()); if (coefficients == null) { @@ -927,7 +935,7 @@ public FieldSpacecraftState mapArrayToState(final FieldAbsoluteDate date, @Override public void mapStateToArray(final FieldSpacecraftState state, final T[] y, final T[] yDot) { - OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngle.MEAN, y, yDot); + OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.MEAN, y, yDot); y[6] = state.getMass(); } @@ -1004,15 +1012,9 @@ private class Main implements MainStateEquations { Main(final FieldODEIntegrator integrator) { yDot = MathArrays.buildArray(field, 7); - for (final DSSTForceModel forceModel : forceModels) { - final FieldEventDetector[] modelDetectors = forceModel.getFieldEventsDetectors(field); - if (modelDetectors != null) { - for (final FieldEventDetector detector : modelDetectors) { - setUpEventDetector(integrator, detector); - } - } - } - + // Setup event detectors for each force model + forceModels.forEach(dsstForceModel -> dsstForceModel.getFieldEventDetectors(field). + forEach(eventDetector -> setUpEventDetector(integrator, eventDetector))); } /** {@inheritDoc} */ @@ -1033,7 +1035,7 @@ public T[] computeDerivatives(final FieldSpacecraftState state) { // compute the contributions of all perturbing forces for (final DSSTForceModel forceModel : forceModels) { - final T[] daidt = elementRates(forceModel, state, auxiliaryElements, forceModel.getParameters(field)); + final T[] daidt = elementRates(forceModel, state, auxiliaryElements, forceModel.getParametersAllValues(field)); for (int i = 0; i < daidt.length; i++) { yDot[i] = yDot[i].add(daidt[i]); } @@ -1047,7 +1049,9 @@ public T[] computeDerivatives(final FieldSpacecraftState state) { * @param forceModel force to take into account * @param state current state * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters force model parameters + * @param parameters force model parameters (all span values for each parameters) + * the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} is called in + * the method to select the right parameter. * @return the mean equinoctial elements rates dai / dt */ private T[] elementRates(final DSSTForceModel forceModel, @@ -1153,7 +1157,7 @@ public void handleStep(final FieldODEStateInterpolator interpolator) { // Compute short periodic coefficients for this step for (DSSTForceModel forceModel : forceModels) { - forceModel.updateShortPeriodTerms(forceModel.getParameters(field), meanStates); + forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(field), meanStates); } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java index 587e3642c6..1f5aaff5d9 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -43,7 +43,7 @@ import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; @@ -263,6 +263,7 @@ public > List> initia *

        * @param auxiliaryElements auxiliary elements related to the current orbit * @param parameters parameters values of the force model parameters + * only 1 value for each parameterDriver * @return new force model context */ private AbstractGaussianContributionContext initializeStep(final AuxiliaryElements auxiliaryElements, @@ -278,6 +279,8 @@ private AbstractGaussianContributionContext initializeStep(final AuxiliaryElemen * @param type of the elements * @param auxiliaryElements auxiliary elements related to the current orbit * @param parameters parameters values of the force model parameters + * (only 1 values for each parameters corresponding + * to state date) by getting the parameters for a specific date. * @return new force model context */ private > FieldAbstractGaussianContributionContext initializeStep( @@ -291,6 +294,7 @@ public double[] getMeanElementRate(final SpacecraftState state, final AuxiliaryE final double[] parameters) { // Container for attributes + final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, parameters); double[] meanElementRate = new double[6]; // Computes the limits for the integral @@ -377,6 +381,7 @@ protected abstract > T[] getLLimits(FieldSpace * @param high upper bound of the integral interval * @param context container for attributes * @param parameters values of the force model parameters + * at state date (1 values for each parameters) * @return the mean element rates */ protected double[] getMeanElementRate(final SpacecraftState state, final GaussQuadrature gauss, final double low, @@ -405,7 +410,7 @@ protected double[] getMeanElementRate(final SpacecraftState state, final GaussQu * @param low lower bound of the integral interval * @param high upper bound of the integral interval * @param context container for attributes - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters(1 values for each parameters) * @return the mean element rates */ protected > T[] getMeanElementRate(final FieldSpacecraftState state, @@ -494,14 +499,16 @@ public void updateShortPeriodTerms(final double[] parameters, final SpacecraftSt final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I); // Container of attributes - final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, parameters); + // Extract the proper parameters valid for the corresponding meanState date from the input array + final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, extractedParameters); // Compute rhoj and sigmaj final double[][] currentRhoSigmaj = computeRhoSigmaCoefficients(meanState.getDate(), auxiliaryElements); // Generate the Cij and Sij coefficients final FourierCjSjCoefficients fourierCjSj = new FourierCjSjCoefficients(meanState, JMAX, auxiliaryElements, - parameters); + extractedParameters); // Generate the Uij and Vij coefficients final UijVijCoefficients uijvij = new UijVijCoefficients(currentRhoSigmaj, fourierCjSj, JMAX); @@ -531,14 +538,16 @@ public > void updateShortPeriodTerms(final T[] final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I); // Container of attributes - final FieldAbstractGaussianContributionContext context = initializeStep(auxiliaryElements, parameters); + // Extract the proper parameters valid for the corresponding meanState date from the input array + final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + final FieldAbstractGaussianContributionContext context = initializeStep(auxiliaryElements, extractedParameters); // Compute rhoj and sigmaj final T[][] currentRhoSigmaj = computeRhoSigmaCoefficients(meanState.getDate(), context, field); // Generate the Cij and Sij coefficients final FieldFourierCjSjCoefficients fourierCjSj = new FieldFourierCjSjCoefficients<>(meanState, JMAX, - auxiliaryElements, parameters, field); + auxiliaryElements, extractedParameters, field); // Generate the Uij and Vij coefficients final FieldUijVijCoefficients uijvij = new FieldUijVijCoefficients<>(currentRhoSigmaj, fourierCjSj, JMAX, @@ -624,6 +633,7 @@ private > T[][] computeRhoSigmaCoefficients(fi *

        * This class is a rewrite of {@link IntegrableFunction} for field elements *

        + * @param type of the field elements */ protected class FieldIntegrableFunction> implements CalculusFieldUnivariateVectorFunction { @@ -662,7 +672,10 @@ protected class FieldIntegrableFunction> * short periodic elements variation * @param j the j index. used only for short periodic variation. * Ignored for mean elements variation. - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (only 1 values + * for each parameters corresponding to state date) obtained by + * calling the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} + * to selected the right value for state date or by getting the parameters for a specific date * @param field field utilized by default */ public FieldIntegrableFunction(final FieldSpacecraftState state, final boolean meanMode, final int j, @@ -675,9 +688,9 @@ public FieldIntegrableFunction(final FieldSpacecraftState state, final boolea this.context = new FieldAbstractGaussianContributionContext<>(auxiliaryElements, this.parameters); // remove derivatives from state final T[] stateVector = MathArrays.buildArray(field, 6); - OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngle.TRUE, stateVector, null); + OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.TRUE, stateVector, null); final FieldOrbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, - PositionAngle.TRUE, state.getDate(), context.getMu(), state.getFrame()); + PositionAngleType.TRUE, state.getDate(), context.getMu(), state.getFrame()); this.state = new FieldSpacecraftState<>(fixedOrbit, state.getAttitude(), state.getMass()); } @@ -718,7 +731,7 @@ public T[] value(final T x) { // Recompose an orbit with time held fixed to be compliant with DSST theory final FieldOrbit recomposedOrbit = new FieldEquinoctialOrbit<>(shiftedOrbit.getA(), shiftedOrbit.getEquinoctialEx(), shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(), - shiftedOrbit.getHy(), shiftedOrbit.getLv(), PositionAngle.TRUE, shiftedOrbit.getFrame(), + shiftedOrbit.getHy(), shiftedOrbit.getLv(), PositionAngleType.TRUE, shiftedOrbit.getFrame(), state.getDate(), context.getMu()); // Get the corresponding attitude @@ -940,7 +953,8 @@ protected class IntegrableFunction implements UnivariateVectorFunction { * short periodic elements variation * @param j the j index. used only for short periodic variation. * Ignored for mean elements variation. - * @param parameters values of the force model parameters + * @param parameters list of the estimated values for each driver at state date of the force model parameters + * only 1 value for each parameter */ IntegrableFunction(final SpacecraftState state, final boolean meanMode, final int j, final double[] parameters) { @@ -952,8 +966,8 @@ protected class IntegrableFunction implements UnivariateVectorFunction { this.context = new AbstractGaussianContributionContext(auxiliaryElements, this.parameters); // remove derivatives from state final double[] stateVector = new double[6]; - OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngle.TRUE, stateVector, null); - final Orbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, PositionAngle.TRUE, + OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.TRUE, stateVector, null); + final Orbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, PositionAngleType.TRUE, state.getDate(), context.getMu(), state.getFrame()); this.state = new SpacecraftState(fixedOrbit, state.getAttitude(), state.getMass()); } @@ -990,7 +1004,7 @@ public double[] value(final double x) { // Recompose an orbit with time held fixed to be compliant with DSST theory final Orbit recomposedOrbit = new EquinoctialOrbit(shiftedOrbit.getA(), shiftedOrbit.getEquinoctialEx(), shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(), shiftedOrbit.getHy(), shiftedOrbit.getLv(), - PositionAngle.TRUE, shiftedOrbit.getFrame(), state.getDate(), context.getMu()); + PositionAngleType.TRUE, shiftedOrbit.getFrame(), state.getDate(), context.getMu()); // Get the corresponding attitude final Attitude recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit, recomposedOrbit.getDate(), @@ -1000,6 +1014,7 @@ public double[] value(final double x) { final SpacecraftState shiftedState = new SpacecraftState(recomposedOrbit, recomposedAttitude, state.getMass()); + // here parameters is a list of all span values of each parameter driver acc = contribution.acceleration(shiftedState, parameters); // Compute the derivatives of the elements by the speed @@ -1581,7 +1596,8 @@ protected class FourierCjSjCoefficients { * @param state the current state * @param jMax maximum value for j * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters list of parameter values at state date for each driver + * of the force model parameters (1 value per parameter) */ FourierCjSjCoefficients(final SpacecraftState state, final int jMax, final AuxiliaryElements auxiliaryElements, final double[] parameters) { @@ -1606,7 +1622,8 @@ protected class FourierCjSjCoefficients { *

        * @param state the current state * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters list of parameter values at state date for each driver + * of the force model parameters (1 value per parameter) */ private void computeCoefficients(final SpacecraftState state, final AuxiliaryElements auxiliaryElements, final double[] parameters) { @@ -1661,6 +1678,7 @@ public double getSij(final int i, final int j) { *

        * @author Petre Bazavan * @author Lucian Barbulescu + * @param type of the field elements */ protected class FieldFourierCjSjCoefficients> { @@ -2056,7 +2074,7 @@ private void storeIfSelected(final Map map, final Set *

        * @author Petre Bazavan * @author Lucian Barbulescu - * + * @param type of the field elements */ protected static class FieldGaussianShortPeriodicCoefficients> implements FieldShortPeriodTerms { @@ -2632,6 +2650,7 @@ public double getV2(final int j) { * * @author Petre Bazavan * @author Lucian Barbulescu + * @param type of the field elements */ protected static class FieldUijVijCoefficients> { @@ -3020,7 +3039,9 @@ protected static class Slot { } - /** Coefficients valid for one time slot. */ + /** Coefficients valid for one time slot. + * @param type of the field elements + */ protected static class FieldSlot> { /** diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionContext.java index e6e03c4b52..154aa8fb88 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -65,6 +65,7 @@ public class AbstractGaussianContributionContext extends ForceModelContext { * * @param auxiliaryElements auxiliary elements related to the current orbit * @param parameters parameters values of the force model parameters + * only 1 value for each parameterDriver */ AbstractGaussianContributionContext(final AuxiliaryElements auxiliaryElements, final double[] parameters) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java index 3d1fea48a0..ad79b32e4f 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,9 +17,10 @@ package org.orekit.propagation.semianalytical.dsst.forces; import java.util.List; +import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.hipparchus.util.MathUtils; @@ -111,14 +112,14 @@ public double getRbar() { } /** {@inheritDoc} */ - public EventDetector[] getEventsDetectors() { - return null; + public Stream getEventDetectors() { + return drag.getEventDetectors(); } /** {@inheritDoc} */ @Override - public > FieldEventDetector[] getFieldEventsDetectors(final Field field) { - return null; + public > Stream> getFieldEventDetectors(final Field field) { + return drag.getFieldEventDetectors(field); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java index bbeb0d3616..cf203181d5 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,22 +17,26 @@ package org.orekit.propagation.semianalytical.dsst.forces; import java.util.List; +import java.util.stream.Stream; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.MathArrays; import org.orekit.attitudes.AttitudeProvider; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.EventDetectorsProvider; import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.integration.AbstractGradientConverter; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.ParametersDriversProvider; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TimeSpanMap.Span; /** This interface represents a force modifying spacecraft motion for a {@link * org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}. @@ -60,7 +64,7 @@ * @author Romain Di Constanzo * @author Pascal Parraud */ -public interface DSSTForceModel extends ParametersDriversProvider { +public interface DSSTForceModel extends ParameterDriversProvider, EventDetectorsProvider { /** * Initialize the force model at the start of propagation. @@ -86,13 +90,27 @@ default > void init(FieldSpacecraftState in init(initialState.toSpacecraftState(), target.toAbsoluteDate()); } + /** {@inheritDoc}.*/ + @Override + default Stream getEventDetectors() { + return getEventDetectors(getParametersDrivers()); + } + + /** {@inheritDoc}.*/ + @Override + default > Stream> getFieldEventDetectors(Field field) { + return getFieldEventDetectors(field, getParametersDrivers()); + } + /** Performs initialization prior to propagation for the current force model. *

        * This method aims at being called at the very beginning of a propagation. *

        * @param auxiliaryElements auxiliary elements related to the current orbit * @param type type of the elements used during the propagation - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters for specific date + * (1 value only per parameter driver) obtained for example by calling + * {@link #getParameters(AbsoluteDate)} on force model. * @return a list of objects that will hold short period terms (the objects * are also retained by the force model, which will update them during propagation) */ @@ -106,46 +124,87 @@ List initializeShortPeriodTerms(AuxiliaryElements auxiliaryEle * @param type of the elements * @param auxiliaryElements auxiliary elements related to the current orbit * @param type type of the elements used during the propagation - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters for specific date + * (1 value only per parameter driver) obtained for example by calling + * {@link #getParameters(AbsoluteDate)} on force model or + * {@link AbstractGradientConverter#getParametersAtStateDate(FieldSpacecraftState, ParameterDriversProvider)} + * on gradient converter. * @return a list of objects that will hold short period terms (the objects * are also retained by the force model, which will update them during propagation) */ > List> initializeShortPeriodTerms(FieldAuxiliaryElements auxiliaryElements, - PropagationType type, T[] parameters); + PropagationType type, T[] parameters); - /** Get force model parameters. - * @return force model parameters - * @since 9.0 + /** Extract the proper parameter drivers' values from the array in input of the + * {@link #updateShortPeriodTerms(double[], SpacecraftState...) updateShortPeriodTerms} method. + * Parameters are filtered given an input date. + * @param parameters the input parameters array containing all span values of all drivers + * from which the parameter values at date date wants to be extracted + * @param date the date + * @return the parameters given the date */ - default double[] getParameters() { - final List drivers = getParametersDrivers(); - final double[] parameters = new double[drivers.size()]; - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = drivers.get(i).getValue(); + default double[] extractParameters(final double[] parameters, final AbsoluteDate date) { + + // Find out the indexes of the parameters in the whole array of parameters + final List allParameters = getParametersDrivers(); + final double[] outParameters = new double[allParameters.size()]; + int index = 0; + int paramIndex = 0; + for (int i = 0; i < allParameters.size(); i++) { + final ParameterDriver driver = allParameters.get(i); + final String driverNameforDate = driver.getNameSpan(date); + // Loop on the spans + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // Add all the parameter drivers of the span + if (span.getData().equals(driverNameforDate)) { + outParameters[index++] = parameters[paramIndex]; + } + paramIndex++; + } } - return parameters; + return outParameters; } - /** Get force model parameters. - * @param field field to which the elements belong - * @param type of the elements - * @return force model parameters - * @since 9.0 + /** Extract the proper parameter drivers' values from the array in input of the + * {@link #updateShortPeriodTerms(CalculusFieldElement[], FieldSpacecraftState...) + * updateShortPeriodTerms} method. Parameters are filtered given an input date. + * @param parameters the input parameters array containing all span values of all drivers + * from which the parameter values at date date wants to be extracted + * @param date the date + * @param extends CalculusFieldElement + * @return the parameters given the date */ - default > T[] getParameters(final Field field) { - final List drivers = getParametersDrivers(); - final T[] parameters = MathArrays.buildArray(field, drivers.size()); - for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue()); + default > T[] extractParameters(final T[] parameters, + final FieldAbsoluteDate date) { + + // Find out the indexes of the parameters in the whole array of parameters + final List allParameters = getParametersDrivers(); + final T[] outParameters = MathArrays.buildArray(date.getField(), allParameters.size()); + int index = 0; + int paramIndex = 0; + for (int i = 0; i < allParameters.size(); i++) { + final ParameterDriver driver = allParameters.get(i); + final String driverNameforDate = driver.getNameSpan(date.toAbsoluteDate()); + // Loop on the spans + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + // Add all the parameter drivers of the span + if (span.getData().equals(driverNameforDate)) { + outParameters[index++] = parameters[paramIndex]; + } + ++paramIndex; + } } - return parameters; + return outParameters; } + /** Computes the mean equinoctial elements rates dai / dt. * * @param state current state information: date, kinematics, attitude * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters at state date (only 1 span for + * each parameter driver) obtained for example by calling {@link #getParameters(AbsoluteDate)} + * on force model. * @return the mean element rates dai/dt */ double[] getMeanElementRate(SpacecraftState state, @@ -156,26 +215,15 @@ default > T[] getParameters(final Field fie * @param type of the elements * @param state current state information: date, kinematics, attitude * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters at state date (only 1 span for + * each parameter driver) obtained for example by calling {@link #getParameters(Field, FieldAbsoluteDate)} + * on force model or + * {@link AbstractGradientConverter#getParametersAtStateDate(FieldSpacecraftState, ParameterDriversProvider)} + * on gradient converter. * @return the mean element rates dai/dt */ > T[] getMeanElementRate(FieldSpacecraftState state, - FieldAuxiliaryElements auxiliaryElements, T[] parameters); - - - /** Get the discrete events related to the model. - * @return array of events detectors or null if the model is not - * related to any discrete events - */ - EventDetector[] getEventsDetectors(); - - /** Get the discrete events related to the model. - * @param type of the elements - * @param field field used by default - * @return array of events detectors or null if the model is not - * related to any discrete events - */ - > FieldEventDetector[] getFieldEventsDetectors(Field field); + FieldAuxiliaryElements auxiliaryElements, T[] parameters); /** Register an attitude provider. *

        @@ -191,7 +239,11 @@ > T[] getMeanElementRate(FieldSpacecraftState< * are the ones that were returned during the call to {@link * #initializeShortPeriodTerms(AuxiliaryElements, PropagationType, double[])}. *

        - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (all span values for each parameters) + * obtained for example by calling + * {@link #getParametersAllValues()} + * on force model. The extract parameter method {@link #extractParameters(double[], AbsoluteDate)} is called in + * the method to select the right parameter corresponding to the mean state date. * @param meanStates mean states information: date, kinematics, attitude */ void updateShortPeriodTerms(double[] parameters, SpacecraftState... meanStates); @@ -203,7 +255,12 @@ > T[] getMeanElementRate(FieldSpacecraftState< * #initializeShortPeriodTerms(AuxiliaryElements, PropagationType, double[])}. *

        * @param type of the elements - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (all span values for each parameters) + * obtained for example by calling {@link #getParametersAllValues(Field)} on force model or + * {@link AbstractGradientConverter#getParameters(FieldSpacecraftState, ParameterDriversProvider)} + * on gradient converter. The extract parameter method + * {@link #extractParameters(CalculusFieldElement[], FieldAbsoluteDate)} is called in + * the method to select the right parameter. * @param meanStates mean states information: date, kinematics, attitude */ @SuppressWarnings("unchecked") diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java new file mode 100644 index 0000000000..f40f2292e6 --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedForm.java @@ -0,0 +1,178 @@ +/* Copyright 2022 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.semianalytical.dsst.forces; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.MathArrays; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.PropagationType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.utils.ParameterDriver; + +/** + * Second order J2-squared force model. + *

        + * The force model implements a closed-form of the J2-squared perturbation. + * The full realization of the model is based on a gaussian quadrature. + * Even if it is very accurate, a gaussian quadrature is usually time consuming. + * A closed-form is less accurate than a gaussian quadrature, but faster. + *

        + * @author Bryan Cazabonne + * @since 12.0 + */ +public class DSSTJ2SquaredClosedForm implements DSSTForceModel { + + /** Model for second order terms. */ + private final J2SquaredModel j2SquaredModel; + + /** Gravity field to use. */ + private final UnnormalizedSphericalHarmonicsProvider provider; + + /** + * Constructor. + * + * @param j2SquaredModel model for second order terms + * @param provider gravity field to use + */ + public DSSTJ2SquaredClosedForm(final J2SquaredModel j2SquaredModel, + final UnnormalizedSphericalHarmonicsProvider provider) { + // Initialize fields + this.j2SquaredModel = j2SquaredModel; + this.provider = provider; + } + + /** {@inheritDoc}. */ + @Override + public double[] getMeanElementRate(final SpacecraftState state, + final AuxiliaryElements auxiliaryElements, + final double[] parameters) { + + // Context + final DSSTJ2SquaredClosedFormContext context = new DSSTJ2SquaredClosedFormContext(auxiliaryElements, provider); + + // Second-order terms + final double[] delta = j2SquaredModel.computeMeanEquinoctialSecondOrderTerms(context); + + // J2 + final double J2 = -provider.onDate(state.getDate()).getUnnormalizedCnm(2, 0); + final double J2SquaredOver2 = 0.5 * J2 * J2; + + // Mean elements rate + final double da = 0.0; + final double dk = J2SquaredOver2 * delta[1]; + final double dh = J2SquaredOver2 * delta[2]; + final double dq = J2SquaredOver2 * delta[3]; + final double dp = J2SquaredOver2 * delta[4]; + final double dM = J2SquaredOver2 * delta[5]; + + // Return + return new double[] { da, dk, dh, dq, dp, dM }; + + } + + /** {@inheritDoc}. */ + @Override + public > T[] getMeanElementRate(final FieldSpacecraftState state, + final FieldAuxiliaryElements auxiliaryElements, + final T[] parameters) { + + // Field + final Field field = state.getDate().getField(); + + // Context + final FieldDSSTJ2SquaredClosedFormContext context = new FieldDSSTJ2SquaredClosedFormContext<>(auxiliaryElements, provider); + + // Second-order terms + final T[] delta = j2SquaredModel.computeMeanEquinoctialSecondOrderTerms(context); + + // J2 + final double J2 = -provider.onDate(state.getDate().toAbsoluteDate()).getUnnormalizedCnm(2, 0); + final double J2SquaredOver2 = 0.5 * J2 * J2; + + // Mean elements rate + final T da = field.getZero(); + final T dk = delta[1].multiply(J2SquaredOver2); + final T dh = delta[2].multiply(J2SquaredOver2); + final T dq = delta[3].multiply(J2SquaredOver2); + final T dp = delta[4].multiply(J2SquaredOver2); + final T dM = delta[5].multiply(J2SquaredOver2); + + // Return + final T[] elements = MathArrays.buildArray(field, 6); + elements[0] = da; + elements[1] = dk; + elements[2] = dh; + elements[3] = dq; + elements[4] = dp; + elements[5] = dM; + + return elements; + + } + + /** {@inheritDoc}. */ + @Override + public List initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements, + final PropagationType type, + final double[] parameters) { + // Currently, there is no short periods for J2-squared closed-form + return Collections.emptyList(); + } + + /** {@inheritDoc}. */ + @Override + public > List> initializeShortPeriodTerms(final FieldAuxiliaryElements auxiliaryElements, + final PropagationType type, + final T[] parameters) { + // Currently, there is no short periods for J2-squared closed-form + return Collections.emptyList(); + } + + /** {@inheritDoc}. */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** {@inheritDoc}. */ + @Override + public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) { + // Nothing is done since this contribution is not sensitive to attitude + } + + /** {@inheritDoc}. */ + @Override + public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) { + // Currently, there is no short periods for J2-squared closed-form + } + + /** {@inheritDoc}. */ + @Override + @SuppressWarnings("unchecked") + public > void updateShortPeriodTerms(final T[] parameters, final FieldSpacecraftState... meanStates) { + // Currently, there is no short periods for J2-squared closed-form + } + +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedFormContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedFormContext.java new file mode 100644 index 0000000000..ffe452325a --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTJ2SquaredClosedFormContext.java @@ -0,0 +1,117 @@ +/* Copyright 2022 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.semianalytical.dsst.forces; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.SinCos; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; + +/** + * This class is a container for the common parameters used in {@link DSSTJ2SquaredClosedForm}. + *

        + * It performs parameters initialization at each integration step for the second-order J2-squared + * contribution to the central body gravitational perturbation. + *

        + * @author Bryan Cazabonne + * @since 12.0 + */ +public class DSSTJ2SquaredClosedFormContext extends ForceModelContext { + + /** Equatorial radius of the central body to the power 4. */ + private final double alpha4; + + /** Semi major axis to the power 4. */ + private final double a4; + + /** sqrt(1 - e * e). */ + private final double eta; + + /** Cosine of the inclination. */ + private final double c; + + /** Sine of the inclination to the power 2. */ + private final double s2; + + /** + * Simple constructor. + * + * @param auxiliaryElements auxiliary elements related to the current orbit + * @param provider provider for spherical harmonics + */ + public DSSTJ2SquaredClosedFormContext(final AuxiliaryElements auxiliaryElements, + final UnnormalizedSphericalHarmonicsProvider provider) { + super(auxiliaryElements); + + // Sine and cosine of the inclination + final double inc = auxiliaryElements.getOrbit().getI(); + final SinCos scI = FastMath.sinCos(inc); + + // Other parameters + this.c = scI.cos(); + this.s2 = scI.sin() * scI.sin(); + + final double alpha2 = provider.getAe() * provider.getAe(); + this.alpha4 = alpha2 * alpha2; + + this.eta = FastMath.sqrt(1.0 - auxiliaryElements.getEcc() * auxiliaryElements.getEcc()); + + final double a2 = auxiliaryElements.getSma() * auxiliaryElements.getSma(); + this.a4 = a2 * a2; + } + + /** + * Get the equatorial radius of the central body to the power 4. + * @return the equatorial radius of the central body to the power 4 + */ + public double getAlpha4() { + return alpha4; + } + + /** + * Get the semi major axis to the power 4. + * @return the semi major axis to the power 4 + */ + public double getA4() { + return a4; + } + + /** + * Get the eta value. + * @return sqrt(1 - e * e) + */ + public double getEta() { + return eta; + } + + /** + * Get the cosine of the inclination. + * @return the cosine of the inclination + */ + public double getC() { + return c; + } + + /** + * Get the sine of the inclination to the power 2. + * @return the sine of the inclination to the power 2 + */ + public double getS2() { + return s2; + } + +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttraction.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttraction.java index 61bfcc6ff3..7a6445940e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttraction.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttraction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,23 +19,22 @@ import java.util.Collections; import java.util.List; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.orekit.attitudes.AttitudeProvider; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; /** Force model for Newtonian central body attraction for the {@link DSSTPropagator DSST propagator}. @@ -68,11 +67,12 @@ public DSSTNewtonianAttraction(final double mu) { 0.0, Double.POSITIVE_INFINITY); } - /** Get the central attraction coefficient μ. + /** Get the central attraction coefficient μ at specific date. + * @param date date at which mu wants to be known * @return mu central attraction coefficient (m³/s²) */ - public double getMu() { - return gmParameterDriver.getValue(); + public double getMu(final AbsoluteDate date) { + return gmParameterDriver.getValue(date); } /** {@inheritDoc} */ @@ -128,7 +128,7 @@ public double[] getMeanElementRate(final SpacecraftState state, final double[] yDot = new double[7]; final EquinoctialOrbit orbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(state.getOrbit()); - orbit.addKeplerContribution(PositionAngle.MEAN, context.getGM(), yDot); + orbit.addKeplerContribution(PositionAngleType.MEAN, context.getGM(), yDot); return yDot; @@ -147,23 +147,11 @@ public > T[] getMeanElementRate(final FieldSpa final T[] yDot = MathArrays.buildArray(field, 7); final FieldEquinoctialOrbit orbit = (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(state.getOrbit()); - orbit.addKeplerContribution(PositionAngle.MEAN, context.getGM(), yDot); + orbit.addKeplerContribution(PositionAngleType.MEAN, context.getGM(), yDot); return yDot; } - /** {@inheritDoc} */ - @Override - public EventDetector[] getEventsDetectors() { - return null; - } - - /** {@inheritDoc} */ - @Override - public > FieldEventDetector[] getFieldEventsDetectors(final Field field) { - return null; - } - /** {@inheritDoc} */ @Override public void registerAttitudeProvider(final AttitudeProvider provider) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java index ca52cb232f..29d6b1f93a 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,7 +22,7 @@ * This class is a container for the common parameters used in {@link DSSTNewtonianAttraction}. *

        * It performs parameters initialization at each integration step for the central body attraction. - *

        + *

        * @author Bryan Cazabonne * @since 10.0 */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java index 97859071ad..7a575af301 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,21 +18,20 @@ import java.util.List; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; +import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.forces.radiation.IsotropicRadiationSingleCoefficient; import org.orekit.forces.radiation.RadiationSensitive; import org.orekit.forces.radiation.SolarRadiationPressure; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.utils.ExtendedPVCoordinatesProvider; @@ -90,15 +89,15 @@ public class DSSTSolarRadiationPressure extends AbstractGaussianContribution { * @param cr satellite radiation pressure coefficient (assuming total specular reflection) * @param area cross sectional area of satellite * @param sun Sun model - * @param equatorialRadius central body equatorial radius (for shadow computation) + * @param centralBody central body (for shadow computation) * @param mu central attraction coefficient - * @since 9.2 + * @since 12.0 */ public DSSTSolarRadiationPressure(final double cr, final double area, final ExtendedPVCoordinatesProvider sun, - final double equatorialRadius, + final OneAxisEllipsoid centralBody, final double mu) { - this(D_REF, P_REF, cr, area, sun, equatorialRadius, mu); + this(D_REF, P_REF, cr, area, sun, centralBody, mu); } /** @@ -112,16 +111,16 @@ public DSSTSolarRadiationPressure(final double cr, final double area, * * * @param sun Sun model - * @param equatorialRadius central body equatorial radius (for shadow computation) + * @param centralBody central body (for shadow computation) * @param spacecraft spacecraft model * @param mu central attraction coefficient - * @since 9.2 + * @since 12.0 */ public DSSTSolarRadiationPressure(final ExtendedPVCoordinatesProvider sun, - final double equatorialRadius, + final OneAxisEllipsoid centralBody, final RadiationSensitive spacecraft, final double mu) { - this(D_REF, P_REF, sun, equatorialRadius, spacecraft, mu); + this(D_REF, P_REF, sun, centralBody, spacecraft, mu); } /** @@ -138,21 +137,21 @@ public DSSTSolarRadiationPressure(final ExtendedPVCoordinatesProvider sun, * @param cr satellite radiation pressure coefficient (assuming total specular reflection) * @param area cross sectional area of satellite * @param sun Sun model - * @param equatorialRadius central body equatorial radius (for shadow computation) + * @param centralBody central body (for shadow computation) * @param mu central attraction coefficient - * @since 9.2 + * @since 12.0 */ public DSSTSolarRadiationPressure(final double dRef, final double pRef, final double cr, final double area, final ExtendedPVCoordinatesProvider sun, - final double equatorialRadius, + final OneAxisEllipsoid centralBody, final double mu) { // cR being the DSST SRP coef and assuming a spherical spacecraft, // the conversion is: // cR = 1 + (1 - kA) * (1 - kR) * 4 / 9 // with kA arbitrary sets to 0 - this(dRef, pRef, sun, equatorialRadius, new IsotropicRadiationSingleCoefficient(area, cr), mu); + this(dRef, pRef, sun, centralBody, new IsotropicRadiationSingleCoefficient(area, cr), mu); } /** @@ -167,23 +166,23 @@ public DSSTSolarRadiationPressure(final double dRef, final double pRef, * @param dRef reference distance for the solar radiation pressure (m) * @param pRef reference solar radiation pressure at dRef (N/m²) * @param sun Sun model - * @param equatorialRadius central body equatorial radius (for shadow computation) + * @param centralBody central body shape model (for umbra/penumbra computation) * @param spacecraft spacecraft model * @param mu central attraction coefficient - * @since 9.2 + * @since 12.0 */ public DSSTSolarRadiationPressure(final double dRef, final double pRef, final ExtendedPVCoordinatesProvider sun, - final double equatorialRadius, + final OneAxisEllipsoid centralBody, final RadiationSensitive spacecraft, final double mu) { //Call to the constructor from superclass using the numerical SRP model as ForceModel super(PREFIX, GAUSS_THRESHOLD, - new SolarRadiationPressure(dRef, pRef, sun, equatorialRadius, spacecraft), mu); + new SolarRadiationPressure(dRef, pRef, sun, centralBody, spacecraft), mu); this.sun = sun; - this.ae = equatorialRadius; + this.ae = centralBody.getEquatorialRadius(); this.spacecraft = spacecraft; } @@ -194,17 +193,6 @@ public RadiationSensitive getSpacecraft() { return spacecraft; } - /** {@inheritDoc} */ - public EventDetector[] getEventsDetectors() { - return null; - } - - /** {@inheritDoc} */ - @Override - public > FieldEventDetector[] getFieldEventsDetectors(final Field field) { - return null; - } - /** {@inheritDoc} */ protected List getParametersDriversWithoutMu() { return spacecraft.getRadiationParametersDrivers(); @@ -218,7 +206,7 @@ protected double[] getLLimits(final SpacecraftState state, final AuxiliaryElemen FastMath.PI + MathUtils.normalizeAngle(state.getLv(), 0)}; // Direction cosines of the Sun in the equinoctial frame - final Vector3D sunDir = sun.getPVCoordinates(state.getDate(), state.getFrame()).getPosition().normalize(); + final Vector3D sunDir = sun.getPosition(state.getDate(), state.getFrame()).normalize(); final double alpha = sunDir.dotProduct(auxiliaryElements.getVectorF()); final double beta = sunDir.dotProduct(auxiliaryElements.getVectorG()); final double gamma = sunDir.dotProduct(auxiliaryElements.getVectorW()); @@ -314,7 +302,7 @@ protected > T[] getLLimits(final FieldSpacecra ll[1] = MathUtils.normalizeAngle(state.getLv(), zero).add(pi); // Direction cosines of the Sun in the equinoctial frame - final FieldVector3D sunDir = sun.getPVCoordinates(state.getDate(), state.getFrame()).getPosition().normalize(); + final FieldVector3D sunDir = sun.getPosition(state.getDate(), state.getFrame()).normalize(); final T alpha = sunDir.dotProduct(auxiliaryElements.getVectorF()); final T beta = sunDir.dotProduct(auxiliaryElements.getVectorG()); final T gamma = sunDir.dotProduct(auxiliaryElements.getVectorW()); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java index b5b830f707..51da268b84 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseral.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,8 +27,8 @@ import java.util.SortedMap; import java.util.TreeMap; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldGradient; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -43,7 +43,7 @@ import org.orekit.errors.OrekitInternalError; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; import org.orekit.frames.StaticTransform; import org.orekit.orbits.FieldOrbit; @@ -51,8 +51,6 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; @@ -333,6 +331,7 @@ public List initializeShortPeriodTerms(final AuxiliaryElements final double[] parameters) { // Initializes specific parameters. + final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters); // Set the highest power of the eccentricity in the analytical power @@ -439,7 +438,10 @@ private int getMaxEccPow(final double e) { * This method aims at being called before mean elements rates computation. *

        * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (only 1 value for each parameter) + * that is to say that the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} + * should have be called before or the parameters list given in argument must correspond + * to the extraction of parameter for a precise date {@link #getParameters(AbsoluteDate)}. * @return new force model context */ private DSSTTesseralContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) { @@ -452,7 +454,8 @@ private DSSTTesseralContext initializeStep(final AuxiliaryElements auxiliaryElem *

        * @param type of the elements * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters list of each estimated values for each driver of the force model parameters + * (each span of each driver) * @return new force model context */ private > FieldDSSTTesseralContext initializeStep(final FieldAuxiliaryElements auxiliaryElements, @@ -466,6 +469,7 @@ public double[] getMeanElementRate(final SpacecraftState spacecraftState, final AuxiliaryElements auxiliaryElements, final double[] parameters) { // Container for attributes + final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters); // Access to potential U derivatives @@ -499,6 +503,7 @@ public > T[] getMeanElementRate(final FieldSpa final Field field = auxiliaryElements.getDate().getField(); // Container for attributes + final FieldDSSTTesseralContext context = initializeStep(auxiliaryElements, parameters); @SuppressWarnings("unchecked") @@ -543,7 +548,9 @@ public void updateShortPeriodTerms(final double[] parameters, final SpacecraftSt final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I); - final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters); + // Extract the proper parameters valid at date from the input array + final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + final DSSTTesseralContext context = initializeStep(auxiliaryElements, extractedParameters); // Initialise the Hansen coefficients for (int s = -maxDegree; s <= maxDegree; s++) { @@ -606,7 +613,9 @@ public > void updateShortPeriodTerms(final T[] final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I); - final FieldDSSTTesseralContext context = initializeStep(auxiliaryElements, parameters); + // Extract the proper parameters valid at date from the input array + final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + final FieldDSSTTesseralContext context = initializeStep(auxiliaryElements, extractedParameters); final FieldHansenObjects fho = (FieldHansenObjects) fieldHansen.get(field); // Initialise the Hansen coefficients @@ -752,18 +761,6 @@ private > void buildCoefficients(final FieldFo } - /** {@inheritDoc} */ - @Override - public EventDetector[] getEventsDetectors() { - return null; - } - - /** {@inheritDoc} */ - @Override - public > FieldEventDetector[] getFieldEventsDetectors(final Field field) { - return null; - } - /** * Get the resonant and non-resonant tesseral terms in the central body spherical harmonic field. * @@ -1995,7 +1992,7 @@ public T[] value(final FieldOrbit meanOrbit) { final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(meanOrbit, I); // Central body rotation angle from equation 2.7.1-(3)(4). - final FieldTransform t = bodyFrame.getTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); + final FieldStaticTransform t = bodyFrame.getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); final FieldVector3D xB = t.transformVector(Vector3D.PLUS_I); final FieldVector3D yB = t.transformVector(Vector3D.PLUS_J); final FieldVector3D f = auxiliaryElements.getVectorF(); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java index 9820cd26a5..879b066591 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,7 +28,7 @@ *

        * It performs parameters initialization at each integration step for the Tesseral contribution * to the central body gravitational perturbation. - *

        + *

        * @author Bryan Cazabonne * @since 10.0 */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java index 46a208d8f0..b214b1137d 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -42,8 +42,6 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient; import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory; @@ -156,7 +154,7 @@ public DSSTThirdBody(final CelestialBody body, final double mu) { 0.0, Double.POSITIVE_INFINITY)); this.body = body; - this.Vns = CoefficientsFactory.computeVnsCoefficients(MAX_POWER); + this.Vns = CoefficientsFactory.computeVns(MAX_POWER); this.doesStaticContextNeedsInitialization = true; fieldShortPeriods = new HashMap<>(); @@ -194,7 +192,7 @@ private void initializeStaticContextIfNeeded(final AuxiliaryElements auxiliaryEl *

        * @param auxiliaryElements auxiliary elements related to the current orbit * @param type type of the elements used during the propagation - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters for state date (1 value for each parameters) */ @Override public List initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements, @@ -257,7 +255,10 @@ public > List> initia * This method aims at being called before mean elements rates computation. *

        * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (only 1 values for each parameters corresponding + * to state date) obtained by calling the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} + * to selected the right value for state date or by getting the parameters for a specific date + * {@link #getParameters(AbsoluteDate)}. * @return new force model context */ private DSSTThirdBodyDynamicContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) { @@ -270,7 +271,7 @@ private DSSTThirdBodyDynamicContext initializeStep(final AuxiliaryElements auxil *

        * @param type of the elements * @param auxiliaryElements auxiliary elements related to the current orbit - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters at state date (1 value per parameter driver) * @return new force model context */ private > FieldDSSTThirdBodyDynamicContext initializeStep(final FieldAuxiliaryElements auxiliaryElements, @@ -381,8 +382,11 @@ public void updateShortPeriodTerms(final double[] parameters, final SpacecraftSt // Auxiliary elements related to the current orbit final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I); + // Extract the proper parameters valid for the corresponding meanState date from the input array + final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + // Container of attributes - final DSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, parameters); + final DSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, extractedParameters); // a / R3 up to power maxAR3Pow final double[] aoR3Pow = computeAoR3Pow(context); @@ -479,8 +483,11 @@ public > void updateShortPeriodTerms(final T[] // Auxiliary elements related to the current orbit final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I); + // Extract the proper parameters valid for the corresponding meanState date from the input array + final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + // Container of attributes - final FieldDSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, parameters); + final FieldDSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, extractedParameters); // a / R3 up to power maxAR3Pow final T[] aoR3Pow = computeAoR3Pow(context); @@ -596,19 +603,6 @@ private > T[] computeAoR3Pow(final FieldDSSTTh return aoR3Pow; } - - /** {@inheritDoc} */ - @Override - public EventDetector[] getEventsDetectors() { - return null; - } - - /** {@inheritDoc} */ - @Override - public > FieldEventDetector[] getFieldEventsDetectors(final Field field) { - return null; - } - /** {@inheritDoc} */ @Override public void registerAttitudeProvider(final AttitudeProvider provider) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyContext.java deleted file mode 100644 index ac7145c7ef..0000000000 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyContext.java +++ /dev/null @@ -1,413 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.semianalytical.dsst.forces; - -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.CombinatoricsUtils; -import org.hipparchus.util.FastMath; -import org.orekit.bodies.CelestialBody; -import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; -import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory; -import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds; - -/** - * This class is a container for the common parameters used in {@link DSSTThirdBody}. - *

        - * It performs parameters initialization at each integration step for the third - * body attraction perturbation. - *

        - * @author Bryan Cazabonne - * @since 10.0 - */ -@Deprecated -public class DSSTThirdBodyContext extends ForceModelContext { - - /** Max power for summation. */ - private static final int MAX_POWER = 22; - - /** Truncation tolerance for big, eccentric orbits. */ - private static final double BIG_TRUNCATION_TOLERANCE = 1.e-1; - - /** Truncation tolerance for small orbits. */ - private static final double SMALL_TRUNCATION_TOLERANCE = 1.9e-6; - - /** Maximum power for eccentricity used in short periodic computation. */ - private static final int MAX_ECCPOWER_SP = 4; - - /** Max power for a/R3 in the serie expansion. */ - private int maxAR3Pow; - - /** Max power for e in the serie expansion. */ - private int maxEccPow; - - /** a / R3 up to power maxAR3Pow. */ - private double[] aoR3Pow; - - /** Max power for e in the serie expansion (for short periodics). */ - private int maxEccPowShort; - - /** Max frequency of F. */ - private int maxFreqF; - - /** Qns coefficients. */ - private double[][] Qns; - - /** Standard gravitational parameter μ for the body in m³/s². */ - private final double gm; - - /** Distance from center of mass of the central body to the 3rd body. */ - private double R3; - - /** A = sqrt(μ * a). */ - private final double A; - - // Direction cosines of the symmetry axis - /** α. */ - private double alpha; - /** β. */ - private double beta; - /** γ. */ - private double gamma; - - /** B². */ - private double BB; - /** B³. */ - private double BBB; - - /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ - private double X; - /** Χ². */ - private double XX; - /** Χ³. */ - private double XXX; - /** -2 * a / A. */ - private double m2aoA; - /** B / A. */ - private double BoA; - /** 1 / (A * B). */ - private double ooAB; - /** -C / (2 * A * B). */ - private double mCo2AB; - /** B / A(1 + B). */ - private double BoABpo; - - /** mu3 / R3. */ - private double muoR3; - - /** b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B).*/ - private double b; - - /** h * Χ³. */ - private double hXXX; - /** k * Χ³. */ - private double kXXX; - - /** Keplerian mean motion. */ - private final double motion; - - /** - * Simple constructor. - * - * @param auxiliaryElements auxiliary elements related to the current orbit - * @param thirdBody body the 3rd body to consider - * @param parameters values of the force model parameters - */ - DSSTThirdBodyContext(final AuxiliaryElements auxiliaryElements, final CelestialBody thirdBody, final double[] parameters) { - - super(auxiliaryElements); - - final double mu = parameters[1]; - A = FastMath.sqrt(mu * auxiliaryElements.getSma()); - this.gm = parameters[0]; - - // Keplerian Mean Motion - final double absA = FastMath.abs(auxiliaryElements.getSma()); - motion = FastMath.sqrt(mu / absA) / absA; - - // Distance from center of mass of the central body to the 3rd body - final Vector3D bodyPos = thirdBody.getPVCoordinates(auxiliaryElements.getDate(), auxiliaryElements.getFrame()).getPosition(); - R3 = bodyPos.getNorm(); - - // Direction cosines - final Vector3D bodyDir = bodyPos.normalize(); - alpha = bodyDir.dotProduct(auxiliaryElements.getVectorF()); - beta = bodyDir.dotProduct(auxiliaryElements.getVectorG()); - gamma = bodyDir.dotProduct(auxiliaryElements.getVectorW()); - - //Χ-2. - BB = auxiliaryElements.getB() * auxiliaryElements.getB(); - //Χ-3. - BBB = BB * auxiliaryElements.getB(); - - //b = 1 / (1 + B) - b = 1. / (1. + auxiliaryElements.getB()); - - // Χ - X = 1. / auxiliaryElements.getB(); - XX = X * X; - XXX = X * XX; - // -2 * a / A - m2aoA = -2. * auxiliaryElements.getSma() / A; - // B / A - BoA = auxiliaryElements.getB() / A; - // 1 / AB - ooAB = 1. / (A * auxiliaryElements.getB()); - // -C / 2AB - mCo2AB = -auxiliaryElements.getC() * ooAB / 2.; - // B / A(1 + B) - BoABpo = BoA / (1. + auxiliaryElements.getB()); - - // mu3 / R3 - muoR3 = gm / R3; - - //h * Χ³ - hXXX = auxiliaryElements.getH() * XXX; - //k * Χ³ - kXXX = auxiliaryElements.getK() * XXX; - - // Truncation tolerance. - final double aoR3 = auxiliaryElements.getSma() / R3; - final double tol = ( aoR3 > .3 || aoR3 > .15 && auxiliaryElements.getEcc() > .25 ) ? BIG_TRUNCATION_TOLERANCE : SMALL_TRUNCATION_TOLERANCE; - - // Utilities for truncation - // Set a lower bound for eccentricity - final double eo2 = FastMath.max(0.0025, auxiliaryElements.getEcc() / 2.); - final double x2o2 = XX / 2.; - final double[] eccPwr = new double[MAX_POWER]; - final double[] chiPwr = new double[MAX_POWER]; - eccPwr[0] = 1.; - chiPwr[0] = X; - for (int i = 1; i < MAX_POWER; i++) { - eccPwr[i] = eccPwr[i - 1] * eo2; - chiPwr[i] = chiPwr[i - 1] * x2o2; - } - - // Auxiliary quantities. - final double ao2rxx = aoR3 / (2. * XX); - double xmuarn = ao2rxx * ao2rxx * gm / (X * R3); - double term = 0.; - - // Compute max power for a/R3 and e. - maxAR3Pow = 2; - maxEccPow = 0; - int n = 2; - int m = 2; - int nsmd2 = 0; - - do { - // Upper bound for Tnm. - term = xmuarn * - (CombinatoricsUtils.factorialDouble(n + m) / (CombinatoricsUtils.factorialDouble(nsmd2) * CombinatoricsUtils.factorialDouble(nsmd2 + m))) * - (CombinatoricsUtils.factorialDouble(n + m + 1) / (CombinatoricsUtils.factorialDouble(m) * CombinatoricsUtils.factorialDouble(n + 1))) * - (CombinatoricsUtils.factorialDouble(n - m + 1) / CombinatoricsUtils.factorialDouble(n + 1)) * - eccPwr[m] * UpperBounds.getDnl(XX, chiPwr[m], n + 2, m); - - if (term < tol) { - if (m == 0) { - break; - } else if (m < 2) { - xmuarn *= ao2rxx; - m = 0; - n++; - nsmd2++; - } else { - m -= 2; - nsmd2++; - } - } else { - maxAR3Pow = n; - maxEccPow = FastMath.max(m, maxEccPow); - xmuarn *= ao2rxx; - m++; - n++; - } - } while (n < MAX_POWER); - - maxEccPow = FastMath.min(maxAR3Pow, maxEccPow); - - // allocate the array aoR3Pow - aoR3Pow = new double[maxAR3Pow + 1]; - - aoR3Pow[0] = 1.; - for (int i = 1; i <= maxAR3Pow; i++) { - aoR3Pow[i] = aoR3 * aoR3Pow[i - 1]; - } - - maxFreqF = maxAR3Pow + 1; - maxEccPowShort = MAX_ECCPOWER_SP; - - Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort)); - - } - - /** Get A = sqrt(μ * a). - * @return A - */ - public double getA() { - return A; - } - - /** Get direction cosine α for central body. - * @return α - */ - public double getAlpha() { - return alpha; - } - - /** Get direction cosine β for central body. - * @return β - */ - public double getBeta() { - return beta; - } - - /** Get direction cosine γ for central body. - * @return γ - */ - public double getGamma() { - return gamma; - } - - /** Get B². - * @return B² - */ - public double getBB() { - return BB; - } - - /** Get B³. - * @return B³ - */ - public double getBBB() { - return BBB; - } - - /** Get b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B). - * @return b - */ - public double getb() { - return b; - } - - /** Get Χ = 1 / sqrt(1 - e²) = 1 / B. - * @return Χ - */ - public double getX() { - return X; - } - - /** Get m2aoA = -2 * a / A. - * @return m2aoA - */ - public double getM2aoA() { - return m2aoA; - } - - /** Get B / A. - * @return BoA - */ - public double getBoA() { - return BoA; - } - - /** Get ooAB = 1 / (A * B). - * @return ooAB - */ - public double getOoAB() { - return ooAB; - } - - /** Get mCo2AB = -C / 2AB. - * @return mCo2AB - */ - public double getMCo2AB() { - return mCo2AB; - } - - /** Get BoABpo = B / A(1 + B). - * @return BoABpo - */ - public double getBoABpo() { - return BoABpo; - } - - /** Get muoR3 = mu3 / R3. - * @return muoR3 - */ - public double getMuoR3() { - return muoR3; - } - - /** Get hXXX = h * Χ³. - * @return hXXX - */ - public double getHXXX() { - return hXXX; - } - - /** Get kXXX = h * Χ³. - * @return kXXX - */ - public double getKXXX() { - return kXXX; - } - - /** Get the value of max power for a/R3 in the serie expansion. - * @return maxAR3Pow - */ - public int getMaxAR3Pow() { - return maxAR3Pow; - } - - /** Get the value of max power for e in the serie expansion. - * @return maxEccPow - */ - public int getMaxEccPow() { - return maxEccPow; - } - - /** Get the value of a / R3 up to power maxAR3Pow. - * @return aoR3Pow - */ - public double[] getAoR3Pow() { - return aoR3Pow.clone(); - } - - /** Get the value of max frequency of F. - * @return maxFreqF - */ - public int getMaxFreqF() { - return maxFreqF; - } - - /** Get the Keplerian mean motion. - *

        The Keplerian mean motion is computed directly from semi major axis - * and central acceleration constant.

        - * @return Keplerian mean motion in radians per second - */ - public double getMeanMotion() { - return motion; - } - - /** Get the value of Qns coefficients. - * @return Qns - */ - public double[][] getQns() { - return Qns.clone(); - } - -} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyDynamicContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyDynamicContext.java index 11a16c0ff1..f2cab7c414 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyDynamicContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyDynamicContext.java @@ -28,7 +28,7 @@ * It performs parameters initialization at each integration step for the third * body attraction perturbation. These parameters change for each integration * step. - *

        + *

        * @author Bryan Cazabonne * @since 11.3.3 */ @@ -115,7 +115,7 @@ public DSSTThirdBodyDynamicContext(final AuxiliaryElements aux, motion = FastMath.sqrt(mu / absA) / absA; // Distance from center of mass of the central body to the 3rd body - final Vector3D bodyPos = body.getPVCoordinates(aux.getDate(), aux.getFrame()).getPosition(); + final Vector3D bodyPos = body.getPosition(aux.getDate(), aux.getFrame()); R3 = bodyPos.getNorm(); // Direction cosines diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyStaticContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyStaticContext.java index 9af4366def..0fc8ca66f2 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyStaticContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyStaticContext.java @@ -28,7 +28,7 @@ * body attraction perturbation. These parameters are initialize as soon as * possible. In fact, they are initialized once with short period terms and * don't evolve during propagation. - *

        + *

        * @author Bryan Cazabonne * @since 11.3.3 */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java index 855071b91b..be3b2c1f2d 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonal.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,8 +44,6 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.PropagationType; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.events.EventDetector; -import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient; import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory; @@ -185,7 +183,7 @@ public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider, 0.0, Double.POSITIVE_INFINITY); // Vns coefficients - this.Vns = CoefficientsFactory.computeVnsCoefficients(provider.getMaxDegree() + 1); + this.Vns = CoefficientsFactory.computeVns(provider.getMaxDegree() + 1); this.provider = provider; this.maxDegree = provider.getMaxDegree(); @@ -284,7 +282,6 @@ public > List> initia // Field used by default final Field field = auxiliaryElements.getDate().getField(); - computeMeanElementsTruncations(auxiliaryElements, parameters, field); switch (type) { @@ -313,7 +310,7 @@ public > List> initia /** Compute indices truncations for mean elements computations. * @param auxiliaryElements auxiliary elements - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters for state date (only 1 value for each parameter) */ private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryElements, final double[] parameters) { @@ -550,6 +547,7 @@ public double[] getMeanElementRate(final SpacecraftState spacecraftState, final AuxiliaryElements auxiliaryElements, final double[] parameters) { // Container of attributes + final DSSTZonalContext context = initializeStep(auxiliaryElements, parameters); // Access to potential U derivatives final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, auxiliaryElements, hansen); @@ -579,18 +577,6 @@ public > T[] getMeanElementRate(final FieldSpa } - /** {@inheritDoc} */ - @Override - public EventDetector[] getEventsDetectors() { - return null; - } - - /** {@inheritDoc} */ - @Override - public > FieldEventDetector[] getFieldEventsDetectors(final Field field) { - return null; - } - /** Compute the mean element rates. * @param date current date * @param context container for attributes @@ -695,7 +681,9 @@ public void updateShortPeriodTerms(final double[] parameters, final SpacecraftSt final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I); // Container of attributes - final DSSTZonalContext context = initializeStep(auxiliaryElements, parameters); + // Extract the proper parameters valid for the corresponding meanState date from the input array + final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + final DSSTZonalContext context = initializeStep(auxiliaryElements, extractedParameters); // Access to potential U derivatives final UAnddU udu = new UAnddU(meanState.getDate(), context, auxiliaryElements, hansen); @@ -736,7 +724,9 @@ public > void updateShortPeriodTerms(final T[] final FieldAuxiliaryElements auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I); // Container of attributes - final FieldDSSTZonalContext context = initializeStep(auxiliaryElements, parameters); + // Extract the proper parameters valid for the corresponding meanState date from the input array + final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate()); + final FieldDSSTZonalContext context = initializeStep(auxiliaryElements, extractedParameters); final FieldHansenObjects fho = (FieldHansenObjects) fieldHansen.get(field); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java index d858346ae8..2d37b0b4af 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,7 +25,7 @@ *

        * It performs parameters initialization at each integration step for the Zonal contribution * to the central body gravitational perturbation. - *

        + *

        * @author Bryan Cazabonne * @since 10.0 */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldAbstractGaussianContributionContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldAbstractGaussianContributionContext.java index c354af4a00..36fd0cf751 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldAbstractGaussianContributionContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldAbstractGaussianContributionContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,6 +19,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.util.FastMath; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.time.AbsoluteDate; /** * This class is a container for the common "field" parameters used in {@link AbstractGaussianContribution}. @@ -27,6 +28,7 @@ *

        * @author Bryan Cazabonne * @since 10.0 + * @param type of the field elements */ public class FieldAbstractGaussianContributionContext> extends FieldForceModelContext { @@ -66,6 +68,11 @@ public class FieldAbstractGaussianContributionContext auxiliaryElements, final T[] parameters) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java new file mode 100644 index 0000000000..18c2f8685d --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java @@ -0,0 +1,119 @@ +/* Copyright 2022 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.semianalytical.dsst.forces; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; + +/** + * This class is a container for the common parameters used in {@link DSSTJ2SquaredClosedForm}. + *

        + * It performs parameters initialization at each integration step for the second-order J2-squared + * contribution to the central body gravitational perturbation. + *

        + * @author Bryan Cazabonne + * @since 12.0 + * @param type of the field elements + */ +public class FieldDSSTJ2SquaredClosedFormContext> extends FieldForceModelContext { + + /** Equatorial radius of the central body to the power 4. */ + private final double alpha4; + + /** Semi major axis to the power 4. */ + private final T a4; + + /** sqrt(1 - e * e). */ + private final T eta; + + /** Cosine of the inclination. */ + private final T c; + + /** Sine of the inclination to the power 2. */ + private final T s2; + + /** + * Simple constructor. + * + * @param auxiliaryElements auxiliary elements related to the current orbit + * @param provider provider for spherical harmonics + */ + public FieldDSSTJ2SquaredClosedFormContext(final FieldAuxiliaryElements auxiliaryElements, + final UnnormalizedSphericalHarmonicsProvider provider) { + super(auxiliaryElements); + + // Sine and cosine of the inclination + final T inc = auxiliaryElements.getOrbit().getI(); + final FieldSinCos scI = FastMath.sinCos(inc); + + // Other parameters + this.c = scI.cos(); + this.s2 = scI.sin().multiply(scI.sin()); + + final double alpha2 = provider.getAe() * provider.getAe(); + this.alpha4 = alpha2 * alpha2; + + this.eta = FastMath.sqrt(auxiliaryElements.getEcc().multiply(auxiliaryElements.getEcc()).negate().add(1.0)); + + final T a2 = auxiliaryElements.getSma().multiply(auxiliaryElements.getSma()); + this.a4 = a2.multiply(a2); + } + + /** + * Get the equatorial radius of the central body to the power 4. + * @return the equatorial radius of the central body to the power 4 + */ + public double getAlpha4() { + return alpha4; + } + + /** + * Get the semi major axis to the power 4. + * @return the semi major axis to the power 4 + */ + public T getA4() { + return a4; + } + + /** + * Get the eta value. + * @return sqrt(1 - e * e) + */ + public T getEta() { + return eta; + } + + /** + * Get the cosine of the inclination. + * @return the cosine of the inclination + */ + public T getC() { + return c; + } + + /** + * Get the sine of the inclination to the power 2. + * @return the sine of the inclination to the power 2 + */ + public T getS2() { + return s2; + } + +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionContext.java index 0f814b56c9..f8aaa73063 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,9 +23,10 @@ * This class is a container for the common "field" parameters used in {@link DSSTNewtonianAttraction}. *

        * It performs parameters initialization at each integration step for the central body attraction. - *

        + *

        * @author Bryan Cazabonne * @since 10.0 + * @param type of the field elements */ public class FieldDSSTNewtonianAttractionContext> extends FieldForceModelContext { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java index 8eb08d37d0..477fe6d384 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTTesseralContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,23 +16,25 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.time.AbsoluteDate; /** * This class is a container for the common "field" parameters used in {@link DSSTTesseral}. *

        * It performs parameters initialization at each integration step for the Tesseral contribution * to the central body gravitational perturbation. - *

        + *

        * @author Bryan Cazabonne * @since 10.0 + * @param type of the field elements */ public class FieldDSSTTesseralContext> extends FieldForceModelContext { @@ -106,7 +108,10 @@ public class FieldDSSTTesseralContext> extends * @param provider provider for spherical harmonics * @param maxFrequencyShortPeriodics maximum value for j * @param bodyPeriod central body rotation period (seconds) - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (only 1 values + * for each parameters corresponding to state date) obtained by calling + * the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} + * to selected the right value for state date or by getting the parameters for a specific date */ FieldDSSTTesseralContext(final FieldAuxiliaryElements auxiliaryElements, final Frame centralBodyFrame, @@ -136,7 +141,7 @@ public class FieldDSSTTesseralContext> extends e2 = auxiliaryElements.getEcc().multiply(auxiliaryElements.getEcc()); // Central body rotation angle from equation 2.7.1-(3)(4). - final FieldTransform t = centralBodyFrame.getTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); + final FieldStaticTransform t = centralBodyFrame.getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate()); final FieldVector3D xB = t.transformVector(FieldVector3D.getPlusI(field)); final FieldVector3D yB = t.transformVector(FieldVector3D.getPlusJ(field)); theta = FastMath.atan2(auxiliaryElements.getVectorF().dotProduct(yB).negate().add((auxiliaryElements.getVectorG().dotProduct(xB)).multiply(I)), diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyContext.java deleted file mode 100644 index 45c1e124aa..0000000000 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyContext.java +++ /dev/null @@ -1,418 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.propagation.semianalytical.dsst.forces; - -import org.hipparchus.Field; -import org.hipparchus.CalculusFieldElement; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.util.CombinatoricsUtils; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; -import org.orekit.bodies.CelestialBody; -import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory; -import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; -import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds; - -/** - * This class is a container for the common "field" parameters used in {@link DSSTThirdBody}. - *

        - * It performs parameters initialization at each integration step for the third - * body attraction perturbation. - *

        - * @author Bryan Cazabonne - * @since 10.0 - */ -public class FieldDSSTThirdBodyContext> extends FieldForceModelContext { - - /** Max power for summation. */ - private static final int MAX_POWER = 22; - - /** Truncation tolerance for big, eccentric orbits. */ - private static final double BIG_TRUNCATION_TOLERANCE = 1.e-1; - - /** Truncation tolerance for small orbits. */ - private static final double SMALL_TRUNCATION_TOLERANCE = 1.9e-6; - - /** Maximum power for eccentricity used in short periodic computation. */ - private static final int MAX_ECCPOWER_SP = 4; - - /** Max power for a/R3 in the serie expansion. */ - private int maxAR3Pow; - - /** Max power for e in the serie expansion. */ - private int maxEccPow; - - /** a / R3 up to power maxAR3Pow. */ - private T[] aoR3Pow; - - /** Max power for e in the serie expansion (for short periodics). */ - private int maxEccPowShort; - - /** Max frequency of F. */ - private int maxFreqF; - - /** Qns coefficients. */ - private T[][] Qns; - - /** Standard gravitational parameter μ for the body in m³/s². */ - private final T gm; - - /** Distance from center of mass of the central body to the 3rd body. */ - private T R3; - - /** A = sqrt(μ * a). */ - private final T A; - - // Direction cosines of the symmetry axis - /** α. */ - private final T alpha; - /** β. */ - private final T beta; - /** γ. */ - private final T gamma; - - /** B². */ - private final T BB; - /** B³. */ - private final T BBB; - - /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ - private final T X; - /** Χ². */ - private final T XX; - /** Χ³. */ - private final T XXX; - /** -2 * a / A. */ - private final T m2aoA; - /** B / A. */ - private final T BoA; - /** 1 / (A * B). */ - private final T ooAB; - /** -C / (2 * A * B). */ - private final T mCo2AB; - /** B / A(1 + B). */ - private final T BoABpo; - - /** mu3 / R3. */ - private final T muoR3; - - /** b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B).*/ - private final T b; - - /** h * Χ³. */ - private final T hXXX; - /** k * Χ³. */ - private final T kXXX; - - /** Keplerian mean motion. */ - private final T motion; - - /** - * Simple constructor. - * - * @param auxiliaryElements auxiliary elements related to the current orbit - * @param thirdBody body the 3rd body to consider - * @param parameters values of the force model parameters - */ - FieldDSSTThirdBodyContext(final FieldAuxiliaryElements auxiliaryElements, - final CelestialBody thirdBody, - final T[] parameters) { - - super(auxiliaryElements); - - // Field for array building - final Field field = auxiliaryElements.getDate().getField(); - final T zero = field.getZero(); - - final T mu = parameters[1]; - A = FastMath.sqrt(mu.multiply(auxiliaryElements.getSma())); - - this.gm = parameters[0]; - - // Keplerian mean motion - final T absA = FastMath.abs(auxiliaryElements.getSma()); - motion = FastMath.sqrt(mu.divide(absA)).divide(absA); - - // Distance from center of mass of the central body to the 3rd body - final FieldVector3D bodyPos = thirdBody.getPVCoordinates(auxiliaryElements.getDate(), auxiliaryElements.getFrame()).getPosition(); - R3 = bodyPos.getNorm(); - - // Direction cosines - final FieldVector3D bodyDir = bodyPos.normalize(); - alpha = (T) bodyDir.dotProduct(auxiliaryElements.getVectorF()); - beta = (T) bodyDir.dotProduct(auxiliaryElements.getVectorG()); - gamma = (T) bodyDir.dotProduct(auxiliaryElements.getVectorW()); - - //Χ-2. - BB = auxiliaryElements.getB().multiply(auxiliaryElements.getB()); - //Χ-3. - BBB = BB.multiply(auxiliaryElements.getB()); - - //b = 1 / (1 + B) - b = auxiliaryElements.getB().add(1.).reciprocal(); - - // Χ - X = auxiliaryElements.getB().reciprocal(); - XX = X.multiply(X); - XXX = X.multiply(XX); - - // -2 * a / A - m2aoA = auxiliaryElements.getSma().multiply(-2.).divide(A); - // B / A - BoA = auxiliaryElements.getB().divide(A); - // 1 / AB - ooAB = (A.multiply(auxiliaryElements.getB())).reciprocal(); - // -C / 2AB - mCo2AB = auxiliaryElements.getC().multiply(ooAB).divide(2.).negate(); - // B / A(1 + B) - BoABpo = BoA.divide(auxiliaryElements.getB().add(1.)); - // mu3 / R3 - muoR3 = R3.divide(gm).reciprocal(); - //h * Χ³ - hXXX = XXX.multiply(auxiliaryElements.getH()); - //k * Χ³ - kXXX = XXX.multiply(auxiliaryElements.getK()); - - // Truncation tolerance. - final T aoR3 = auxiliaryElements.getSma().divide(R3); - final double tol = ( aoR3.getReal() > .3 || aoR3.getReal() > .15 && auxiliaryElements.getEcc().getReal() > .25) ? BIG_TRUNCATION_TOLERANCE : SMALL_TRUNCATION_TOLERANCE; - - // Utilities for truncation - // Set a lower bound for eccentricity - final T eo2 = FastMath.max(zero.add(0.0025), auxiliaryElements.getEcc().divide(2.)); - final T x2o2 = XX.divide(2.); - final T[] eccPwr = MathArrays.buildArray(field, MAX_POWER); - final T[] chiPwr = MathArrays.buildArray(field, MAX_POWER); - eccPwr[0] = zero.add(1.); - chiPwr[0] = X; - for (int i = 1; i < MAX_POWER; i++) { - eccPwr[i] = eccPwr[i - 1].multiply(eo2); - chiPwr[i] = chiPwr[i - 1].multiply(x2o2); - } - - // Auxiliary quantities. - final T ao2rxx = aoR3.divide(XX.multiply(2.)); - T xmuarn = ao2rxx.multiply(ao2rxx).multiply(gm).divide(X.multiply(R3)); - T term = zero; - - // Compute max power for a/R3 and e. - maxAR3Pow = 2; - maxEccPow = 0; - int n = 2; - int m = 2; - int nsmd2 = 0; - - do { - term = xmuarn.multiply((CombinatoricsUtils.factorialDouble(n + m) / (CombinatoricsUtils.factorialDouble(nsmd2) * CombinatoricsUtils.factorialDouble(nsmd2 + m))) * - (CombinatoricsUtils.factorialDouble(n + m + 1) / (CombinatoricsUtils.factorialDouble(m) * CombinatoricsUtils.factorialDouble(n + 1))) * - (CombinatoricsUtils.factorialDouble(n - m + 1) / CombinatoricsUtils.factorialDouble(n + 1))). - multiply(eccPwr[m]).multiply(UpperBounds.getDnl(XX, chiPwr[m], n + 2, m)); - - if (term.getReal() < tol) { - if (m == 0) { - break; - } else if (m < 2) { - xmuarn = xmuarn.multiply(ao2rxx); - m = 0; - n++; - nsmd2++; - } else { - m -= 2; - nsmd2++; - } - } else { - maxAR3Pow = n; - maxEccPow = FastMath.max(m, maxEccPow); - xmuarn = xmuarn.multiply(ao2rxx); - m++; - n++; - } - } while (n < MAX_POWER); - - maxEccPow = FastMath.min(maxAR3Pow, maxEccPow); - - // allocate the array aoR3Pow - aoR3Pow = MathArrays.buildArray(field, maxAR3Pow + 1); - - aoR3Pow[0] = field.getOne(); - for (int i = 1; i <= maxAR3Pow; i++) { - aoR3Pow[i] = aoR3.multiply(aoR3Pow[i - 1]); - } - - maxFreqF = maxAR3Pow + 1; - maxEccPowShort = MAX_ECCPOWER_SP; - - Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort)); - } - - /** Get A = sqrt(μ * a). - * @return A - */ - public T getA() { - return A; - } - - /** Get direction cosine α for central body. - * @return α - */ - public T getAlpha() { - return alpha; - } - - /** Get direction cosine β for central body. - * @return β - */ - public T getBeta() { - return beta; - } - - /** Get direction cosine γ for central body. - * @return γ - */ - public T getGamma() { - return gamma; - } - - /** Get B². - * @return B² - */ - public T getBB() { - return BB; - } - - /** Get B³. - * @return B³ - */ - public T getBBB() { - return BBB; - } - - /** Get b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B). - * @return b - */ - public T getb() { - return b; - } - - /** Get Χ = 1 / sqrt(1 - e²) = 1 / B. - * @return Χ - */ - public T getX() { - return X; - } - - /** Get m2aoA = -2 * a / A. - * @return m2aoA - */ - public T getM2aoA() { - return m2aoA; - } - - /** Get B / A. - * @return BoA - */ - public T getBoA() { - return BoA; - } - - /** Get ooAB = 1 / (A * B). - * @return ooAB - */ - public T getOoAB() { - return ooAB; - } - - /** Get mCo2AB = -C / 2AB. - * @return mCo2AB - */ - public T getMCo2AB() { - return mCo2AB; - } - - /** Get BoABpo = B / A(1 + B). - * @return BoABpo - */ - public T getBoABpo() { - return BoABpo; - } - - /** Get muoR3 = mu3 / R3. - * @return muoR3 - */ - public T getMuoR3() { - return muoR3; - } - - /** Get hXXX = h * Χ³. - * @return hXXX - */ - public T getHXXX() { - return hXXX; - } - - /** Get kXXX = h * Χ³. - * @return kXXX - */ - public T getKXXX() { - return kXXX; - } - - /** Get the value of max power for a/R3 in the serie expansion. - * @return maxAR3Pow - */ - public int getMaxAR3Pow() { - return maxAR3Pow; - } - - /** Get the value of max power for e in the serie expansion. - * @return maxEccPow - */ - public int getMaxEccPow() { - return maxEccPow; - } - - /** Get the value of a / R3 up to power maxAR3Pow. - * @return aoR3Pow - */ - public T[] getAoR3Pow() { - return aoR3Pow.clone(); - } - - /** Get the value of max frequency of F. - * @return maxFreqF - */ - public int getMaxFreqF() { - return maxFreqF; - } - - /** Get the Keplerian mean motion. - *

        The Keplerian mean motion is computed directly from semi major axis - * and central acceleration constant.

        - * @return Keplerian mean motion in radians per second - */ - public T getMeanMotion() { - return motion; - } - - /** Get the value of Qns coefficients. - * @return Qns - */ - public T[][] getQns() { - return Qns.clone(); - } - -} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java index 44aa2d1428..2673748c0e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java @@ -28,9 +28,10 @@ * It performs parameters initialization at each integration step for the third * body attraction perturbation. These parameters change for each integration * step. - *

        + *

        * @author Bryan Cazabonne * @since 12.0 + * @param type of the field elements */ public class FieldDSSTThirdBodyDynamicContext> extends FieldForceModelContext { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java index 2b47d46168..1075048e1c 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTZonalContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,15 +20,17 @@ import org.hipparchus.util.FastMath; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.time.AbsoluteDate; /** * This class is a container for the common "field" parameters used in {@link DSSTZonal}. *

        * It performs parameters initialization at each integration step for the Zonal contribution * to the central body gravitational perturbation. - *

        + *

        * @author Bryan Cazabonne * @since 10.0 + * @param type of the field elements */ public class FieldDSSTZonalContext> extends FieldForceModelContext { @@ -86,7 +88,10 @@ public class FieldDSSTZonalContext> extends Fi * * @param auxiliaryElements auxiliary elements related to the current orbit * @param provider provider for spherical harmonics - * @param parameters values of the force model parameters + * @param parameters values of the force model parameters (only 1 values + * for each parameters corresponding to state date) obtained by calling the extract + * parameter method {@link #extractParameters(double[], AbsoluteDate)} + * to selected the right value for state date or by getting the parameters for a specific date */ FieldDSSTZonalContext(final FieldAuxiliaryElements auxiliaryElements, final UnnormalizedSphericalHarmonicsProvider provider, diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldForceModelContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldForceModelContext.java index 60ac188c58..9ac2ec044a 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldForceModelContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldForceModelContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,6 +22,7 @@ /** Base class for dsst force models parameter containers. * @author Bryan Cazabonne * @since 10.0 + * @param type of the field elements */ public abstract class FieldForceModelContext> { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldShortPeriodTerms.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldShortPeriodTerms.java index e17e8d8950..7a9b33ff96 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldShortPeriodTerms.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldShortPeriodTerms.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,7 @@ *

        * @see DSSTForceModel * @author Luc Maisonobe + * @param type of the field elements */ public interface FieldShortPeriodTerms > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ForceModelContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ForceModelContext.java index bd340a08a7..e50dfcb291 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ForceModelContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ForceModelContext.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java new file mode 100644 index 0000000000..4279eeb91d --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/J2SquaredModel.java @@ -0,0 +1,52 @@ +/* Copyright 2022 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.semianalytical.dsst.forces; + +import org.hipparchus.CalculusFieldElement; + +/** + * Semi-analytical J2-squared model. + *

        + * This interface is implemented by models providing J2-squared + * second-order terms in equinoctial elements. These terms are + * used in the computation of the closed-form J2-squared perturbation + * in semi-analytical satellite theory. + *

        + * @see ZeisModel + * @author Bryan Cazabonne + * @since 12.0 + */ +public interface J2SquaredModel { + + /** + * Compute the J2-squared second-order terms in equinoctial elements. + * @param context model context + * @return the J2-squared second-order terms in equinoctial elements. + * Order must follow: [A, K, H, Q, P, M] + */ + double[] computeMeanEquinoctialSecondOrderTerms(DSSTJ2SquaredClosedFormContext context); + + /** + * Compute the J2-squared second-order terms in equinoctial elements. + * @param context model context + * @param type of the elements + * @return the J2-squared second-order terms in equinoctial elements. + * Order must follow: [A, K, H, Q, P, M] + */ + > T[] computeMeanEquinoctialSecondOrderTerms(FieldDSSTJ2SquaredClosedFormContext context); + +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ShortPeriodTerms.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ShortPeriodTerms.java index e6703d06fe..1dab7e8afb 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ShortPeriodTerms.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ShortPeriodTerms.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ZeisModel.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ZeisModel.java new file mode 100644 index 0000000000..d8fd6297f5 --- /dev/null +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/ZeisModel.java @@ -0,0 +1,151 @@ +/* Copyright 2022 Bryan Cazabonne + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * Bryan Cazabonne licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.propagation.semianalytical.dsst.forces; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.MathArrays; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; + +/** + * Zeis model for J2-squared second-order terms. + * + * @see "ZEIS, Eric and CEFOLA, P. Computerized algebraic utilities for the + * construction of nonsingular satellite theories. Journal of Guidance and + * Control, 1980, vol. 3, no 1, p. 48-54." + * + * @see "SAN-JUAN, Juan F., LÓPEZ, Rosario, et CEFOLA, Paul J. A Second-Order + * Closed-Form $$ J_2 $$ Model for the Draper Semi-Analytical Satellite + * Theory. The Journal of the Astronautical Sciences, 2022, p. 1-27." + * + * @author Bryan Cazabonne + * @since 12.0 + */ +public class ZeisModel implements J2SquaredModel { + + /** + * Retrograde factor I. + *

        + * DSST model needs equinoctial orbit as internal representation. Classical + * equinoctial elements have discontinuities when inclination is close to zero. + * In this representation, I = +1.
        + * To avoid this discontinuity, another representation exists and equinoctial + * elements can be expressed in a different way, called "retrograde" orbit. This + * implies I = -1.
        + * As Orekit doesn't implement the retrograde orbit, I is always set to +1. But + * for the sake of consistency with the theory, the retrograde factor has been + * kept in the formulas. + *

        + */ + private static final int I = 1; + + /** Constructor. */ + public ZeisModel() { + // Nothing to do... + } + + /** {@inheritDoc}. */ + @Override + public double[] computeMeanEquinoctialSecondOrderTerms(final DSSTJ2SquaredClosedFormContext context) { + + // Auxiliary elements + final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements(); + + // Zeis constant + final double c2z = computeC2Z(context); + + // Useful terms + final double s2mf = 19.0 * context.getS2() - 15.0; + final double s2pIcmo = context.getS2() + I * context.getC() - 1.0; + final double s4mts2 = 19.0 * context.getS2() * context.getS2() - 30.0 * context.getS2() + 12.0; + + // Second-order terms (Ref [2] Eq. 37) + final double deltaA = 0.0; + final double deltaK = -c2z * auxiliaryElements.getH() * s2mf * s2pIcmo; + final double deltaH = c2z * auxiliaryElements.getK() * s2mf * s2pIcmo; + final double deltaQ = -c2z * context.getC() * auxiliaryElements.getP() * s2mf; + final double deltaP = c2z * context.getC() * auxiliaryElements.getQ() * s2mf; + final double deltaM = 0.5 * c2z * (2.0 * s2mf * s2pIcmo + 5.0 * s4mts2 * context.getEta()); + + // Return + return new double[] { deltaA, deltaK, deltaH, deltaQ, deltaP, deltaM }; + + } + + /** {@inheritDoc}. */ + @Override + public > T[] computeMeanEquinoctialSecondOrderTerms(final FieldDSSTJ2SquaredClosedFormContext context) { + + // Auxiliary elements + final FieldAuxiliaryElements auxiliaryElements = context.getFieldAuxiliaryElements(); + + // Field + final Field field = auxiliaryElements.getDate().getField(); + + // Zeis constant + final T c2z = computeC2Z(context); + + // Useful terms + final T s2mf = context.getS2().multiply(19.0).subtract(15.0); + final T s2pIcmo = context.getS2().add(context.getC().multiply(I)).subtract(1.0); + final T s4mts2 = context.getS2().multiply(context.getS2()).multiply(19.0).subtract(context.getS2().multiply(30.0)).add(12.0); + + // Second-order terms (Ref [2] Eq. 37) + final T deltaA = field.getZero(); + final T deltaK = c2z.multiply(auxiliaryElements.getH()).multiply(s2mf).multiply(s2pIcmo).negate(); + final T deltaH = c2z.multiply(auxiliaryElements.getK()).multiply(s2mf).multiply(s2pIcmo); + final T deltaQ = c2z.multiply(context.getC()).multiply(auxiliaryElements.getP()).multiply(s2mf).negate(); + final T deltaP = c2z.multiply(context.getC()).multiply(auxiliaryElements.getQ()).multiply(s2mf); + final T deltaM = c2z.multiply(0.5).multiply(s2mf.multiply(s2pIcmo).multiply(2.0).add(s4mts2.multiply(context.getEta()).multiply(5.0))); + + // Return + final T[] terms = MathArrays.buildArray(field, 6); + terms[0] = deltaA; + terms[1] = deltaK; + terms[2] = deltaH; + terms[3] = deltaQ; + terms[4] = deltaP; + terms[5] = deltaM; + return terms; + + } + + /** + * Get the value of the Zeis constant. + * + * @param context model context + * @return the value of the Zeis constant + */ + public double computeC2Z(final DSSTJ2SquaredClosedFormContext context) { + final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements(); + return 0.75 * context.getAlpha4() * auxiliaryElements.getMeanMotion() / (context.getA4() * context.getEta()); + } + + /** + * Get the value of the Zeis constant. + * + * @param context model context + * @param type of the elements + * @return the value of the Zeis constant + */ + public > T computeC2Z(final FieldDSSTJ2SquaredClosedFormContext context) { + final FieldAuxiliaryElements auxiliaryElements = context.getFieldAuxiliaryElements(); + return auxiliaryElements.getMeanMotion().multiply(context.getAlpha4()).multiply(0.75).divide(context.getA4().multiply(context.getEta())); + } + +} diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/package-info.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/package-info.java index dd329d7d32..befd6b095f 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/package-info.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/package-info.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/package-info.java index faef7f8c36..89ba444ab0 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/package-info.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java index ae7c9a9aa6..a053cc0e7d 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/AuxiliaryElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -86,6 +86,9 @@ public class AuxiliaryElements { */ private final int I; + /** Orbit. */ + private Orbit orbit; + /** B = sqrt(1 - h² - k²). */ private final double B; @@ -116,6 +119,9 @@ public class AuxiliaryElements { */ public AuxiliaryElements(final Orbit orbit, final int retrogradeFactor) { + // Orbit + this.orbit = orbit; + // Date of the orbit date = orbit.getDate(); @@ -168,6 +174,13 @@ public AuxiliaryElements(final Orbit orbit, final int retrogradeFactor) { gamma = w.getZ(); } + /** Get the orbit. + * @return the orbit + */ + public Orbit getOrbit() { + return orbit; + } + /** Get the date of the orbit. * @return the date */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java index 49aed4967d..b78b96d4a4 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java index 5da053daf8..47b2ab74a9 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,11 +17,10 @@ package org.orekit.propagation.semianalytical.dsst.utilities; import java.util.SortedMap; -import java.util.TreeMap; import java.util.concurrent.ConcurrentSkipListMap; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.CombinatoricsUtils; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -204,23 +203,12 @@ public static > T[][] computeGsHs(final T k, f return GsHs; } - /** Compute the Vn,s coefficients from 2.8.2-(1)(2). - * @param order Order of the computation. Computation will be done from 0 to order -1 - * @return Map of the Vn, s coefficients - * @deprecated as of 11.3.3, replaced by {@link #computeVnsCoefficients(int)} - */ - @Deprecated - public static TreeMap computeVns(final int order) { - return new TreeMap<>(computeVnsCoefficients(order)); - } - /** Compute the Vn,s coefficients from 2.8.2-(1)(2). * @param order Order of the computation. Computation will be done from 0 to order -1 * @return Map of the Vn, s coefficients * @since 11.3.3 */ - // TODO rename to computeVns in 12.0? - public static SortedMap computeVnsCoefficients(final int order) { + public static SortedMap computeVns(final int order) { if (order > LAST_VNS_ORDER) { // Compute coefficient @@ -266,7 +254,7 @@ public static double getVmns(final int m, final int n, final int s) { if ((n - s) % 2 == 0) { // Update the Vns coefficient if ((n + 1) > LAST_VNS_ORDER) { - computeVnsCoefficients(n + 1); + computeVns(n + 1); } if (s >= 0) { result = fns * VNS.get(new NSKey(n, s)) / fnm; diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java index cb8f2bba2a..5090454c71 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldAuxiliaryElements.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,8 @@ *

        * Most of them are defined in Danielson paper at § 2.1. *

        + * @author Bryan Cazabonne + * @param type of the field elements */ public class FieldAuxiliaryElements> { @@ -175,6 +177,13 @@ public FieldAuxiliaryElements(final FieldOrbit orbit, final int retrogradeFac gamma = (T) w.getZ(); } + /** Get the orbit. + * @return the orbit + */ + public FieldOrbit getOrbit() { + return orbit; + } + /** Get the date of the orbit. * @return the date */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java index 9e5e6c9192..fa51c58df4 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -35,6 +35,7 @@ * The Cj(k, h) and the Sj(k, h) elements are store as an * {@link ArrayList} of {@link Complex} number, the Cj(k, h) being * represented by the real and the Sj(k, h) by the imaginary part. + * @param type of the field elements */ public class FieldCjSjCoefficient > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldFixedNumberInterpolationGrid.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldFixedNumberInterpolationGrid.java index cdec709bb9..66de50b4d9 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldFixedNumberInterpolationGrid.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldFixedNumberInterpolationGrid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,6 +27,7 @@ * meaning that for short steps, the grid will be dense, * while for long steps the points will be far away one from each other *

        + * @param type of the field elements */ public class FieldFixedNumberInterpolationGrid> implements FieldInterpolationGrid { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHIJjsPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHIJjsPolynomials.java index a66893fa2a..ae204b3155 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHIJjsPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHIJjsPolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,6 +27,7 @@ *

        * @author Lucian Barbulescu * @author Bryan Cazabonne (field translation) + * @param type of the field elements */ public class FieldGHIJjsPolynomials> { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java index 510d39ea52..0e39114fc1 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -28,6 +28,7 @@ *

        * @author Romain Di Costanzo * @author Bryan Cazabonne (field translation) + * @param type of the field elements */ public class FieldGHmsjPolynomials > { /** Cj(k, h), Sj(k, h) coefficient. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java index 6842ba8042..4fd51abbbc 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,7 +24,9 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; -/** Compute the Γmn,s(γ) function from equation 2.7.1-(13). */ +/** Compute the Γmn,s(γ) function from equation 2.7.1-(13). + * @param type of the field elements + */ public class FieldGammaMnsFunction > { /** Factorial ratios. */ diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldInterpolationGrid.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldInterpolationGrid.java index 91af9a501f..9cd5437ac1 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldInterpolationGrid.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldInterpolationGrid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,11 +22,13 @@ *

        * An interpolation grid provides a grid of time points * that can be used for interpolation processes. - *

        + *

        + *

        * In the context of DSST propagation, an interpolation grid is used for the * computation through interpolation of short periodics coefficients - *

        + *

        * @author Nicolas Bernard + * @param type of the field elements */ public interface FieldInterpolationGrid> { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java index bfedf525c2..1bc3730044 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,7 @@ * Lns(γ) = ( R / a )nVnsQns(γ) *

        * @author Lucian Barbulescu + * @param type of the field elements */ public class FieldLnsCoefficients > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldMaxGapInterpolationGrid.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldMaxGapInterpolationGrid.java index 49ede3c787..7199e07306 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldMaxGapInterpolationGrid.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldMaxGapInterpolationGrid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,6 +29,7 @@ * * @author Luc Maisonobe * @since 7.1 + * @param type of the field elements */ public class FieldMaxGapInterpolationGrid > implements FieldInterpolationGrid { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldShortPeriodicsInterpolatedCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldShortPeriodicsInterpolatedCoefficient.java index 1c86daa53b..cc0137ce22 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldShortPeriodicsInterpolatedCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldShortPeriodicsInterpolatedCoefficient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -34,7 +34,7 @@ * {@link #value}. *

        * @author Nicolas Bernard - * + * @param type of the field elements */ public class FieldShortPeriodicsInterpolatedCoefficient > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FixedNumberInterpolationGrid.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FixedNumberInterpolationGrid.java index ad933ddc99..da3f314e82 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FixedNumberInterpolationGrid.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FixedNumberInterpolationGrid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHIJjsPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHIJjsPolynomials.java index 0fbd339472..6dcbf341a9 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHIJjsPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHIJjsPolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHmsjPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHmsjPolynomials.java index 60303cb7a2..2c2251306e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHmsjPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GHmsjPolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java index 157b586231..8292e5b55e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/InterpolationGrid.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/InterpolationGrid.java index d11f1e6a07..0486970356 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/InterpolationGrid.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/InterpolationGrid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,10 +20,11 @@ *

        * An interpolation grid provides a grid of time points * that can be used for interpolation processes. - *

        + *

        + *

        * In the context of DSST propagation, an interpolation grid is used for the * computation through interpolation of short periodics coefficients - *

        + *

        * @author Nicolas Bernard */ public interface InterpolationGrid { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java index 3eb0a80868..4764224145 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/LnsCoefficients.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/LnsCoefficients.java index 835da16819..223d5ea132 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/LnsCoefficients.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/LnsCoefficients.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/MaxGapInterpolationGrid.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/MaxGapInterpolationGrid.java index 12391e6d89..056ef036f7 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/MaxGapInterpolationGrid.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/MaxGapInterpolationGrid.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java index 11b4776101..20b5eb53ce 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java index f6687ede10..2f5bb84c6a 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java index 0ac7fd1bf5..06649e40ab 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java index f5842fa5e0..1311e537fa 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenTesseralLinear.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -37,6 +37,7 @@ * @author Petre Bazavan * @author Lucian Barbulescu * @author Bryan Cazabonne + * @param type of the field elements */ public class FieldHansenTesseralLinear > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java index b3ad1a38e2..f24aeafcaf 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,6 +33,7 @@ * @author Petre Bazavan * @author Lucian Barbulescu * @author Bryan Cazabonne + * @param type of the field elements */ public class FieldHansenThirdBodyLinear > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java index c5e421f642..b3d7025e4b 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -33,6 +33,7 @@ * @author Petre Bazavan * @author Lucian Barbulescu * @author Bryan Cazabonne + * @param type of the field elements */ public class FieldHansenZonalLinear > { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java index 944f504d0e..4533ec25f7 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java index 47612aeb53..3e95b120df 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java index 3a0c4048e8..c42de94bf4 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java index ef2fbddb9f..6e6eccd7b8 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java index 3c3957d87e..f88e64741e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/package-info.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/package-info.java index cb6e79ce4b..3eb086f30b 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/package-info.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/package-info.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/package-info.java index b36a732fd1..7c630a5691 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/package-info.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/propagation/semianalytical/package-info.java b/src/main/java/org/orekit/propagation/semianalytical/package-info.java index 1ead91fc24..3a3fa37c6c 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/package-info.java +++ b/src/main/java/org/orekit/propagation/semianalytical/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractAlfriend1999.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractAlfriend1999.java new file mode 100644 index 0000000000..8380b49587 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractAlfriend1999.java @@ -0,0 +1,149 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.linear.BlockFieldMatrix; +import org.hipparchus.linear.BlockRealMatrix; +import org.hipparchus.linear.FieldLUDecomposition; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.linear.LUDecomposition; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.util.MathArrays; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; + +/** + * Abstract class for Alfriend1999 normal and maximised methods as they share lots of similarities. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public abstract class AbstractAlfriend1999 extends AbstractShortTermEncounter2DPOCMethod { + + /** + * Default constructor. + * + * @param name name of the method + */ + protected AbstractAlfriend1999(final String name) { + super(name); + } + + /** {@inheritDoc} */ + @Override + public ProbabilityOfCollision compute(final double xm, final double ym, final double sigmaX, final double sigmaY, + final double radius) { + // Reconstruct necessary values from inputs + final double squaredMahalanobisDistance = + ShortTermEncounter2DDefinition.computeSquaredMahalanobisDistance(xm, ym, sigmaX, sigmaY); + + // Compute covariance matrix determinant + final double covarianceMatrixDeterminant = computeCovarianceMatrixDeterminant(sigmaX, sigmaY); + + // Compute probability of collision + final double value = computeValue(radius, squaredMahalanobisDistance, covarianceMatrixDeterminant); + + return new ProbabilityOfCollision(value, getName(), this.isAMaximumProbabilityOfCollisionMethod()); + } + + /** {@inheritDoc} */ + @Override + public > FieldProbabilityOfCollision compute(final T xm, final T ym, final T sigmaX, + final T sigmaY, + final T radius) { + // Reconstruct necessary values from inputs + final T squaredMahalanobisDistance = + FieldShortTermEncounter2DDefinition.computeSquaredMahalanobisDistance(xm, ym, sigmaX, sigmaY); + + // Compute covariance matrix determinant + final T covarianceMatrixDeterminant = computeCovarianceMatrixDeterminant(sigmaX, sigmaY); + + // Compute probability of collision + final T value = computeValue(radius, squaredMahalanobisDistance, covarianceMatrixDeterminant); + + return new FieldProbabilityOfCollision<>(value, getName(), this.isAMaximumProbabilityOfCollisionMethod()); + } + + /** + * Compute the covariance matrix determinant. + * + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * + * @return covariance matrix determinant + */ + private double computeCovarianceMatrixDeterminant(final double sigmaX, final double sigmaY) { + // Rebuild covariance matrix + final RealMatrix covarianceMatrix = new BlockRealMatrix(new double[][] { + { sigmaX * sigmaX, 0 }, + { 0, sigmaY * sigmaY } + }); + + // Compute determinant + return new LUDecomposition(covarianceMatrix).getDeterminant(); + } + + /** + * Compute the covariance matrix determinant. + * + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param type of the field elements + * + * @return covariance matrix determinant + */ + private > T computeCovarianceMatrixDeterminant(final T sigmaX, final T sigmaY) { + // Rebuild covariance matrix + final T[][] covarianceMatrixData = MathArrays.buildArray(sigmaX.getField(), 2, 2); + covarianceMatrixData[0][0] = sigmaX.multiply(sigmaX); + covarianceMatrixData[1][1] = sigmaY.multiply(sigmaY); + + final FieldMatrix covarianceMatrix = new BlockFieldMatrix<>(covarianceMatrixData); + + // Compute determinant + return new FieldLUDecomposition<>(covarianceMatrix).getDeterminant(); + } + + /** + * Compute the value of the probability of collision. + * + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param squaredMahalanobisDistance squared Mahalanobis distance + * @param covarianceMatrixDeterminant covariance matrix determinant + * + * @return value of the probability of collision + */ + abstract double computeValue(double radius, double squaredMahalanobisDistance, double covarianceMatrixDeterminant); + + /** + * Compute the value of the probability of collision. + * + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param squaredMahalanobisDistance squared Mahalanobis distance + * @param covarianceMatrixDeterminant covariance matrix determinant + * @param type of the field elements + * + * @return value of the probability of collision + */ + abstract > T computeValue(T radius, T squaredMahalanobisDistance, + T covarianceMatrixDeterminant); +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java new file mode 100644 index 0000000000..a00f25c1ef --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java @@ -0,0 +1,391 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.integration.FieldTrapezoidIntegrator; +import org.hipparchus.analysis.integration.FieldUnivariateIntegrator; +import org.hipparchus.analysis.integration.UnivariateIntegrator; +import org.hipparchus.geometry.euclidean.twod.FieldVector2D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.util.FastMath; +import org.orekit.data.DataContext; +import org.orekit.files.ccsds.ndm.cdm.Cdm; +import org.orekit.files.ccsds.ndm.cdm.CdmData; +import org.orekit.files.ccsds.ndm.cdm.CdmMetadata; +import org.orekit.files.ccsds.ndm.cdm.CdmRelativeMetadata; +import org.orekit.frames.encounter.EncounterLOFType; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldStateCovariance; +import org.orekit.propagation.StateCovariance; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; +import org.orekit.utils.Fieldifier; + +/** + * This abstract class serves as a foundation to create 1D numerical 2D probability of collision computing method. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public abstract class AbstractShortTermEncounter1DNumerical2DPOCMethod + extends AbstractShortTermEncounter2DPOCMethod { + + /** Default univariate function numerical integrator. */ + private final UnivariateIntegrator integrator; + + /** Default maximum number of function evaluation when integrating. */ + private final int maxNbOfEval; + + /** + * Customizable constructor. + * + * @param name name of the method + * @param integrator integrator + * @param maxNbOfEval max number of evaluation + */ + protected AbstractShortTermEncounter1DNumerical2DPOCMethod(final String name, + final UnivariateIntegrator integrator, + final int maxNbOfEval) { + super(name); + + this.integrator = integrator; + this.maxNbOfEval = maxNbOfEval; + } + + /** + * Compute the probability of collision using an {@link Cdm Orekit Conjunction Data Message}. + * + * @param cdm conjunction data message input + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * @param customIntegrator different univariate function numerical integrator than the one defined in the instance + * @param customMaxNbOfEval different maximum number of function evaluation when integrating than the one defined in the + * instance + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return probability of collision + */ + public ProbabilityOfCollision compute(final Cdm cdm, final double primaryRadius, final double secondaryRadius, + final UnivariateIntegrator customIntegrator, final int customMaxNbOfEval, + final double zeroThreshold) { + + final CdmRelativeMetadata cdmRelativeMetadata = cdm.getRelativeMetadata(); + final CdmData primaryData = cdm.getDataObject1(); + final CdmData secondaryData = cdm.getDataObject2(); + final DataContext cdmDataContext = cdm.getDataContext(); + + // Extract primary data + final Orbit primaryOrbit = + getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, cdm.getMetadataObject1(), cdmDataContext); + final StateCovariance primaryCovariance = getObjectStateCovarianceFromCdm(cdmRelativeMetadata, primaryData); + + // Extract secondary data + final Orbit secondaryOrbit = + getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, cdm.getMetadataObject2(), cdmDataContext); + final StateCovariance secondaryCovariance = getObjectStateCovarianceFromCdm(cdmRelativeMetadata, secondaryData); + + return compute(primaryOrbit, primaryCovariance, primaryRadius, + secondaryOrbit, secondaryCovariance, secondaryRadius, + customIntegrator, customMaxNbOfEval, zeroThreshold); + } + + /** + * Compute the probability of collision using an {@link Cdm Orekit Conjunction Data Message}. + * + * @param cdm conjunction data message + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * @param zeroThreshold threshold below which values are considered equal to zero + * @param customIntegrator different univariate function numerical integrator than the one defined in the instance + * @param customMaxNbOfEval different maximum number of function evaluation when integrating than the one defined in the + * instance + * @param type of the field elements + * + * @return probability of collision + */ + public > FieldProbabilityOfCollision compute(final Cdm cdm, + final T primaryRadius, + final T secondaryRadius, + final FieldUnivariateIntegrator customIntegrator, + final int customMaxNbOfEval, + final double zeroThreshold) { + + final Field field = primaryRadius.getField(); + final CdmRelativeMetadata cdmRelativeMetadata = cdm.getRelativeMetadata(); + final CdmData primaryData = cdm.getDataObject1(); + final CdmData secondaryData = cdm.getDataObject2(); + final CdmMetadata primaryMetadata = cdm.getMetadataObject1(); + final CdmMetadata secondaryMetadata = cdm.getMetadataObject2(); + final DataContext cdmDataContext = cdm.getDataContext(); + + // Extract primary data + final FieldOrbit primaryOrbit = + Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, + primaryMetadata, cdmDataContext)); + final FieldStateCovariance primaryCovariance = + Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, primaryData)); + + // Extract secondary data + final FieldOrbit secondaryOrbit = + Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, + secondaryMetadata, cdmDataContext)); + final FieldStateCovariance secondaryCovariance = + Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, secondaryData)); + + return compute(primaryOrbit, primaryCovariance, primaryRadius, + secondaryOrbit, secondaryCovariance, secondaryRadius, + customIntegrator, customMaxNbOfEval, zeroThreshold); + } + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * @param customIntegrator different univariate function numerical integrator than the one defined in the instance + * @param customMaxNbOfEval different maximum number of function evaluation when integrating than the one defined in the + * instance + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return probability of collision + */ + public ProbabilityOfCollision compute(final Orbit primaryAtTCA, + final StateCovariance primaryCovariance, + final double primaryRadius, + final Orbit secondaryAtTCA, + final StateCovariance secondaryCovariance, + final double secondaryRadius, + final UnivariateIntegrator customIntegrator, + final int customMaxNbOfEval, + final double zeroThreshold) { + + final ShortTermEncounter2DDefinition encounterDefinition = new ShortTermEncounter2DDefinition( + primaryAtTCA, primaryCovariance, primaryRadius, + secondaryAtTCA, secondaryCovariance, secondaryRadius, + EncounterLOFType.DEFAULT, DEFAULT_TCA_DIFFERENCE_TOLERANCE); + + return compute(encounterDefinition, customIntegrator, customMaxNbOfEval, zeroThreshold); + } + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * @param customIntegrator different univariate function numerical integrator than the one defined in the instance + * @param customMaxNbOfEval different maximum number of function evaluation when integrating than the one defined in the + * instance + * @param zeroThreshold threshold below which values are considered equal to zero + * @param type of the field elements + * + * @return probability of collision + */ + public > FieldProbabilityOfCollision compute( + final FieldOrbit primaryAtTCA, + final FieldStateCovariance primaryCovariance, + final T primaryRadius, + final FieldOrbit secondaryAtTCA, + final FieldStateCovariance secondaryCovariance, + final T secondaryRadius, + final FieldUnivariateIntegrator customIntegrator, + final int customMaxNbOfEval, + final double zeroThreshold) { + + final FieldShortTermEncounter2DDefinition encounterDefinition = + new FieldShortTermEncounter2DDefinition<>( + primaryAtTCA, primaryCovariance, primaryRadius, + secondaryAtTCA, secondaryCovariance, secondaryRadius, + EncounterLOFType.DEFAULT, DEFAULT_TCA_DIFFERENCE_TOLERANCE); + + return compute(encounterDefinition, customIntegrator, customMaxNbOfEval, zeroThreshold); + } + + /** + * Compute the probability of collision using a given collision definition. + * + * @param encounterDefinition probabilityOfCollision definition between a primary and a secondary collision object + * @param customIntegrator different univariate function numerical integrator than the one defined in the instance + * @param customMaxNbOfEval different maximum number of function evaluation when integrating than the one defined in the + * instance + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return probability of collision + */ + public ProbabilityOfCollision compute(final ShortTermEncounter2DDefinition encounterDefinition, + final UnivariateIntegrator customIntegrator, + final int customMaxNbOfEval, + final double zeroThreshold) { + + final Vector2D otherPositionAfterRotationInCollisionPlane = + encounterDefinition.computeOtherPositionInRotatedCollisionPlane(zeroThreshold); + + final RealMatrix projectedDiagonalizedCombinedPositionalCovarianceMatrix = + encounterDefinition.computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix(); + + return compute(otherPositionAfterRotationInCollisionPlane.getX(), + otherPositionAfterRotationInCollisionPlane.getY(), + FastMath.sqrt(projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(0, 0)), + FastMath.sqrt(projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(1, 1)), + encounterDefinition.getCombinedRadius(), + customIntegrator, + customMaxNbOfEval); + } + + /** + * Compute the probability of collision using given collision definition. + * + * @param encounterDefinition encounter definition between a primary and a secondary collision object + * @param customIntegrator custom integrator to use in place of the integrator from the constructor + * @param customMaxNbOfEval custom maximum number of evaluations to use in place of the custom maximum number from the + * @param zeroThreshold threshold below which values are considered equal to zero + * @param type of the field element + * + * @return probability of collision + */ + public > FieldProbabilityOfCollision compute( + final FieldShortTermEncounter2DDefinition encounterDefinition, + final FieldUnivariateIntegrator customIntegrator, + final int customMaxNbOfEval, + final double zeroThreshold) { + + final FieldVector2D otherPositionAfterRotationInCollisionPlane = + encounterDefinition.computeOtherPositionInRotatedCollisionPlane(zeroThreshold); + + final FieldMatrix projectedDiagonalizedCombinedPositionalCovarianceMatrix = + encounterDefinition.computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix(); + + return compute(otherPositionAfterRotationInCollisionPlane.getX(), + otherPositionAfterRotationInCollisionPlane.getY(), + projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(0, 0).sqrt(), + projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(1, 1).sqrt(), + encounterDefinition.getCombinedRadius(), + customIntegrator, + customMaxNbOfEval); + } + + /** + * {@inheritDoc} + *

        + * It uses the defaults integrator and maximum number of function evaluation when integrating. + */ + public ProbabilityOfCollision compute(final double xm, final double ym, final double sigmaX, final double sigmaY, + final double radius) { + return compute(xm, ym, sigmaX, sigmaY, radius, integrator, maxNbOfEval); + } + + /** + * Compute the probability of collision using arguments specific to the rotated encounter frame. + *

        + * The rotated encounter frame is define by the initial encounter frame (defined in + * {@link ShortTermEncounter2DDefinition}) rotated by the rotation matrix which is used to diagonalize the combined + * covariance matrix. + *

        + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param type of the field elements + * + * @return probability of collision + */ + public > FieldProbabilityOfCollision compute(final T xm, final T ym, + final T sigmaX, final T sigmaY, + final T radius) { + return compute(xm, ym, sigmaX, sigmaY, radius, new FieldTrapezoidIntegrator<>(xm.getField()), + maxNbOfEval); + } + + /** + * Compute the probability of collision using arguments specific to the rotated encounter frame and custom numerical + * configuration. + *

        + * The rotated encounter frame is define by the initial encounter frame (defined in + * {@link ShortTermEncounter2DDefinition}) rotated by the rotation matrix which is used to diagonalize the combined + * covariance matrix. + *

        + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param customIntegrator custom integrator to use in place of the integrator from the constructor + * @param customMaxNbOfEval custom maximum number of evaluations to use in place of the custom maximum number from the + * constructor + * + * @return probability of collision + */ + public abstract ProbabilityOfCollision compute(double xm, double ym, double sigmaX, double sigmaY, double radius, + UnivariateIntegrator customIntegrator, int customMaxNbOfEval); + + /** + * Compute the probability of collision using arguments specific to the rotated encounter frame and custom numerical + * configuration. + *

        + * The rotated encounter frame is define by the initial encounter frame (defined in + * {@link ShortTermEncounter2DDefinition}) rotated by the rotation matrix which is used to diagonalize the combined + * covariance matrix. + *

        + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param customIntegrator custom integrator to use in place of the integrator from the constructor + * @param customMaxNbOfEval custom maximum number of evaluations to use in place of the custom maximum number from the + * constructor + * @param type of the field element + * + * @return probability of collision + */ + public abstract > FieldProbabilityOfCollision compute(T xm, T ym, + T sigmaX, T sigmaY, + T radius, + FieldUnivariateIntegrator customIntegrator, + int customMaxNbOfEval); + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java new file mode 100644 index 0000000000..424138c4a8 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java @@ -0,0 +1,271 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.twod.FieldVector2D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.util.FastMath; +import org.orekit.data.DataContext; +import org.orekit.files.ccsds.ndm.cdm.Cdm; +import org.orekit.files.ccsds.ndm.cdm.CdmData; +import org.orekit.files.ccsds.ndm.cdm.CdmMetadata; +import org.orekit.files.ccsds.ndm.cdm.CdmRelativeMetadata; +import org.orekit.frames.Frame; +import org.orekit.frames.LOFType; +import org.orekit.frames.Transform; +import org.orekit.frames.encounter.EncounterLOFType; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldStateCovariance; +import org.orekit.propagation.StateCovariance; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Fieldifier; +import org.orekit.utils.PVCoordinates; + +/** + * This abstract class serves as a foundation to create 2D probability of collision computing method assuming a short term + * encounter model. + *

        + * All the methods extending this class will at least assume the followings : + *

          + *
        • Short term encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        + * As listed in the assumptions, methods extending this class are to be used in short encounter, + * meaning that there must be a high relative velocity. For ease of computation, the resulting swept volume + * is extended to infinity so that the integral becomes bivariate instead of trivariate (conservative hypothesis). + *

        + * Consequently and if we consider Earth, methods implementing this interface are recommended for + * collision happening in Low/Medium Earth Orbit (LEO and MEO) but are not recommended for collision + * happening in Geostationary Earth Orbit (GEO). + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public abstract class AbstractShortTermEncounter2DPOCMethod implements ShortTermEncounter2DPOCMethod { + + /** Default time of closest approach difference tolerance. */ + public static final double DEFAULT_TCA_DIFFERENCE_TOLERANCE = 1e-6; + + /** Name of the method. */ + private final String name; + + /** + * Constructor. + * + * @param name name of the method + */ + protected AbstractShortTermEncounter2DPOCMethod(final String name) { + this.name = name; + } + + /** {@inheritDoc} */ + public ProbabilityOfCollision compute(final Cdm cdm, final double combinedRadius) { + + final CdmRelativeMetadata cdmRelativeMetadata = cdm.getRelativeMetadata(); + final CdmData primaryData = cdm.getDataObject1(); + final CdmData secondaryData = cdm.getDataObject2(); + final DataContext cdmDataContext = cdm.getDataContext(); + + // Extract primary data + final Orbit primaryOrbit = getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, + cdm.getMetadataObject1(), cdmDataContext); + final StateCovariance primaryCovariance = getObjectStateCovarianceFromCdm(cdmRelativeMetadata, primaryData); + + // Extract secondary data + final Orbit secondaryOrbit = getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, + cdm.getMetadataObject2(), cdmDataContext); + final StateCovariance secondaryCovariance = getObjectStateCovarianceFromCdm(cdmRelativeMetadata, secondaryData); + + return compute(primaryOrbit, primaryCovariance, secondaryOrbit, secondaryCovariance, combinedRadius, + DEFAULT_ZERO_THRESHOLD); + } + + /** {@inheritDoc} */ + public > FieldProbabilityOfCollision compute(final Cdm cdm, + final T combinedRadius, + final double zeroThreshold) { + + final Field field = combinedRadius.getField(); + final CdmRelativeMetadata cdmRelativeMetadata = cdm.getRelativeMetadata(); + final CdmData primaryData = cdm.getDataObject1(); + final CdmData secondaryData = cdm.getDataObject2(); + final CdmMetadata primaryMetadata = cdm.getMetadataObject1(); + final CdmMetadata secondaryMetadata = cdm.getMetadataObject2(); + final DataContext cdmDataContext = cdm.getDataContext(); + + // Extract primary data + final FieldOrbit primaryOrbit = + Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, + primaryMetadata, cdmDataContext)); + final FieldStateCovariance primaryCovariance = + Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, primaryData)); + + // Extract secondary data + final FieldOrbit secondaryOrbit = + Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, + secondaryMetadata, cdmDataContext)); + final FieldStateCovariance secondaryCovariance = + Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, secondaryData)); + + return compute(primaryOrbit, primaryCovariance, secondaryOrbit, secondaryCovariance, combinedRadius, zeroThreshold); + } + + /** {@inheritDoc} */ + public ProbabilityOfCollision compute(final Orbit primaryAtTCA, + final StateCovariance primaryCovariance, + final Orbit secondaryAtTCA, + final StateCovariance secondaryCovariance, + final double combinedRadius, + final double zeroThreshold) { + + final ShortTermEncounter2DDefinition shortTermEncounter2DDefinition = new ShortTermEncounter2DDefinition( + primaryAtTCA, primaryCovariance, secondaryAtTCA, secondaryCovariance, + combinedRadius, EncounterLOFType.DEFAULT, DEFAULT_TCA_DIFFERENCE_TOLERANCE); + + return compute(shortTermEncounter2DDefinition, zeroThreshold); + } + + /** {@inheritDoc} */ + public > FieldProbabilityOfCollision compute( + final FieldOrbit primaryAtTCA, final FieldStateCovariance primaryCovariance, + final FieldOrbit secondaryAtTCA, final FieldStateCovariance secondaryCovariance, + final T combinedRadius, final double zeroThreshold) { + + final FieldShortTermEncounter2DDefinition FieldShortTermEncounter2DDefinition = + new FieldShortTermEncounter2DDefinition<>( + primaryAtTCA, primaryCovariance, secondaryAtTCA, secondaryCovariance, + combinedRadius, EncounterLOFType.DEFAULT, DEFAULT_TCA_DIFFERENCE_TOLERANCE); + + return compute(FieldShortTermEncounter2DDefinition, zeroThreshold); + } + + /** {@inheritDoc} */ + public ProbabilityOfCollision compute(final ShortTermEncounter2DDefinition encounter, + final double zeroThreshold) { + + final Vector2D otherPositionAfterRotationInCollisionPlane = + encounter.computeOtherPositionInRotatedCollisionPlane(zeroThreshold); + + final RealMatrix projectedDiagonalizedCombinedPositionalCovarianceMatrix = + encounter.computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix(); + + return compute(otherPositionAfterRotationInCollisionPlane.getX(), + otherPositionAfterRotationInCollisionPlane.getY(), + FastMath.sqrt(projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(0, 0)), + FastMath.sqrt(projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(1, 1)), + encounter.getCombinedRadius()); + } + + /** {@inheritDoc} */ + public > FieldProbabilityOfCollision compute( + final FieldShortTermEncounter2DDefinition encounter, final double zeroThreshold) { + + final FieldVector2D otherPositionAfterRotationInCollisionPlane = + encounter.computeOtherPositionInRotatedCollisionPlane(zeroThreshold); + + final FieldMatrix projectedDiagonalizedCombinedPositionalCovarianceMatrix = + encounter.computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix(); + + return compute(otherPositionAfterRotationInCollisionPlane.getX(), + otherPositionAfterRotationInCollisionPlane.getY(), + projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(0, 0).sqrt(), + projectedDiagonalizedCombinedPositionalCovarianceMatrix.getEntry(1, 1).sqrt(), + encounter.getCombinedRadius()); + } + + /** {@inheritDoc} */ + public abstract ProbabilityOfCollision compute(double xm, double ym, double sigmaX, double sigmaY, double radius); + + /** {@inheritDoc} */ + public abstract > FieldProbabilityOfCollision compute(T xm, T ym, + T sigmaX, T sigmaY, + T radius); + + /** {@inheritDoc} */ + @Override + public String getName() { + return name; + } + + /** {@inheritDoc} */ + @Override + public boolean isAMaximumProbabilityOfCollisionMethod() { + return false; + } + + /** + * Extract collision object spacecraft state from given {@link Cdm Conjunction Data Message} data. + * + * @param cdmRelativeMetadata conjunction data message relative metadata + * @param cdmData collision object conjunction data message data + * @param cdmMetadata collision object conjunction data message metadata + * @param cdmDataContext conjunction data message data context + * + * @return basic collision object spacecraft state from conjunction data message + */ + protected Orbit getObjectOrbitFromCdm(final CdmRelativeMetadata cdmRelativeMetadata, + final CdmData cdmData, + final CdmMetadata cdmMetadata, + final DataContext cdmDataContext) { + + // Extract orbit + final Frame frame = cdmMetadata.getRefFrame().asFrame(); + final AbsoluteDate tca = cdmRelativeMetadata.getTca(); + final PVCoordinates pvInFrame = new PVCoordinates(cdmData.getStateVectorBlock().getPositionVector(), + cdmData.getStateVectorBlock().getVelocityVector()); + final double mu = cdmMetadata.getOrbitCenter().getBody().getGM(); + + // Simple case where the reference frame is already pseudo-inertial + if (frame.isPseudoInertial()) { + return new CartesianOrbit(pvInFrame, frame, tca, mu); + } + // Otherwise, convert coordinates to default inertial frame + final Frame inertial = cdmDataContext.getFrames().getGCRF(); + final Transform toInertial = frame.getTransformTo(inertial, cdmRelativeMetadata.getTca()); + final PVCoordinates pvInInertial = toInertial.transformPVCoordinates(pvInFrame); + + return new CartesianOrbit(pvInInertial, inertial, tca, mu); + } + + /** + * Get collision object state covariance from given {@link Cdm Conjunction Data Message} data. + * + * @param cdmRelativeMetadata conjunction data message relative metadata + * @param cdmData collision object conjunction data message data + * + * @return collision object state covariance + */ + protected StateCovariance getObjectStateCovarianceFromCdm(final CdmRelativeMetadata cdmRelativeMetadata, + final CdmData cdmData) { + final AbsoluteDate tca = cdmRelativeMetadata.getTca(); + final RealMatrix rtnCovarianceMatrix = + cdmData.getRTNCovarianceBlock().getRTNCovarianceMatrix().getSubMatrix(0, 5, 0, 5); + return new StateCovariance(rtnCovarianceMatrix, tca, LOFType.QSW_INERTIAL); + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java new file mode 100644 index 0000000000..8940c5c4a3 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java @@ -0,0 +1,269 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.special.Erf; +import org.hipparchus.util.FastMath; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; + +/** + * Compute the probability of collision using the method described in :"S. Alfano. A numerical implementation of spherical + * objet collision probability. Journal of Astronautical Sciences, 53(1), January-March 2005." + *

        + * It assumes : + *

          + *
        • Short encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        + *

        + * Also, it has been implemented using a simpson integration scheme as explained in his paper. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class Alfano2005 extends AbstractShortTermEncounter2DPOCMethod { + + /** Empty constructor. */ + public Alfano2005() { + super(ShortTermEncounter2DPOCMethodType.ALFANO_2005.name()); + } + + /** {@inheritDoc} */ + public ProbabilityOfCollision compute(final double xm, final double ym, final double sigmaX, final double sigmaY, + final double radius) { + // Computing development order M + final int developmentOrderM = computeOrderM(xm, ym, sigmaX, sigmaY, radius); + + // Computing x step + final double xStep = radius / (2 * developmentOrderM); + + // Computing initial x0 + final double x0 = 0.015 * xStep - radius; + + // 1 : m0 + final double m0 = 2. * getRecurrentPart(x0, xm, ym, sigmaX, sigmaY, radius); + + // 2 : mEven + double mEvenSum = 0.; + for (int i = 1; i < developmentOrderM; i++) { + final double x2i = 2. * i * xStep - radius; + mEvenSum += getRecurrentPart(x2i, xm, ym, sigmaX, sigmaY, radius); + } + + final double otherTerm = FastMath.exp(-xm * xm / (2 * sigmaX * sigmaX)) * ( + Erf.erf((-ym + radius) / (FastMath.sqrt(2) * sigmaY)) - + Erf.erf((-ym - radius) / (FastMath.sqrt(2) * sigmaY))); + + final double mEven = 2. * (mEvenSum + otherTerm); + + // 3 : mOdd + double mOddSum = 0.; + for (int i = 1; i <= developmentOrderM; i++) { + final double x2i_1 = (2. * i - 1.) * xStep - radius; + mOddSum += getRecurrentPart(x2i_1, xm, ym, sigmaX, sigmaY, radius); + } + + final double mOdd = 4. * mOddSum; + + // Output + final double factor = xStep / (3. * sigmaX * FastMath.sqrt(8. * FastMath.PI)); + + final double value = factor * (m0 + mEven + mOdd); + + return new ProbabilityOfCollision(value, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + + /** {@inheritDoc} */ + @Override + public > FieldProbabilityOfCollision compute(final T xm, final T ym, + final T sigmaX, final T sigmaY, + final T radius) { + final T zero = xm.getField().getZero(); + + // Computing development order M + final int developmentOrderM = computeOrderM(xm, ym, sigmaX, sigmaY, radius); + + // Computing x step + final T xStep = radius.multiply(0.5 / developmentOrderM); + + // Computing initial x0 + final T x0 = xStep.multiply(0.015).subtract(radius); + + // 1 : m0 + final T m0 = getRecurrentPart(x0, xm, ym, sigmaX, sigmaY, radius).multiply(2.); + + // 2 : mEven + T mEvenSum = zero; + for (int i = 1; i < developmentOrderM; i++) { + final T x2i = xStep.multiply(2. * i).subtract(radius); + mEvenSum = mEvenSum.add(getRecurrentPart(x2i, xm, ym, sigmaX, sigmaY, radius)); + } + + final T rootTwoSigmaY = sigmaY.multiply(FastMath.sqrt(2)); + + final T otherTerm = xm.multiply(xm).divide(sigmaX.multiply(sigmaX).multiply(-2.)).exp() + .multiply(Erf.erf(radius.subtract(ym).divide(rootTwoSigmaY)) + .subtract(Erf.erf(radius.add(ym).negate().divide(rootTwoSigmaY)))); + + final T mEven = mEvenSum.add(otherTerm).multiply(2.); + + // 3 : mOdd + T mOddSum = zero; + for (int i = 1; i <= developmentOrderM; i++) { + final T x2i_1 = xStep.multiply(2. * i - 1).subtract(radius); + mOddSum = mOddSum.add(getRecurrentPart(x2i_1, xm, ym, sigmaX, sigmaY, radius)); + } + + final T mOdd = mOddSum.multiply(4.); + + // Output + final T factor = xStep.divide(sigmaX.multiply(3 * FastMath.sqrt(8. * FastMath.PI))); + + final T value = factor.multiply(m0.add(mEven).add(mOdd)); + + return new FieldProbabilityOfCollision<>(value, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + + /** + * Get the development order M from inputs. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * + * @return development order M + */ + private int computeOrderM(final double xm, final double ym, final double sigmaX, final double sigmaY, + final double radius) { + final int M = (int) (5 * radius / (FastMath.min(FastMath.min(sigmaX, sigmaY), + FastMath.min(sigmaX, FastMath.sqrt(xm * xm + ym * ym))))); + final int LOWER_LIMIT = 10; + final int UPPER_LIMIT = 100000; + + if (M >= UPPER_LIMIT) { + return UPPER_LIMIT; + } else if (M <= LOWER_LIMIT) { + return LOWER_LIMIT; + } else { + return M; + } + } + + /** + * Get the development order M from inputs. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param type of the field elements + * + * @return development order M + */ + private > int computeOrderM(final T xm, final T ym, final T sigmaX, final T sigmaY, + final T radius) { + + final double xmR = xm.getReal(); + final double ymR = ym.getReal(); + final int M = (int) (radius.getReal() * 5. / FastMath.min(FastMath.min(sigmaX.getReal(), sigmaY.getReal()), + FastMath.min(sigmaX.getReal(), + FastMath.sqrt(xmR * xmR + ymR * ymR)))); + + final int lowerLimit = 10; + final int upperLimit = 10000; + + if (M >= upperLimit) { + return upperLimit; + } else if (M <= lowerLimit) { + return lowerLimit; + } else { + return M; + } + } + + /** + * Get the recurrent equation from Alfano's method. + * + * @param x step + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param radius sum of primary and secondary collision object equivalent sphere radius (m) + * + * @return recurrent equation from Alfano's method + */ + private double getRecurrentPart(final double x, final double xm, final double ym, final double sigmaX, + final double sigmaY, final double radius) { + return (Erf.erf((-ym + FastMath.sqrt(radius * radius - x * x)) / (FastMath.sqrt(2) * sigmaY)) - + Erf.erf((-ym - FastMath.sqrt(radius * radius - x * x)) / (FastMath.sqrt(2) * sigmaY))) * + (FastMath.exp(-(x - xm) * (x - xm) / (2. * sigmaX * sigmaX)) + + FastMath.exp(-(x + xm) * (x + xm) / (2. * sigmaX * sigmaX))); + } + + /** + * Get the recurrent equation from Alfano's method. + * + * @param x step + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param radius sum of primary and secondary collision object equivalent sphere radius (m) + * @param type of the field elements + * + * @return recurrent equation from Alfano's method + */ + private > T getRecurrentPart(final T x, final T xm, final T ym, final T sigmaX, + final T sigmaY, final T radius) { + final T minusTwoSigmaXSquared = sigmaX.multiply(sigmaX).multiply(-2.); + final T radiusSquaredMinusXSquaredSQRT = radius.multiply(radius).subtract(x.multiply(x)).sqrt(); + final T rootTwoSigmaY = sigmaY.multiply(FastMath.sqrt(2)); + final T xMinusXm = x.subtract(xm); + final T xPlusXm = x.add(xm); + + return Erf.erf(radiusSquaredMinusXSquaredSQRT.subtract(ym).divide(rootTwoSigmaY)).subtract( + Erf.erf(radiusSquaredMinusXSquaredSQRT.add(ym).negate().divide(rootTwoSigmaY))) + .multiply(xMinusXm.multiply(xMinusXm).divide(minusTwoSigmaXSquared).exp() + .add(xPlusXm.multiply(xPlusXm).divide(minusTwoSigmaXSquared).exp())); + } + + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethodType getType() { + return ShortTermEncounter2DPOCMethodType.ALFANO_2005; + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java new file mode 100644 index 0000000000..a11ce6bbd3 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java @@ -0,0 +1,79 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; + +/** + * Compute the probability of collision using the method described in : "Kyle Alfriend, Maruthi Akella, Joseph Frisbee, James + * Foster, Deok-Jin Lee, and Matthew Wilkins. Probability of ProbabilityOfCollision Error Analysis. Space Debris, 1(1):21–35, + * 1999.". + *

        It assumes : + *

          + *
        • Short encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        • Both objects are in circular orbits (eq 14).
        • + *
        • Probability density function is constant over the collision disk (eq 18).
        • + *
        + *

        + * By assuming a constant probability density function over the collision circle this method will, + * most of the time, give much higher probability of collision than other regular methods. + * That is why it is qualified as a maximum probability of collision computing method.

        + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class Alfriend1999 extends AbstractAlfriend1999 { + + /** Empty constructor. */ + public Alfriend1999() { + super(ShortTermEncounter2DPOCMethodType.ALFRIEND_1999.name()); + } + + /** {@inheritDoc} */ + @Override + public boolean isAMaximumProbabilityOfCollisionMethod() { + return true; + } + + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethodType getType() { + return ShortTermEncounter2DPOCMethodType.ALFRIEND_1999; + } + + /** {@inheritDoc} */ + @Override + double computeValue(final double radius, final double squaredMahalanobisDistance, + final double covarianceMatrixDeterminant) { + return FastMath.exp(-0.5 * squaredMahalanobisDistance) * radius * radius / + (2 * FastMath.sqrt(covarianceMatrixDeterminant)); + } + + /** {@inheritDoc} */ + @Override + > T computeValue(final T radius, final T squaredMahalanobisDistance, + final T covarianceMatrixDeterminant) { + return squaredMahalanobisDistance.multiply(-0.5).exp().multiply(radius).multiply(radius) + .divide(covarianceMatrixDeterminant.sqrt().multiply(2.)); + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Max.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Max.java new file mode 100644 index 0000000000..74ce31bf88 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Max.java @@ -0,0 +1,96 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; + +/** + * Compute the probability of collision assuming the worst case described in : "Kyle Alfriend, Maruthi Akella, Joseph + * Frisbee, James Foster, Deok-Jin Lee, and Matthew Wilkins. Probability of ProbabilityOfCollision Error Analysis. Space + * Debris, 1(1):21–35, 1999.". + *

        It assumes: + *

          + *
        • Short encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        • Both objects are in circular orbits (eq 14).
        • + *
        • Probability density function is constant over the collision circle (eq 18).
        • + *
        • Covariance multiplied by a coefficient KSquared = MahalanobisDistanceSquared / 2 (eq 19-20).
        • + *
        + *

        + * By assuming a constant probability density function over the collision circle this method will, + * most of the time, give much higher probability of collision than other regular methods. + * That is why it is qualified as a maximum probability of collision computing method. + * + * @author Vincent Cucchietti + * @see Mahalanobis distance. + * @since 12.0 + */ +public class Alfriend1999Max extends AbstractAlfriend1999 { + + /** Empty constructor. */ + public Alfriend1999Max() { + super(ShortTermEncounter2DPOCMethodType.ALFRIEND_1999_MAX.name()); + } + + /** + * Compute the value of the probability of collision. + * + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param squaredMahalanobisDistance squared Mahalanobis distance + * @param covarianceMatrixDeterminant covariance matrix determinant + * + * @return value of the probability of collision + */ + @Override + public double computeValue(final double radius, final double squaredMahalanobisDistance, + final double covarianceMatrixDeterminant) { + return radius * radius / (squaredMahalanobisDistance * FastMath.sqrt(covarianceMatrixDeterminant) * FastMath.E); + } + + /** + * Compute the value of the probability of collision. + * + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param squaredMahalanobisDistance squared Mahalanobis distance + * @param covarianceMatrixDeterminant covariance matrix determinant + * @param type of the field elements + * + * @return value of the probability of collision + */ + @Override + public > T computeValue(final T radius, final T squaredMahalanobisDistance, + final T covarianceMatrixDeterminant) { + return radius.multiply(radius).divide(squaredMahalanobisDistance.multiply(covarianceMatrixDeterminant.sqrt()) + .multiply(FastMath.E)); + } + + /** {@inheritDoc} */ + @Override + public boolean isAMaximumProbabilityOfCollisionMethod() { + return true; + } + + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethodType getType() { + return ShortTermEncounter2DPOCMethodType.ALFRIEND_1999_MAX; + } +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997.java new file mode 100644 index 0000000000..b3e4602eac --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997.java @@ -0,0 +1,148 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; + +/** + * Compute the probability of collision using the method described in :
        "Chan, K. “Collision Probability Analyses for + * Earth Orbiting Satellites.” In Space Cooperation into the 21st Century: 7th AAS/JRS/CSA Symposium, International Space + * Conference of Pacific-Basin Societies (ISCOPS; formerly PISSTA) (July 15-18, 1997, Nagasaki, Japan), edited by Peter M. + * Bainum, et al., 1033-1048. Advances in the Astronautical Sciences Series 96. San Diego, California: Univelt, 1997. (Zeroth + * order analytical expression). + *

        + * This method is also described in depth in : "CHAN, F. Kenneth, et al. Spacecraft collision probability. El Segundo, CA : + * Aerospace Press, 2008." + *

        + * It assumes : + *

          + *
        • Short encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        • Approximate ellipse by a disk
        • + *
        + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class Chan1997 extends AbstractShortTermEncounter2DPOCMethod { + + /** Empty constructor. */ + public Chan1997() { + super(ShortTermEncounter2DPOCMethodType.CHAN_1997.name()); + } + + /** {@inheritDoc} */ + public ProbabilityOfCollision compute(final double xm, final double ym, + final double sigmaX, final double sigmaY, + final double radius) { + + // Intermediary terms u and v + final double u = radius * radius / (sigmaX * sigmaY); + final double v = (xm * xm / (sigmaX * sigmaX)) + (ym * ym / (sigmaY * sigmaY)); + + // Number of terms M recommended by Chan + final int M; + + if (u <= 0.01 || v <= 1) { + M = 3; + } else if (u > 0.01 && u <= 1 || v > 1 && v <= 9) { + M = 10; + } else if (u > 1 && u <= 25 || v > 9 && v <= 25) { + M = 20; + } else { + M = 60; + } + + double t = 1.0; + double s = 1.0; + double sum = 1.0; + + // first iteration + double value = FastMath.exp(-v * 0.5) * t - FastMath.exp(-(u + v) * 0.5) * t * sum; + + // iterative expression + for (int i = 1; i < M; i++) { + t = (v * 0.5) / i * t; + s = (u * 0.5) / i * s; + sum = sum + s; + value = MathArrays.linearCombination(1, value, FastMath.exp(-v * 0.5), t) - + FastMath.exp(-(u + v) * 0.5) * t * sum; + } + + return new ProbabilityOfCollision(value, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + + /** {@inheritDoc} */ + public > FieldProbabilityOfCollision compute(final T xm, final T ym, + final T sigmaX, final T sigmaY, + final T radius) { + + // Intermediary terms u and v + final T u = radius.pow(2).divide(sigmaX.multiply(sigmaY)); + final T v = xm.divide(sigmaX).pow(2).add(ym.divide(sigmaY).pow(2)); + + // Number of terms M recommended by Chan + final int M; + + if (u.getReal() <= 0.01 || v.getReal() <= 1) { + M = 3; + } else if (u.getReal() > 0.01 && u.getReal() <= 1 || v.getReal() > 1 && v.getReal() <= 9) { + M = 10; + } else if (u.getReal() > 1 && u.getReal() <= 25 || v.getReal() > 9 && v.getReal() <= 25) { + M = 20; + } else { + M = 60; + } + + final Field field = radius.getField(); + + T t = field.getOne(); + T s = field.getOne(); + T sum = field.getOne(); + + // first iteration + T value = v.multiply(-0.5).exp().multiply(t) + .subtract(u.add(v).multiply(-0.5).exp().multiply(t).multiply(sum)); + + // iterative expression + for (int i = 1; i < M; i++) { + t = v.multiply(0.5).divide(i).multiply(t); + s = u.multiply(0.5).divide(i).multiply(s); + sum = sum.add(s); + + value = value.add(v.multiply(-0.5).exp().multiply(t) + .subtract(u.add(v).multiply(-0.5).exp().multiply(t).multiply(sum))); + } + + return new FieldProbabilityOfCollision<>(value, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethodType getType() { + return ShortTermEncounter2DPOCMethodType.CHAN_1997; + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java new file mode 100644 index 0000000000..49e2f2e221 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java @@ -0,0 +1,745 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.twod.FieldVector2D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.hipparchus.linear.Array2DRowFieldMatrix; +import org.hipparchus.linear.BlockFieldMatrix; +import org.hipparchus.linear.FieldLUDecomposition; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.MathUtils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; +import org.orekit.frames.LOF; +import org.orekit.frames.LOFType; +import org.orekit.frames.encounter.EncounterLOF; +import org.orekit.frames.encounter.EncounterLOFType; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldStateCovariance; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; + +/** + * Defines the encounter between two collision object at time of closest approach assuming a short-term encounter model . It + * uses the given {@link EncounterLOFType encounter frame type} to define the encounter. + *

        + * Both the primary and secondary collision object can be at the reference of the encounter frame, it is up to the user to + * choose. + *

        + * The "reference" object is the object considered at the reference of the given encounter frame while the "other" object is + * the one not placed at the reference. + *

        + * For example, if the user wants the primary to be at the reference of the default encounter frame, they will have to input + * data in the following manner: + *

        {@code
        + * final FieldShortTermEncounter2DDefinition encounter = new FieldShortTermEncounter2DDefinition<>(primaryOrbitAtTCA, primaryCovarianceAtTCA, primaryRadius, secondaryOrbitAtTCA, secondaryCovarianceAtTCA, secondaryRadius);
        + *  }
        + * 
        + * However, if the user wants to put the secondary at the reference and use the + * {@link org.orekit.frames.encounter.ValsecchiEncounterFrame Valsecchi encounter frame}, they will have to type : + *
        {@code
        + * final FieldShortTermEncounter2DDefinition encounter = new FieldShortTermEncounter2DDefinition<>(secondaryOrbitAtTCA, secondaryCovarianceAtTCA, secondaryRadius, primaryOrbitAtTCA, primaryCovarianceAtTCA, primaryRadius, EncounterLOFType.VALSECCHI_2003);
        + *  }
        + * 
        + * Note that in the current implementation, the shape of the collision objects is assumed to be a sphere. + * + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class FieldShortTermEncounter2DDefinition> { + + /** Default threshold below which values are considered equal to zero. */ + private static final double DEFAULT_ZERO_THRESHOLD = 1e-15; + + /** Field to which the instance elements belong. */ + private final Field instanceField; + + /** + * Time of closest approach. + *

        + * Commonly called TCA. + */ + private final FieldAbsoluteDate tca; + + /** Reference collision object at time of closest approach. */ + private final FieldOrbit referenceAtTCA; + + /** Reference collision object covariance matrix in its respective RTN frame. */ + private final FieldStateCovariance referenceCovariance; + + /** Other collision object at time of closest approach. */ + private final FieldOrbit otherAtTCA; + + /** Other collision object covariance matrix in its respective RTN frame. */ + private final FieldStateCovariance otherCovariance; + + /** Combined radius (m). */ + private final T combinedRadius; + + /** Encounter local orbital frame to use. */ + private final EncounterLOF encounterFrame; + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param referenceRadius reference collision's equivalent sphere radius + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param otherRadius other collision's equivalent sphere radius + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public FieldShortTermEncounter2DDefinition(final FieldOrbit referenceAtTCA, + final FieldStateCovariance referenceCovariance, + final T referenceRadius, + final FieldOrbit otherAtTCA, + final FieldStateCovariance otherCovariance, + final T otherRadius) { + this(referenceAtTCA, referenceCovariance, otherAtTCA, otherCovariance, referenceRadius.add(otherRadius)); + } + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param combinedRadius combined radius (m) + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public FieldShortTermEncounter2DDefinition(final FieldOrbit referenceAtTCA, + final FieldStateCovariance referenceCovariance, + final FieldOrbit otherAtTCA, + final FieldStateCovariance otherCovariance, + final T combinedRadius) { + this(referenceAtTCA, referenceCovariance, otherAtTCA, otherCovariance, combinedRadius, + EncounterLOFType.DEFAULT, 1e-6); + } + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param referenceRadius reference collision's equivalent sphere radius + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param otherRadius other collision's equivalent sphere radius + * @param encounterFrameType type of encounter frame to use + * @param tcaTolerance tolerance on reference and other times of closest approach difference + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public FieldShortTermEncounter2DDefinition(final FieldOrbit referenceAtTCA, + final FieldStateCovariance referenceCovariance, + final T referenceRadius, + final FieldOrbit otherAtTCA, + final FieldStateCovariance otherCovariance, + final T otherRadius, + final EncounterLOFType encounterFrameType, + final double tcaTolerance) { + this(referenceAtTCA, referenceCovariance, otherAtTCA, otherCovariance, referenceRadius.add(otherRadius), + encounterFrameType, tcaTolerance); + } + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param combinedRadius combined radius (m) + * @param encounterFrameType type of encounter frame to use + * @param tcaTolerance tolerance on reference and other times of closest approach difference + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public FieldShortTermEncounter2DDefinition(final FieldOrbit referenceAtTCA, + final FieldStateCovariance referenceCovariance, + final FieldOrbit otherAtTCA, + final FieldStateCovariance otherCovariance, + final T combinedRadius, + final EncounterLOFType encounterFrameType, + final double tcaTolerance) { + + if (referenceAtTCA.getDate().isCloseTo(otherAtTCA.getDate(), tcaTolerance)) { + + this.tca = referenceAtTCA.getDate(); + this.instanceField = tca.getField(); + + this.referenceAtTCA = referenceAtTCA; + this.referenceCovariance = referenceCovariance; + + this.otherAtTCA = otherAtTCA; + this.otherCovariance = otherCovariance; + + this.combinedRadius = combinedRadius; + + this.encounterFrame = encounterFrameType.getFrame(otherAtTCA.getPVCoordinates()); + } else { + throw new OrekitException(OrekitMessages.DIFFERENT_TIME_OF_CLOSEST_APPROACH); + } + + } + + /** + * Compute the squared Mahalanobis distance. + * + * @param xm other collision object projected xm position onto the collision plane in the rotated encounter frame + * @param ym other collision object projected ym position onto the collision plane in the rotated encounter frame + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param type of the field elements + * + * @return squared Mahalanobis distance + */ + public static > T computeSquaredMahalanobisDistance(final T xm, final T ym, + final T sigmaX, final T sigmaY) { + + final T[][] positionData = MathArrays.buildArray(xm.getField(), 2, 1); + positionData[0][0] = xm; + positionData[1][0] = ym; + final FieldVector2D position = new FieldVector2D<>(xm, ym); + + final T[][] covarianceMatrixData = MathArrays.buildArray(sigmaX.getField(), 2, 2); + covarianceMatrixData[0][0] = sigmaX.multiply(sigmaX); + covarianceMatrixData[1][1] = sigmaY.multiply(sigmaY); + final FieldMatrix covariance = new BlockFieldMatrix<>(covarianceMatrixData); + + return computeSquaredMahalanobisDistance(position, covariance); + } + + /** + * Compute the squared Mahalanobis distance. + * + * @param otherPosition other collision object projected position onto the collision plane in the rotated encounter + * frame + * @param covarianceMatrix combined covariance matrix projected onto the collision plane and diagonalized + * @param type of the field elements + * + * @return squared Mahalanobis distance + */ + public static > T computeSquaredMahalanobisDistance( + final FieldVector2D otherPosition, + final FieldMatrix covarianceMatrix) { + + final FieldMatrix covarianceMatrixInverse = new FieldLUDecomposition<>(covarianceMatrix).getSolver().getInverse(); + + final FieldMatrix otherPositionOnCollisionPlaneMatrix = new Array2DRowFieldMatrix<>(otherPosition.toArray()); + + return otherPositionOnCollisionPlaneMatrix.transposeMultiply( + covarianceMatrixInverse.multiply(otherPositionOnCollisionPlaneMatrix)).getEntry(0, 0); + } + + /** + * Compute the other collision position and velocity relative to the reference collision object. Expressed in the + * reference collision object inertial frame. + * + * @return other collision position and velocity relative to the reference collision object, expressed in the reference + * collision object inertial frame. + */ + public FieldPVCoordinates computeOtherRelativeToReferencePVInReferenceInertial() { + + // Extract reference inertial frame + final Frame referenceInertial = referenceAtTCA.getFrame(); + + // Get PVCoordinates in the same frame + final FieldPVCoordinates referencePV = referenceAtTCA.getPVCoordinates(); + final FieldPVCoordinates otherPVInReferenceInertial = otherAtTCA.getPVCoordinates(referenceInertial); + + // Create relative pv expressed in the reference inertial frame + final FieldVector3D relativePosition = otherPVInReferenceInertial.getPosition() + .subtract(referencePV.getPosition()); + final FieldVector3D relativeVelocity = otherPVInReferenceInertial.getVelocity() + .subtract(referencePV.getVelocity()); + + return new FieldPVCoordinates<>(relativePosition, relativeVelocity); + } + + /** + * Compute the projection matrix from the reference collision object inertial frame to the collision plane. + *

        + * Note that this matrix will only rotate from the reference collision object inertial frame to the encounter frame and + * project onto the collision plane, this is only a rotation. + *

        + * + * @return projection matrix from the reference collision object inertial frame to the collision plane + */ + public FieldMatrix computeReferenceInertialToCollisionPlaneProjectionMatrix() { + + // Create transform from reference inertial frame to encounter local orbital frame + final FieldTransform referenceInertialToEncounterFrameTransform = + new FieldTransform<>(tca, + computeReferenceInertialToReferenceTNWTransform(), + computeReferenceTNWToEncounterFrameTransform()); + + // Create rotation matrix from reference inertial frame to encounter local orbital frame + final FieldMatrix referenceInertialToEncounterFrameRotationMatrix = + new Array2DRowFieldMatrix<>(referenceInertialToEncounterFrameTransform.getRotation().getMatrix()); + + // Create projection matrix from encounter frame to collision plane + final FieldMatrix encounterFrameToCollisionPlaneProjectionMatrix = + encounterFrame.computeProjectionMatrix(tca.getField()); + + // Create projection matrix from reference inertial frame to collision plane + return encounterFrameToCollisionPlaneProjectionMatrix.multiply(referenceInertialToEncounterFrameRotationMatrix); + } + + /** + * Compute the combined covariance matrix diagonalized and projected onto the collision plane. + *

        + * Diagonalize projected positional covariance matrix in a specific manner to have + * σxx2 ≤ σyy2. + * + * @return combined covariance matrix diagonalized and projected onto the collision plane + */ + public FieldMatrix computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix() { + + final FieldMatrix covarianceMatrixToDiagonalize = computeProjectedCombinedPositionalCovarianceMatrix(); + + final T sigmaXSquared = covarianceMatrixToDiagonalize.getEntry(0, 0); + final T sigmaYSquared = covarianceMatrixToDiagonalize.getEntry(1, 1); + + final T crossTerm = covarianceMatrixToDiagonalize.getEntry(0, 1); + final T recurrentTerm = sigmaXSquared.subtract(sigmaYSquared).multiply(0.5).pow(2) + .add(crossTerm.multiply(crossTerm)).sqrt(); + + final T eigenValueX = sigmaXSquared.add(sigmaYSquared).multiply(0.5).subtract(recurrentTerm); + final T eigenValueY = sigmaXSquared.add(sigmaYSquared).multiply(0.5).add(recurrentTerm); + + final FieldMatrix projectedAndDiagonalizedCombinedPositionalCovarianceMatrix = + new BlockFieldMatrix<>(instanceField, 2, 2); + projectedAndDiagonalizedCombinedPositionalCovarianceMatrix.setEntry(0, 0, eigenValueX); + projectedAndDiagonalizedCombinedPositionalCovarianceMatrix.setEntry(0, 1, instanceField.getZero()); + projectedAndDiagonalizedCombinedPositionalCovarianceMatrix.setEntry(1, 0, instanceField.getZero()); + projectedAndDiagonalizedCombinedPositionalCovarianceMatrix.setEntry(1, 1, eigenValueY); + + return projectedAndDiagonalizedCombinedPositionalCovarianceMatrix; + } + + /** + * Compute the projected combined covariance matrix onto the collision plane. + * + * @return projected combined covariance matrix onto the collision plane + */ + public FieldMatrix computeProjectedCombinedPositionalCovarianceMatrix() { + + // Compute the positional covariance in the encounter local orbital frame + final FieldMatrix combinedPositionalCovarianceMatrixInEncounterFrame = + computeCombinedCovarianceInEncounterFrame().getMatrix().getSubMatrix(0, 2, 0, 2); + + // Project it onto the collision plane + return encounterFrame.projectOntoCollisionPlane(combinedPositionalCovarianceMatrixInEncounterFrame); + } + + /** + * Compute the combined covariance expressed in the encounter frame. + * + * @return combined covariance expressed in the encounter frame + */ + public FieldStateCovariance computeCombinedCovarianceInEncounterFrame() { + return computeCombinedCovarianceInReferenceTNW().changeCovarianceFrame(referenceAtTCA, encounterFrame); + } + + /** + * Compute the other collision object {@link FieldVector2D position} projected onto the collision plane. + * + * @return other collision object position projected onto the collision plane + */ + public FieldVector2D computeOtherPositionInCollisionPlane() { + + // Express other in reference inertial + final FieldPVCoordinates otherInReferenceInertial = otherAtTCA.getPVCoordinates(referenceAtTCA.getFrame()); + + // Express other in reference TNW local orbital frame + final FieldPVCoordinates otherPVInReferenceTNW = + computeReferenceInertialToReferenceTNWTransform().transformPVCoordinates(otherInReferenceInertial); + + // Express other in encounter local orbital frame + final FieldPVCoordinates otherPVInEncounterFrame = + computeReferenceTNWToEncounterFrameTransform().transformPVCoordinates( + otherPVInReferenceTNW); + + return encounterFrame.projectOntoCollisionPlane(otherPVInEncounterFrame.getPosition()); + + } + + /** + * Compute the other collision object {@link FieldVector2D position} in the rotated collision plane. + *

        + * Uses a default zero threshold of 1e-15. + *

        + * The coordinates are often noted xm and ym in probability of collision related papers. + *

        + *

        + * The mentioned rotation concerns the rotation that diagonalize the combined covariance matrix inside the collision + * plane. + *

        + * + * @return other collision object position in the rotated collision plane + */ + public FieldVector2D computeOtherPositionInRotatedCollisionPlane() { + return computeOtherPositionInRotatedCollisionPlane(DEFAULT_ZERO_THRESHOLD); + + } + + /** + * Compute the other collision object {@link Vector2D position} in the rotated collision plane. + *

        + * The coordinates are often noted xm and ym in probability of collision related papers. + *

        + * The mentioned rotation concerns the rotation that diagonalize the combined covariance matrix inside the collision + * plane. + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return other collision object position in the rotated collision plane + */ + public FieldVector2D computeOtherPositionInRotatedCollisionPlane(final double zeroThreshold) { + + // Project the other position onto the collision plane + final FieldMatrix otherPositionInCollisionPlaneMatrix = + new Array2DRowFieldMatrix<>(computeOtherPositionInCollisionPlane().toArray()); + + // Express other in the rotated collision plane + final FieldMatrix otherPositionRotatedInCollisionPlane = + computeEncounterPlaneRotationMatrix(zeroThreshold).multiply(otherPositionInCollisionPlaneMatrix); + + return new FieldVector2D<>(otherPositionRotatedInCollisionPlane.getColumn(0)); + + } + + /** + * Compute the Encounter duration (s) evaluated using Coppola's formula described in : "COPPOLA, Vincent, et al. + * Evaluating the short encounter assumption of the probability of collision formula. 2012." + *

        + * This method is to be used to check the validity of the short-term encounter model. The user is expected to compare the + * computed duration with the orbital period from both objects and draw its own conclusions. + *

        + * It uses γ = 1e-16 as the resolution of a double is nearly 1e-16 so γ smaller than that are not meaningful to compute. + * + * @return encounter duration (s) evaluated using Coppola's formula + */ + public T computeCoppolaEncounterDuration() { + + // Default value for γ = 1e-16 + final T DEFAULT_ALPHA_C = instanceField.getOne().multiply(5.864); + + final FieldMatrix combinedPositionalCovarianceMatrix = computeCombinedCovarianceInEncounterFrame() + .getMatrix().getSubMatrix(0, 2, 0, 2); + + // Extract off-plane cross-term matrix + final FieldMatrix projectionMatrix = encounterFrame.computeProjectionMatrix(instanceField); + final FieldMatrix axisNormalToCollisionPlane = + new Array2DRowFieldMatrix<>(encounterFrame.getAxisNormalToCollisionPlane(instanceField).toArray()); + final FieldMatrix offPlaneCrossTermMatrix = + projectionMatrix.multiply(combinedPositionalCovarianceMatrix.multiply(axisNormalToCollisionPlane)); + + // Covariance sub-matrix of the in-plane terms + final FieldMatrix probabilityDensity = + encounterFrame.projectOntoCollisionPlane(combinedPositionalCovarianceMatrix); + final FieldMatrix probabilityDensityInverse = + new FieldLUDecomposition<>(probabilityDensity).getSolver().getInverse(); + + // Recurrent term in Coppola's paper : bᵀb + final FieldMatrix b = offPlaneCrossTermMatrix.transposeMultiply(probabilityDensityInverse).transpose(); + final T recurrentTerm = b.multiplyTransposed(b).getEntry(0, 0); + + // Position uncertainty normal to collision plane + final T sigmaSqNormalToPlan = axisNormalToCollisionPlane.transposeMultiply( + combinedPositionalCovarianceMatrix.multiply(axisNormalToCollisionPlane)).getEntry(0, 0); + final T sigmaV = sigmaSqNormalToPlan.subtract(b.multiplyTransposed(offPlaneCrossTermMatrix).getEntry(0, 0)) + .sqrt(); + + final T relativeVelocity = computeOtherRelativeToReferencePVInReferenceInertial().getVelocity().getNorm(); + + return DEFAULT_ALPHA_C.multiply(sigmaV).multiply(2 * FastMath.sqrt(2)).add( + combinedRadius.multiply(recurrentTerm.add(1).sqrt().add(recurrentTerm.sqrt()))).divide(relativeVelocity); + } + + /** + * Compute the miss distance at time of closest approach. + * + * @return miss distance + */ + public T computeMissDistance() { + + // Get positions expressed in the same frame at time of closest approach + final FieldVector3D referencePositionAtTCA = referenceAtTCA.getPosition(); + final FieldVector3D otherPositionAtTCA = otherAtTCA.getPosition(referenceAtTCA.getFrame()); + + // Compute relative position + final FieldVector3D relativePosition = otherPositionAtTCA.subtract(referencePositionAtTCA); + + return relativePosition.getNorm(); + } + + /** + * Compute the Mahalanobis distance computed with the other collision object projected onto the collision plane (commonly + * called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix is + * diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + *

        + * Uses a default zero threshold of 1e-15 for the computation of the diagonalizing of the projected covariance matrix. + * + * @return Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public T computeMahalanobisDistance() { + return computeMahalanobisDistance(DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the Mahalanobis distance computed with the other collision object projected onto the collision plane (commonly + * called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix is + * diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public T computeMahalanobisDistance(final double zeroThreshold) { + return computeSquaredMahalanobisDistance(zeroThreshold).sqrt(); + } + + /** + * Compute the squared Mahalanobis distance computed with the other collision object projected onto the collision plane + * (commonly called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix + * is diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + *

        + * Uses a default zero threshold of 1e-15 for the computation of the diagonalizing of the projected covariance matrix. + * + * @return squared Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public T computeSquaredMahalanobisDistance() { + return computeSquaredMahalanobisDistance(DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the squared Mahalanobis distance computed with the other collision object projected onto the collision plane + * (commonly called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix + * is diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return squared Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public T computeSquaredMahalanobisDistance(final double zeroThreshold) { + + final FieldMatrix otherPositionAfterRotationInCollisionPlane = + new Array2DRowFieldMatrix<>(computeOtherPositionInRotatedCollisionPlane(zeroThreshold).toArray()); + + final FieldMatrix inverseCovarianceMatrix = + new FieldLUDecomposition<>( + computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix()).getSolver() + .getInverse(); + + return otherPositionAfterRotationInCollisionPlane.transpose().multiply( + inverseCovarianceMatrix.multiply( + otherPositionAfterRotationInCollisionPlane)) + .getEntry(0, 0); + } + + /** Get new encounter instance. + * @return new encounter instance + */ + public ShortTermEncounter2DDefinition toEncounter() { + return new ShortTermEncounter2DDefinition(referenceAtTCA.toOrbit(), referenceCovariance.toStateCovariance(), + otherAtTCA.toOrbit(), otherCovariance.toStateCovariance(), + combinedRadius.getReal()); + } + + /** + * Takes both covariance matrices (expressed in their respective RTN local orbital frame) from reference and other + * collision object with which this instance was created and sum them in the reference collision object TNW local orbital + * frame. + * + * @return combined covariance matrix expressed in the reference collision object TNW local orbital frame + */ + public FieldStateCovariance computeCombinedCovarianceInReferenceTNW() { + + // Express reference covariance in reference TNW local orbital frame + final FieldMatrix referenceCovarianceMatrixInTNW = + referenceCovariance.changeCovarianceFrame(referenceAtTCA, LOFType.TNW_INERTIAL).getMatrix(); + + // Express other covariance in reference inertial frame + final FieldMatrix otherCovarianceMatrixInReferenceInertial = + otherCovariance.changeCovarianceFrame(otherAtTCA, referenceAtTCA.getFrame()).getMatrix(); + + final FieldStateCovariance otherCovarianceInReferenceInertial = new FieldStateCovariance<>( + otherCovarianceMatrixInReferenceInertial, tca, referenceAtTCA.getFrame(), + OrbitType.CARTESIAN, PositionAngleType.MEAN); + + // Express other covariance in reference TNW local orbital frame + final FieldMatrix otherCovarianceMatrixInReferenceTNW = otherCovarianceInReferenceInertial.changeCovarianceFrame( + referenceAtTCA, LOFType.TNW_INERTIAL).getMatrix(); + + // Return the combined covariance expressed in the reference TNW local orbital frame + return new FieldStateCovariance<>(referenceCovarianceMatrixInTNW.add(otherCovarianceMatrixInReferenceTNW), tca, + LOFType.TNW_INERTIAL); + } + + /** + * Compute the {@link FieldTransform transform} from the reference collision object inertial frame of reference to its + * TNW local orbital frame. + * + * @return transform from the reference collision object inertial frame of reference to its TNW local orbital frame + */ + private FieldTransform computeReferenceInertialToReferenceTNWTransform() { + return LOFType.TNW.transformFromInertial(tca, referenceAtTCA.getPVCoordinates()); + } + + /** + * Compute the {@link FieldTransform transform} from the reference collision object TNW local orbital frame to the + * encounter frame. + * + * @return transform from the reference collision object TNW local orbital frame to the encounter frame + */ + private FieldTransform computeReferenceTNWToEncounterFrameTransform() { + return LOF.transformFromLOFInToLOFOut(LOFType.TNW_INERTIAL, encounterFrame, tca, + referenceAtTCA.getPVCoordinates()); + } + + /** + * Compute the rotation matrix that diagonalize the combined positional covariance matrix projected onto the collision + * plane. + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return rotation matrix that diagonalize the combined covariance matrix projected onto the collision plane + */ + private FieldMatrix computeEncounterPlaneRotationMatrix(final double zeroThreshold) { + + final FieldMatrix combinedCovarianceMatrixInEncounterFrame = + computeCombinedCovarianceInEncounterFrame().getMatrix(); + + final FieldMatrix combinedPositionalCovarianceMatrixProjectedOntoBPlane = + encounterFrame.projectOntoCollisionPlane( + combinedCovarianceMatrixInEncounterFrame.getSubMatrix(0, 2, 0, 2)); + + final T sigmaXSquared = combinedPositionalCovarianceMatrixProjectedOntoBPlane.getEntry(0, 0); + final T sigmaYSquared = combinedPositionalCovarianceMatrixProjectedOntoBPlane.getEntry(1, 1); + final T crossTerm = combinedPositionalCovarianceMatrixProjectedOntoBPlane.getEntry(0, 1); + final T correlation = crossTerm.divide(sigmaXSquared.multiply(sigmaYSquared).sqrt()); + + // If the matrix is not initially diagonalized + final T theta; + if (FastMath.abs(crossTerm).getReal() > zeroThreshold) { + final T recurrentTerm = sigmaYSquared.subtract(sigmaXSquared).divide(crossTerm.multiply(2)); + theta = recurrentTerm.subtract(correlation.sign().multiply(recurrentTerm.pow(2).add(1).sqrt())).atan(); + } + // Else, the matrix is already diagonalized + else { + // Rotation in order to have sigmaXSquared < sigmaYSquared + if (sigmaXSquared.subtract(sigmaYSquared).getReal() > 0) { + theta = tca.getField().getOne().multiply(MathUtils.SEMI_PI); + } + // Else, there is no need for a rotation + else { + theta = tca.getField().getZero(); + } + } + + final T cosTheta = theta.cos(); + final T sinTheta = theta.sin(); + final Array2DRowFieldMatrix rotationMatrix = new Array2DRowFieldMatrix<>(tca.getField(), 2, 2); + rotationMatrix.setEntry(0, 0, cosTheta); + rotationMatrix.setEntry(0, 1, sinTheta); + rotationMatrix.setEntry(1, 0, sinTheta.negate()); + rotationMatrix.setEntry(1, 1, cosTheta); + + return rotationMatrix; + } + + /** + * Get the Time of Closest Approach. + *

        + * Commonly called TCA. + * + * @return time of closest approach + */ + public FieldAbsoluteDate getTca() { + return tca; + } + + /** Get reference's orbit at time of closest approach. + * @return reference's orbit at time of closest approach + */ + public FieldOrbit getReferenceAtTCA() { + return referenceAtTCA; + } + + /** Get other's orbit at time of closest approach. + * @return other's orbit at time of closest approach + */ + public FieldOrbit getOtherAtTCA() { + return otherAtTCA; + } + + /** Get reference's covariance. + * @return reference's covariance + */ + public FieldStateCovariance getReferenceCovariance() { + return referenceCovariance; + } + + /** Get other's covariance. + * @return other's covariance + */ + public FieldStateCovariance getOtherCovariance() { + return otherCovariance; + } + + /** Get combined radius. + * @return combined radius (m) + */ + public T getCombinedRadius() { + return combinedRadius; + } + + /** Get encounter local orbital frame. + * @return encounter local orbital frame + */ + public EncounterLOF getEncounterFrame() { + return encounterFrame; + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java new file mode 100644 index 0000000000..0db7a451ec --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java @@ -0,0 +1,445 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * MIT License + * + * Copyright (c) 2019 Romain Serra + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.linear.FieldVector; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.stat.StatUtils; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.MathUtils; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; + +/** + * Compute the probability of collision using the method described in : "SERRA, Romain, ARZELIER, Denis, JOLDES, Mioara, et + * al. Fast and accurate computation of orbital collision probability for short-term encounters. Journal of Guidance, + * Control, and Dynamics, 2016, vol. 39, no 5, p. 1009-1021.". + *

        + * It is one of the recommended methods to use. + *

        + * It assumes : + *

          + *
        • Short encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        + *

        + * The following constants are defined when using the empty constructor : + *

          + *
        • A default absolute accuracy of 1e-30.
        • + *
        • A maximum number of computed terms of 37000.
        • + *
        + *

        + * This implementation has been translated from python from the provided source code of Romain SERRA on the + * following github account + * + * @author Vincent Cucchietti + * @author Romain Serra + * @since 12.0 + */ +public class Laas2015 extends AbstractShortTermEncounter2DPOCMethod { + + /** Default scaling threshold to use when sum becomes large. */ + public static final double DEFAULT_SCALING_THRESHOLD = 1e10; + + /** + * Defines the absolute accuracy of this method. For example, given an absolute accuracy of 1e-10, the probability of + * collision will be exact until its 1e-10 digit. + */ + private final double absoluteAccuracy; + + /** Defines the max number of terms that the method will use, thus reducing the computation time in particular cases. */ + private final int maxNumberOfTerms; + + /** + * Default constructor. + *

        + * It uses a default absolute accuracy of 1e-30 and a maximum number of terms of 37000 which is the max number of terms + * computed based on Romain SERRA's observation (p.56 of "Romain Serra. Opérations de proximité en orbite : * évaluation + * du risque de collision et calcul de manoeuvres optimales pour l’évitement et le rendez-vous. Automatique / * + * Robotique. INSA de Toulouse, 2015. Français. NNT : 2015ISAT0035. tel-01261497") about Alfano test case 5 where he + * explains that 37000 terms were enough to meet the required precision of 5 significant digits. + */ + public Laas2015() { + this(1.E-30, 37000); + } + + /** Simple constructor. + * @param absoluteAccuracy absolute accuracy of the result + * @param maxNumberOfTerms max number of terms to compute + */ + public Laas2015(final double absoluteAccuracy, final int maxNumberOfTerms) { + super(ShortTermEncounter2DPOCMethodType.LAAS_2015.name()); + this.absoluteAccuracy = absoluteAccuracy; + this.maxNumberOfTerms = maxNumberOfTerms; + } + + /** {@inheritDoc} */ + public final ProbabilityOfCollision compute(final double xm, final double ym, + final double sigmaX, + final double sigmaY, + final double radius) { + + // CHECKSTYLE: stop Indentation check + // Initializing recurrent terms + final double xmSquared = xm * xm; + final double ymSquared = ym * ym; + final double sigmaXSq = sigmaX * sigmaX; + final double sigmaYSq = sigmaY * sigmaY; + final double radiusSquared = radius * radius; + + final double p = 1. / (2. * (sigmaX * sigmaX)); + final double phiY = 1. - (sigmaX / sigmaY * sigmaX / sigmaY); + final double omegaX = (xm / (2. * sigmaXSq)) * (xm / (2. * sigmaXSq)); + final double omegaY = (ym / (2. * sigmaYSq)) * (ym / (2. * sigmaYSq)); + final double bigOmega = phiY * 0.5 + (omegaX + omegaY) / p; + + final double alpha0 = 0.5 * FastMath.exp(-(xmSquared / sigmaXSq + ymSquared / sigmaYSq) * 0.5) / sigmaX / sigmaY; + + // Lower boundary + final double l0 = alpha0 * (1. - FastMath.exp(-p * radiusSquared)) / p; + + // Upper boundary + final double u0Temp = alpha0 * (FastMath.exp(p * bigOmega * radiusSquared) - + FastMath.exp(-p * radiusSquared)) / (p * (1. + bigOmega)); + final double u0 = u0Temp > 1 ? 1 : u0Temp; + + // If the boundaries are close enough to the actual value according to defined relative accuracy + if (u0 - l0 <= absoluteAccuracy) { + return new ProbabilityOfCollision(StatUtils.mean(u0, l0), u0, l0, getName(), + isAMaximumProbabilityOfCollisionMethod()); + } + // Otherwise + else { + final int n1 = (int) (2. * FastMath.ceil(FastMath.E * p * radiusSquared * (1. + bigOmega))); + final double n2_inter = + alpha0 * FastMath.exp(p * radiusSquared * bigOmega) / + (absoluteAccuracy * p * FastMath.sqrt(MathUtils.TWO_PI * n1) * + (1. + bigOmega)); + final int n2 = (int) FastMath.ceil(FastMath.log(2, n2_inter)); + + // Number of terms to get the relative accuracy desired + int nMax = FastMath.max(n1, n2) - 1; + nMax = FastMath.min(nMax, maxNumberOfTerms); + + // Initializing terms used in the equations + final double pSquared = p * p; + final double pCubed = pSquared * p; + final double pTimesRadiusSquared = p * radiusSquared; + final double radiusFourth = radiusSquared * radiusSquared; + final double radiusSixth = radiusFourth * radiusSquared; + final double phiYSquared = phiY * phiY; + final double pPhiY = p * phiY; + final double omegaSum = omegaX + omegaY; + final double pSqTimesHalfPhiYSqPlusOne = pSquared * MathArrays.linearCombination(0.5, phiYSquared, 1., 1.); + + final double recurrentTerm0 = MathArrays.linearCombination(0.5, phiY, 1., 1.); + final double recurrentTerm1 = MathArrays.linearCombination(p, recurrentTerm0, 1., omegaSum); + final double recurrentTerm2 = MathArrays.linearCombination(1., pSqTimesHalfPhiYSqPlusOne, 2., pPhiY * omegaY); + final double recurrentTerm3 = recurrentTerm1 * recurrentTerm1; + + final double auxiliaryTerm0 = radiusSixth * pCubed * phiYSquared * omegaX; + final double auxiliaryTerm1 = radiusFourth * pSquared * phiY; + final double auxiliaryTerm2 = 2. * omegaX * recurrentTerm0; + + final double auxiliaryTerm3 = phiY * MathArrays.linearCombination(2., omegaX, 1.5, p) + omegaSum; + final double auxiliaryTerm4 = pPhiY * recurrentTerm0 * 2.; + final double auxiliaryTerm5 = p * (2. * phiY + 1.); + + double kPlus2 = 2.; + double kPlus3 = 3.; + double kPlus4 = 4.; + double kPlus5 = 5.; + double halfY = 2.5; + + // Initialize recurrence + double c0 = alpha0 * radiusSquared; + double c1 = c0 * radiusSquared * 0.5 * recurrentTerm1; + double c2 = c0 * (radiusFourth / 12.) * (recurrentTerm3 + recurrentTerm2); + double c3 = c0 * (radiusSixth / 144.) * (recurrentTerm1 * (recurrentTerm3 + 3. * recurrentTerm2) + + 2. * (pCubed * (1. + phiYSquared * phiY * 0.5) + 3. * pSquared * phiYSquared * omegaY)); + final double[] initialCoefficients = new double[] { c0, c1, c2, c3 }; + + double sum = 0.; + double rescalingCounter = 0.; + for (int i = 0; i < FastMath.min(nMax, 4); i++) { + sum += initialCoefficients[i]; + + // Rescale quantities if necessary + if (sum > DEFAULT_SCALING_THRESHOLD) { + rescalingCounter += FastMath.log10(DEFAULT_SCALING_THRESHOLD); + c0 /= DEFAULT_SCALING_THRESHOLD; + c1 /= DEFAULT_SCALING_THRESHOLD; + c2 /= DEFAULT_SCALING_THRESHOLD; + c3 /= DEFAULT_SCALING_THRESHOLD; + sum /= DEFAULT_SCALING_THRESHOLD; + } + + } + + // Iterate + double temp; + for (int k = 0; k < nMax - 4; k++) { + + // Rescale quantities if necessary + if (sum > DEFAULT_SCALING_THRESHOLD) { + rescalingCounter += FastMath.log10(DEFAULT_SCALING_THRESHOLD); + c0 /= DEFAULT_SCALING_THRESHOLD; + c1 /= DEFAULT_SCALING_THRESHOLD; + c2 /= DEFAULT_SCALING_THRESHOLD; + c3 /= DEFAULT_SCALING_THRESHOLD; + sum /= DEFAULT_SCALING_THRESHOLD; + } + + // Recurrence relation + final double denominator = kPlus4 * kPlus3; + + temp = c3 * MathArrays.linearCombination(1, recurrentTerm1, kPlus3, auxiliaryTerm5); + temp -= c2 * pTimesRadiusSquared * MathArrays.linearCombination(halfY, auxiliaryTerm4, 1, auxiliaryTerm3) / + kPlus4; + temp += c1 * auxiliaryTerm1 * MathArrays.linearCombination(halfY, pPhiY, 1., auxiliaryTerm2) / denominator; + temp -= c0 * auxiliaryTerm0 / (denominator * kPlus2); + temp *= radiusSquared / (kPlus4 * kPlus5); + + c0 = c1; + c1 = c2; + c2 = c3; + c3 = temp; + + // Update intermediate variables + kPlus2 = kPlus3; + kPlus3 = kPlus4; + kPlus4 = kPlus5; + kPlus5 = kPlus5 + 1.; + halfY += 1.; + + // Update sum + sum += c3; + + } + // CHECKSTYLE: resume Indentation check + final double value = sum * + FastMath.exp(MathArrays.linearCombination(FastMath.log(10.), rescalingCounter, -p, radiusSquared)); + + return new ProbabilityOfCollision(value, l0, u0, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + } + + /** {@inheritDoc} */ + public final > FieldProbabilityOfCollision compute(final T xm, final T ym, + final T sigmaX, + final T sigmaY, + final T radius) { + + // CHECKSTYLE: stop Indentation check + // Initializing recurrent terms + final Field field = xm.getField(); + final T zero = field.getZero(); + final T one = field.getOne(); + + final T xmSquared = xm.multiply(xm); + final T ymSquared = ym.multiply(ym); + final T sigmaXSquared = sigmaX.multiply(sigmaX); + final T sigmaYSquared = sigmaY.multiply(sigmaY); + final T twoSigmaXY = sigmaX.multiply(sigmaY).multiply(2); + final T radiusSquared = radius.multiply(radius); + final T radiusFourth = radiusSquared.multiply(radiusSquared); + final T radiusSixth = radiusFourth.multiply(radiusSquared); + + final T p = sigmaX.multiply(sigmaX).reciprocal().multiply(0.5); + final T pTimesRadiusSquared = p.multiply(radiusSquared); + final T phiY = sigmaXSquared.divide(sigmaYSquared).negate().add(1.); + final T omegaX = xm.divide(sigmaXSquared.multiply(2.)).pow(2.); + final T omegaY = ym.divide(sigmaYSquared.multiply(2.)).pow(2.); + final T omegaSum = omegaX.add(omegaY); + final T bigOmega = phiY.multiply(0.5).add(omegaX.add(omegaY).divide(p)); + + final T minusP = p.negate(); + final T pSquared = p.multiply(p); + final T pCubed = p.multiply(pSquared); + final T pPhiY = p.multiply(phiY); + final T phiYSquared = phiY.multiply(phiY); + final T pRadiusSquaredBigOmega = p.multiply(radiusSquared).multiply(bigOmega); + final T bigOmegaPlusOne = bigOmega.add(1.); + final T pSqTimesHalfPhiYSqPlusOne = pSquared.multiply(phiYSquared.multiply(0.5).add(1.)); + + final T alpha0 = xmSquared.divide(sigmaXSquared).add(ymSquared.divide(sigmaYSquared)).multiply(-0.5).exp() + .divide(twoSigmaXY); + + // Lower boundary + final T l0 = alpha0.multiply(radiusSquared.multiply(minusP).exp().negate().add(1.)).divide(p); + + // Upper boundary + final T u0Temp = alpha0.multiply(pRadiusSquaredBigOmega.exp().subtract(radiusSquared.multiply(minusP).exp())) + .divide(p.multiply(bigOmegaPlusOne)); + final T u0 = u0Temp.getReal() > 1 ? one : u0Temp; + + // If the boundaries are close enough to the actual value according to defined relative accuracy + if (u0.getReal() - l0.getReal() <= absoluteAccuracy) { + return new FieldProbabilityOfCollision<>(u0.add(l0).multiply(0.5), u0, l0, getName(), + isAMaximumProbabilityOfCollisionMethod()); + } + // Otherwise + else { + final int n1 = (int) (2. * + FastMath.ceil(FastMath.E * p.multiply(radiusSquared).multiply(bigOmegaPlusOne).getReal())); + final double n2_inter = + alpha0.getReal() * FastMath.exp(pRadiusSquaredBigOmega.getReal()) / + (absoluteAccuracy * p.getReal() * FastMath.sqrt(MathUtils.TWO_PI * n1) * + (1. + bigOmega.getReal())); + final int n2 = (int) FastMath.ceil(FastMath.log(2., n2_inter)); + + // Number of terms to get the relative accuracy desired + int nMax = FastMath.max(n1, n2) - 1; + nMax = FastMath.min(nMax, maxNumberOfTerms); + + // Recurrent term in the equations + final T recurrentTerm0 = phiY.multiply(0.5).add(1); + final T recurrentTerm1 = p.multiply(recurrentTerm0).add(omegaSum); + final T recurrentTerm2 = pSqTimesHalfPhiYSqPlusOne.add(pPhiY.multiply(omegaY).multiply(2.)); + final T recurrentTerm3 = recurrentTerm1.multiply(recurrentTerm1); + + final T auxiliaryTerm0 = radiusSixth.multiply(pCubed).multiply(phiYSquared).multiply(omegaX); + final T auxiliaryTerm1 = radiusFourth.multiply(pSquared).multiply(phiY); + final T auxiliaryTerm2 = omegaX.multiply(recurrentTerm0).multiply(2.); + final T auxiliaryTerm3 = phiY.multiply(omegaX.multiply(2.).add(p.multiply(1.5))).add(omegaSum); + final T auxiliaryTerm4 = pPhiY.multiply(recurrentTerm0).multiply(2.); + final T auxiliaryTerm5 = p.multiply(phiY.multiply(2.).add(1.)); + + T kPlus2 = one.multiply(2.); + T kPlus3 = one.multiply(3.); + T kPlus4 = one.multiply(4.); + T kPlus5 = one.multiply(5.); + T halfY = one.multiply(2.5); + + // Initialize recurrence + T c0 = alpha0.multiply(radiusSquared); + T c1 = c0.multiply(radiusSquared).multiply(0.5).multiply(recurrentTerm1); + T c2 = c0.multiply(radiusFourth.divide(12.)).multiply(recurrentTerm3.add(recurrentTerm2)); + T c3 = c0.multiply(radiusSixth.divide(144.)).multiply( + recurrentTerm1.multiply(recurrentTerm2.multiply(3.).add(recurrentTerm3)) + .add(pCubed.multiply(2.).multiply(phiYSquared.multiply(phiY).multiply(0.5).add(1.))) + .add(pSquared.multiply(phiYSquared).multiply(omegaY).multiply(6.))); + final FieldVector initialCoefficients = MatrixUtils.createFieldVector(field, 4); + initialCoefficients.setEntry(0, c0); + initialCoefficients.setEntry(1, c1); + initialCoefficients.setEntry(2, c2); + initialCoefficients.setEntry(3, c3); + + T sum = zero; + T rescalingCounter = zero; + for (int i = 0; i < FastMath.min(nMax, 4); i++) { + sum = sum.add(initialCoefficients.getEntry(i)); + + // Rescale quantities if necessary + if (sum.getReal() > DEFAULT_SCALING_THRESHOLD) { + rescalingCounter = rescalingCounter.add(FastMath.log10(DEFAULT_SCALING_THRESHOLD)); + c0 = c0.divide(DEFAULT_SCALING_THRESHOLD); + c1 = c1.divide(DEFAULT_SCALING_THRESHOLD); + c2 = c2.divide(DEFAULT_SCALING_THRESHOLD); + c3 = c3.divide(DEFAULT_SCALING_THRESHOLD); + sum = sum.divide(DEFAULT_SCALING_THRESHOLD); + } + + } + + // Iterate + T temp; + for (int k = 0; k < nMax - 4; k++) { + + // Rescale quantities if necessary + if (sum.getReal() > DEFAULT_SCALING_THRESHOLD) { + rescalingCounter = rescalingCounter.add(FastMath.log10(DEFAULT_SCALING_THRESHOLD)); + c0 = c0.divide(DEFAULT_SCALING_THRESHOLD); + c1 = c1.divide(DEFAULT_SCALING_THRESHOLD); + c2 = c2.divide(DEFAULT_SCALING_THRESHOLD); + c3 = c3.divide(DEFAULT_SCALING_THRESHOLD); + sum = sum.divide(DEFAULT_SCALING_THRESHOLD); + } + + // Recurrence relation + final T denominator = kPlus4.multiply(kPlus3); + + temp = c3.multiply(recurrentTerm1.add(auxiliaryTerm5.multiply(kPlus3))); + temp = temp.subtract( + c2.multiply(pTimesRadiusSquared).multiply(auxiliaryTerm4.multiply(halfY).add(auxiliaryTerm3)) + .divide(kPlus4)); + temp = temp.add( + c1.multiply(auxiliaryTerm1).multiply(pPhiY.multiply(halfY).add(auxiliaryTerm2)).divide(denominator)); + temp = temp.subtract(c0.multiply(auxiliaryTerm0).divide(denominator.multiply(kPlus2))); + temp = temp.multiply(radiusSquared.divide(kPlus4.multiply(kPlus5))); + + c0 = c1; + c1 = c2; + c2 = c3; + c3 = temp; + + // Update intermediate variables + kPlus2 = kPlus3; + kPlus3 = kPlus4; + kPlus4 = kPlus5; + kPlus5 = kPlus5.add(1.); + halfY = halfY.add(1.); + + // Update sum + sum = sum.add(c3); + + } + final T value = + sum.multiply((rescalingCounter.multiply(FastMath.log(10.)).subtract(pTimesRadiusSquared)).exp()); + + return new FieldProbabilityOfCollision<>(value, l0, u0, getName(), isAMaximumProbabilityOfCollisionMethod()); + // CHECKSTYLE: resume Indentation check + } + } + + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethodType getType() { + return ShortTermEncounter2DPOCMethodType.LAAS_2015; + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java new file mode 100644 index 0000000000..8f48342390 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java @@ -0,0 +1,586 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.CalculusFieldUnivariateFunction; +import org.hipparchus.analysis.UnivariateFunction; +import org.hipparchus.analysis.integration.FieldUnivariateIntegrator; +import org.hipparchus.analysis.integration.TrapezoidIntegrator; +import org.hipparchus.analysis.integration.UnivariateIntegrator; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.MathUtils; +import org.hipparchus.util.SinCos; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; + +/** + * Compute the probability of collision using the method described in :"PATERA, Russell P. Calculating collision probability + * for arbitrary space vehicle shapes via numerical quadrature. Journal of guidance, control, and dynamics, 2005, vol. 28, no + * 6, p. 1326-1328.". + *

        + * It is one of the recommended methods to use. + *

        + * It assumes : + *

          + *
        • Short encounter leading to a linear relative motion.
        • + *
        • Spherical collision object (in this implementation only. The method could be used on non spherical object).
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        + * It has been rewritten to use Orekit specific inputs. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class Patera2005 extends AbstractShortTermEncounter1DNumerical2DPOCMethod { + + /** Default threshold defining if miss-distance and combined radius are considered equal (+- 10 cm). */ + private static final double DEFAULT_EQUALITY_THRESHOLD = 1e-1; + + /** + * Default constructor built with the following trapezoid integrator: + *
          + *
        • Minimal iteration count of 5
        • + *
        • Maximum iteration count of 50000
        • + *
        . + */ + public Patera2005() { + this(new TrapezoidIntegrator(5, TrapezoidIntegrator.TRAPEZOID_MAX_ITERATIONS_COUNT), 50000); + } + + /** + * Customizable constructor. + * + * @param integrator integrator + * @param maxNbOfEval max number of evaluation + */ + public Patera2005(final UnivariateIntegrator integrator, final int maxNbOfEval) { + super("PATERA_2005", integrator, maxNbOfEval); + } + + /** {@inheritDoc} */ + @Override + public ProbabilityOfCollision compute(final double xm, final double ym, + final double sigmaX, final double sigmaY, + final double radius, + final UnivariateIntegrator integrator, + final int customMaxNbOfEval) { + + // Depending on miss distance and the combined radius, three distinct cases exist + final double value; + final double missDistance = FastMath.sqrt(xm * xm + ym * ym); + + // reference outside the hardbody area, first part of eq(11) is equal to 0 + if (missDistance > radius + DEFAULT_EQUALITY_THRESHOLD) { + final CommonPateraFunction function = new CommonPateraFunction(xm, ym, sigmaX, sigmaY, radius); + value = -integrator.integrate(customMaxNbOfEval, function, 0, MathUtils.TWO_PI) / MathUtils.TWO_PI; + + } + + // reference within the hardbody area, first part of eq(11) is equal to 1 + else if (missDistance < radius - DEFAULT_EQUALITY_THRESHOLD) { + final CommonPateraFunction function = new CommonPateraFunction(xm, ym, sigmaX, sigmaY, radius); + value = 1 - integrator.integrate(customMaxNbOfEval, function, 0, MathUtils.TWO_PI) / MathUtils.TWO_PI; + } + + // Peculiar case where miss distance = combined radius, r may be equal to zero so eq(9) must be used + else { + final PateraFunctionSpecialCase function = new PateraFunctionSpecialCase(xm, ym, sigmaX, sigmaY, radius); + value = integrator.integrate(customMaxNbOfEval, function, 0, MathUtils.TWO_PI) / MathUtils.TWO_PI; + } + + return new ProbabilityOfCollision(value, 0, 0, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + + /** {@inheritDoc} */ + @Override + public > FieldProbabilityOfCollision compute(final T xm, final T ym, + final T sigmaX, final T sigmaY, + final T radius, + final FieldUnivariateIntegrator customIntegrator, + final int customMaxNbOfEval) { + // Depending on miss distance and the combined radius, three distinct cases exist + final Field field = xm.getField(); + final T zero = field.getZero(); + final T one = field.getOne(); + final T twoPiField = one.multiply(MathUtils.TWO_PI); + + final T value; + final double missDistance = xm.multiply(xm).add(ym.multiply(ym)).sqrt().getReal(); + final double radiusReal = radius.getReal(); + + // Reference outside the hardbody area, first part of eq(11) is equal to 0 + if (missDistance > radiusReal + DEFAULT_EQUALITY_THRESHOLD) { + final CommonFieldPateraFunction function = + new CommonFieldPateraFunction<>(xm, ym, sigmaX, sigmaY, radius); + value = customIntegrator.integrate(customMaxNbOfEval, function, zero, twoPiField).divide(twoPiField).negate(); + } + + // Reference within the hardbody area, first part of eq(11) is equal to 1 + else if (missDistance < radiusReal - DEFAULT_EQUALITY_THRESHOLD) { + final CommonFieldPateraFunction function = + new CommonFieldPateraFunction<>(xm, ym, sigmaX, sigmaY, radius); + value = one.subtract( + customIntegrator.integrate(customMaxNbOfEval, function, zero, twoPiField).divide(twoPiField)); + } + + // Peculiar case where miss distance = combined radius, r may be equal to zero so eq(9) must be used + else { + final FieldPateraFunctionSpecialCase function = + new FieldPateraFunctionSpecialCase<>(xm, ym, sigmaX, sigmaY, radius); + value = customIntegrator.integrate(customMaxNbOfEval, function, zero, twoPiField).divide(twoPiField); + } + + return new FieldProbabilityOfCollision<>(value, zero, zero, getName(), isAMaximumProbabilityOfCollisionMethod()); + } + + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethodType getType() { + return ShortTermEncounter2DPOCMethodType.PATERA_2005; + } + + /** Commonly used function used in equation (11) in Patera's paper. */ + private static class CommonPateraFunction extends AbstractPateraFunction { + + /** + * Constructor. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected + * onto the collision plane (m) + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + */ + private CommonPateraFunction(final double xm, final double ym, final double sigmaX, final double sigmaY, + final double radius) { + super(xm, ym, sigmaX, sigmaY, radius); + } + + /** {@inheritDoc} */ + @Override + public double value(final double xm, final double ym, final double scaleFactor, final double sigma, + final double radius, final double theta) { + + final SinCos sinCosTheta = FastMath.sinCos(theta); + final double sinTheta = sinCosTheta.sin(); + final double cosTheta = sinCosTheta.cos(); + + final double xPrime = getXPrime(cosTheta); + final double yPrime = getYPrime(sinTheta); + final double rSquared = getRSquared(xPrime, yPrime); + + return FastMath.exp(-0.5 * rSquared / sigma / sigma) * + (radius * scaleFactor * MathArrays.linearCombination(xm, cosTheta, ym, sinTheta) + + scaleFactor * radius * radius) / rSquared; + } + } + + /** + * Function used in the rare case where miss distance = combined radius. It represents equation (9) in Patera's paper but + * has been modified to be used with Orekit specific inputs. + */ + private static class PateraFunctionSpecialCase extends AbstractPateraFunction { + + /** + * Constructor. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected + * onto the collision plane (m) + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + */ + private PateraFunctionSpecialCase(final double xm, final double ym, final double sigmaX, + final double sigmaY, final double radius) { + super(xm, ym, sigmaX, sigmaY, radius); + } + + /** {@inheritDoc} */ + @Override + public double value(final double xm, final double ym, final double scaleFactor, final double sigma, + final double radius, final double theta) { + + final SinCos sinCosTheta = FastMath.sinCos(theta); + final double sinTheta = sinCosTheta.sin(); + final double cosTheta = sinCosTheta.cos(); + + final double xPrime = getXPrime(cosTheta); + final double yPrime = getYPrime(sinTheta); + + final double rSquared = getRSquared(xPrime, yPrime); + final double sigmaSquared = sigma * sigma; + final double oneOverTwoSigmaSq = 1. / (2 * sigmaSquared); + final double rSqOverTwoSigmaSq = oneOverTwoSigmaSq * rSquared; + + return radius * scaleFactor * (MathArrays.linearCombination(xm, cosTheta, ym, sinTheta) + radius) * + oneOverTwoSigmaSq * (1 - rSqOverTwoSigmaSq * (0.5 - rSqOverTwoSigmaSq * (1. / 6 - rSqOverTwoSigmaSq * ( + 1. / 24 - rSqOverTwoSigmaSq / 720)))); + + } + } + + /** Abstract class for different functions used in Patera's paper. */ + private abstract static class AbstractPateraFunction implements UnivariateFunction { + /** + * Position on the x-axis of the rotated encounter frame. + */ + private final double xm; + + /** + * Position on the y-axis of the rotated encounter frame. + */ + private final double ym; + + /** + * Recurrent term used in Patera 2005 formula. + */ + private final double scaleFactor; + + /** + * General sigma after symmetrization. + */ + private final double sigma; + + /** + * Hardbody radius (m). + */ + private final double radius; + + /** + * Constructor. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected + * onto the collision plane (m) + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + */ + AbstractPateraFunction(final double xm, final double ym, final double sigmaX, final double sigmaY, + final double radius) { + this.xm = xm; + this.ym = ym; + this.scaleFactor = sigmaY / sigmaX; + this.sigma = sigmaY; + this.radius = radius; + } + + /** + * Compute the value of the function. + * + * @param theta angle at which the function value should be evaluated + * + * @return the value of the function + * + * @throws IllegalArgumentException when the activated method itself can ascertain that a precondition, specified in + * the API expressed at the level of the activated method, has been violated. When Hipparchus throws an + * {@code IllegalArgumentException}, it is usually the consequence of checking the actual parameters passed to the + * method. + */ + public double value(final double theta) { + return value(xm, ym, scaleFactor, sigma, radius, theta); + } + + /** + * Compute the value of the defined Patera function for input theta. + * + * @param x secondary collision object projected position onto the collision plane in the rotated encounter frame + * x-axis (m) + * @param y secondary collision object projected position onto the collision plane in the rotated encounter frame + * y-axis (m) + * @param scale scale factor used to symmetrize the probability density of collision, equal to sigmaY / sigmaX + * @param symmetrizedSigma symmetrized position sigma equal to sigmaY + * @param combinedRadius sum of primary and secondary collision object equivalent sphere radii (m) + * @param theta current angle of evaluation of the deformed hardbody radius (rad) + * + * @return value of the defined Patera function for input conjunction and theta + */ + public abstract double value(double x, double y, double scale, double symmetrizedSigma, double combinedRadius, + double theta); + + /** + * Get current x-axis component to the deformed hardbody perimeter. + * + * @param cosTheta cos of the angle determining deformed hardbody radius x-axis component to compute + * + * @return current x-axis component to the deformed hardbody perimeter + */ + public double getXPrime(final double cosTheta) { + return scaleFactor * (xm + radius * cosTheta); + } + + /** + * Get current y-axis component to the deformed hardbody perimeter. + * + * @param sinTheta sin of the angle determining deformed hardbody radius y-axis component to compute + * + * @return current y-axis component to the deformed hardbody perimeter + */ + public double getYPrime(final double sinTheta) { + return ym + radius * sinTheta; + } + + /** + * Get current distance from the reference to the determined hardbody perimeter. + * + * @param xPrime current x-axis component to the deformed hardbody perimeter + * @param yPrime current y-axis component to the deformed hardbody perimeter + * + * @return current distance from the reference to the determined hardbody perimeter + */ + public double getRSquared(final double xPrime, final double yPrime) { + return xPrime * xPrime + yPrime * yPrime; + } + } + + /** Commonly used function used in equation (11) in Patera's paper. */ + private static class CommonFieldPateraFunction> + extends AbstractFieldPateraFunction { + + /** + * Constructor. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected + * onto the collision plane (m) + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + */ + private CommonFieldPateraFunction(final T xm, final T ym, final T sigmaX, final T sigmaY, + final T radius) { + super(xm, ym, sigmaX, sigmaY, radius); + } + + /** {@inheritDoc} */ + @Override + public T value(final T xm, final T ym, final T scaleFactor, final T sigma, + final T radius, final T theta) { + + final FieldSinCos sinCosTheta = theta.sinCos(); + final T sinTheta = sinCosTheta.sin(); + final T cosTheta = sinCosTheta.cos(); + + final T xPrime = getXPrime(cosTheta); + final T yPrime = getYPrime(sinTheta); + final T rSquared = getRSquared(xPrime, yPrime); + + return rSquared.divide(sigma).divide(sigma).multiply(-0.5).exp() + .multiply(radius.multiply(scaleFactor).multiply(xm.multiply(cosTheta) + .add(ym.multiply(sinTheta))) + .add(scaleFactor.multiply(radius).multiply(radius))).divide(rSquared); + } + + } + + /** + * Function used in the rare case where miss distance = combined radius. It represents equation (9) in Patera's paper but + * has been modified to be used with Orekit specific inputs. + */ + private static class FieldPateraFunctionSpecialCase> + extends AbstractFieldPateraFunction { + + /** + * Constructor. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected + * onto the collision plane (m) + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + */ + private FieldPateraFunctionSpecialCase(final T xm, final T ym, final T sigmaX, + final T sigmaY, final T radius) { + super(xm, ym, sigmaX, sigmaY, radius); + } + + /** {@inheritDoc} */ + @Override + public T value(final T xm, final T ym, final T scaleFactor, final T sigma, + final T radius, final T theta) { + + final FieldSinCos sinCosTheta = theta.sinCos(); + final T sinTheta = sinCosTheta.sin(); + final T cosTheta = sinCosTheta.cos(); + + final T xPrime = scaleFactor.multiply(xm).add(scaleFactor.multiply(radius).multiply(cosTheta)); + final T yPrime = ym.add(radius.multiply(sinTheta)); + final T rSquared = xPrime.multiply(xPrime).add(yPrime.multiply(yPrime)); + final T sigmaSquared = sigma.multiply(sigma); + final T oneOverTwoSigmaSq = sigmaSquared.multiply(2.).reciprocal(); + final T rSqOverTwoSigmaSq = rSquared.multiply(oneOverTwoSigmaSq); + + // Recursive approach to maximize usage of the same fielded variables + return radius.multiply(scaleFactor).multiply(xm.multiply(cosTheta).add(ym.multiply(sinTheta)).add(radius)) + .multiply(oneOverTwoSigmaSq.negate() + .multiply(rSqOverTwoSigmaSq + .multiply(rSqOverTwoSigmaSq + .multiply(rSqOverTwoSigmaSq + .multiply( + rSqOverTwoSigmaSq.multiply( + -1. / 720) + .add(1. / 24)) + .subtract(1. / 6)) + .add(0.5)) + .subtract(1.))); + } + } + + /** Abstract class for different functions used in Patera's paper. */ + private abstract static class AbstractFieldPateraFunction> implements + CalculusFieldUnivariateFunction { + /** + * Position on the x-axis of the rotated encounter frame. + */ + private final T xm; + + /** + * Position on the y-axis of the rotated encounter frame. + */ + private final T ym; + + /** + * Recurrent term used in Patera 2005 formula. + */ + private final T scaleFactor; + + /** + * General sigma after symmetrization. + */ + private final T sigma; + + /** + * Hardbody radius (m). + */ + private final T radius; + + /** + * Constructor. + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected + * onto the collision plane (m) + * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto + * the collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + */ + AbstractFieldPateraFunction(final T xm, final T ym, final T sigmaX, final T sigmaY, + final T radius) { + this.xm = xm; + this.ym = ym; + this.scaleFactor = sigmaY.divide(sigmaX); + this.sigma = sigmaY; + this.radius = radius; + } + + /** + * Compute the value of the function. + * + * @param theta angle at which the function value should be evaluated + * + * @return the value of the function + * + * @throws IllegalArgumentException when the activated method itself can ascertain that a precondition, specified in + * the API expressed at the level of the activated method, has been violated. When Hipparchus throws an + * {@code IllegalArgumentException}, it is usually the consequence of checking the actual parameters passed to the + * method. + */ + @Override + public T value(final T theta) { + return value(xm, ym, scaleFactor, sigma, radius, theta); + } + + /** + * Compute the value of the defined Patera function for input theta. + * + * @param x secondary collision object projected position onto the collision plane in the rotated encounter frame + * x-axis (m) + * @param y secondary collision object projected position onto the collision plane in the rotated encounter frame + * y-axis (m) + * @param scale scale factor used to symmetrize the probability density of collision, equal to sigmaY / sigmaX + * @param symmetrizedSigma symmetrized position sigma equal to sigmaY + * @param combinedRadius sum of primary and secondary collision object equivalent sphere radii (m) + * @param theta current angle of evaluation of the deformed hardbody radius (rad) + * + * @return value of the defined Patera function for input conjunction and theta + */ + public abstract T value(T x, T y, T scale, T symmetrizedSigma, T combinedRadius, + T theta); + + /** + * Get current x-axis component to the deformed hardbody perimeter. + * + * @param cosTheta cos of the angle determining deformed hardbody radius x-axis component to compute + * + * @return current x-axis component to the deformed hardbody perimeter + */ + public T getXPrime(final T cosTheta) { + return scaleFactor.multiply(xm).add(scaleFactor.multiply(radius).multiply(cosTheta)); + } + + /** + * Get current y-axis component to the deformed hardbody perimeter. + * + * @param sinTheta sin of the angle determining deformed hardbody radius y-axis component to compute + * + * @return current y-axis component to the deformed hardbody perimeter + */ + public T getYPrime(final T sinTheta) { + return ym.add(radius.multiply(sinTheta)); + } + + /** + * Get current distance from the reference to the determined hardbody perimeter. + * + * @param xPrime current x-axis component to the deformed hardbody perimeter + * @param yPrime current y-axis component to the deformed hardbody perimeter + * + * @return current distance from the reference to the determined hardbody perimeter + */ + public T getRSquared(final T xPrime, final T yPrime) { + return xPrime.multiply(xPrime).add(yPrime.multiply(yPrime)); + } + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java new file mode 100644 index 0000000000..4ca812e2b8 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java @@ -0,0 +1,681 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.twod.Vector2D; +import org.hipparchus.linear.Array2DRowRealMatrix; +import org.hipparchus.linear.EigenDecompositionSymmetric; +import org.hipparchus.linear.LUDecomposition; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.frames.LOF; +import org.orekit.frames.LOFType; +import org.orekit.frames.Transform; +import org.orekit.frames.encounter.EncounterLOF; +import org.orekit.frames.encounter.EncounterLOFType; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.StateCovariance; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinates; + +/** + * Defines the encounter between two collision object at time of closest approach assuming a short-term encounter model . It + * uses the given {@link EncounterLOFType encounter frame type} to define the encounter. + *

        + * Both the primary and secondary collision object can be at the reference of the encounter frame, it is up to the user to + * choose. + *

        + * The "reference" object is the object considered at the reference of the given encounter frame while the "other" object is + * the one not placed at the reference. + *

        + * For example, if the user wants the primary to be at the reference of the default encounter frame, they will have to input + * data in the following manner: + *

        {@code
        + * final ShortTermEncounter2DDefinition encounter = new ShortTermEncounter2DDefinition(primaryOrbitAtTCA, primaryCovariance, primaryRadius, secondaryOrbitAtTCA, secondaryCovariance, secondaryRadius);
        + *  }
        + * 
        + * However, if the user wants to put the secondary at the reference and use the + * {@link org.orekit.frames.encounter.ValsecchiEncounterFrame Valsecchi encounter frame}, they will have to type : + *
        {@code
        + * final ShortTermEncounter2DDefinition encounter = new ShortTermEncounter2DDefinition(secondaryOrbitAtTCA, secondaryCovariance, secondaryRadius, primaryOrbitAtTCA, primaryCovariance, primaryRadius, EncounterLOFType.VALSECCHI_2003);
        + *  }
        + * 
        + * Note that in the current implementation, the shape of the collision objects is assumed to be a sphere. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class ShortTermEncounter2DDefinition { + + /** Default threshold below which values are considered equal to zero. */ + private static final double DEFAULT_ZERO_THRESHOLD = 1e-15; + + /** Default epsilon when checking covariance matrix symmetry. */ + private static final double DEFAULT_SYMMETRY_EPSILON = 1e-8; + + /** + * Time of closest approach. + *

        + * Commonly called TCA. + */ + private final AbsoluteDate tca; + + /** Reference collision object at time of closest approach. */ + private final Orbit referenceAtTCA; + + /** Reference collision object covariance matrix in its respective RTN frame. */ + private final StateCovariance referenceCovariance; + + /** Other collision object at time of closest approach. */ + private final Orbit otherAtTCA; + + /** Other collision object covariance matrix in its respective RTN frame. */ + private final StateCovariance otherCovariance; + + /** Combined radius (m). */ + private final double combinedRadius; + + /** Encounter local orbital frame to use. */ + private final EncounterLOF encounterFrame; + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param referenceRadius reference collision's equivalent sphere radius + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param otherRadius other collision's equivalent sphere radius + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public ShortTermEncounter2DDefinition(final Orbit referenceAtTCA, final StateCovariance referenceCovariance, + final double referenceRadius, final Orbit otherAtTCA, + final StateCovariance otherCovariance, final double otherRadius) { + this(referenceAtTCA, referenceCovariance, otherAtTCA, otherCovariance, referenceRadius + otherRadius); + } + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param combinedRadius combined radius (m) + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public ShortTermEncounter2DDefinition(final Orbit referenceAtTCA, final StateCovariance referenceCovariance, + final Orbit otherAtTCA, final StateCovariance otherCovariance, + final double combinedRadius) { + this(referenceAtTCA, referenceCovariance, otherAtTCA, otherCovariance, combinedRadius, EncounterLOFType.DEFAULT, + 1e-6); + } + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param referenceRadius reference collision's equivalent sphere radius + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param otherRadius other collision's equivalent sphere radius + * @param encounterFrameType type of encounter frame to use + * @param tcaTolerance tolerance on reference and other times of closest approach difference + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public ShortTermEncounter2DDefinition(final Orbit referenceAtTCA, final StateCovariance referenceCovariance, + final double referenceRadius, final Orbit otherAtTCA, + final StateCovariance otherCovariance, final double otherRadius, + final EncounterLOFType encounterFrameType, final double tcaTolerance) { + this(referenceAtTCA, referenceCovariance, otherAtTCA, otherCovariance, referenceRadius + otherRadius, + encounterFrameType, tcaTolerance); + } + + /** + * Constructor. + * + * @param referenceAtTCA reference collision object orbit at time of closest approach + * @param referenceCovariance reference collision object covariance matrix in its respective RTN frame + * @param otherAtTCA other collision object orbit at time of closest approach + * @param otherCovariance other collision object covariance matrix in its respective RTN frame + * @param combinedRadius combined radius (m) + * @param encounterFrameType type of encounter frame to use + * @param tcaTolerance tolerance on reference and other times of closest approach difference + * + * @throws OrekitException If both collision object spacecraft state don't have the same definition date. + */ + public ShortTermEncounter2DDefinition(final Orbit referenceAtTCA, final StateCovariance referenceCovariance, + final Orbit otherAtTCA, final StateCovariance otherCovariance, + final double combinedRadius, final EncounterLOFType encounterFrameType, + final double tcaTolerance) { + + if (referenceAtTCA.getDate().isCloseTo(otherAtTCA.getDate(), tcaTolerance)) { + + this.tca = referenceAtTCA.getDate(); + + this.referenceAtTCA = referenceAtTCA; + this.referenceCovariance = referenceCovariance; + + this.otherAtTCA = otherAtTCA; + this.otherCovariance = otherCovariance; + + this.combinedRadius = combinedRadius; + + this.encounterFrame = encounterFrameType.getFrame(otherAtTCA.getPVCoordinates()); + } else { + throw new OrekitException(OrekitMessages.DIFFERENT_TIME_OF_CLOSEST_APPROACH); + } + + } + + /** + * Compute the squared Mahalanobis distance. + * + * @param xm other collision object projected xm position onto the collision plane in the rotated encounter frame + * @param ym other collision object projected ym position onto the collision plane in the rotated encounter frame + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane + * + * @return squared Mahalanobis distance + */ + public static double computeSquaredMahalanobisDistance(final double xm, final double ym, + final double sigmaX, final double sigmaY) { + final Vector2D position = new Vector2D(xm, ym); + + final RealMatrix covariance = new Array2DRowRealMatrix(new double[][] { + { sigmaX * sigmaX, 0 }, + { 0, sigmaY * sigmaY } }); + + return computeSquaredMahalanobisDistance(position, covariance); + } + + /** + * Compute the squared Mahalanobis distance. + * + * @param otherPosition other collision object projected position onto the collision plane in the rotated encounter + * frame + * @param covarianceMatrix combined covariance matrix projected onto the collision plane and diagonalized + * + * @return squared Mahalanobis distance + */ + public static double computeSquaredMahalanobisDistance(final Vector2D otherPosition, final RealMatrix covarianceMatrix) { + + final RealMatrix covarianceMatrixInverse = new LUDecomposition(covarianceMatrix).getSolver().getInverse(); + + final RealMatrix otherPositionOnCollisionPlaneMatrix = new Array2DRowRealMatrix(otherPosition.toArray()); + + return otherPositionOnCollisionPlaneMatrix.transposeMultiply( + covarianceMatrixInverse.multiply(otherPositionOnCollisionPlaneMatrix)).getEntry(0, 0); + } + + /** + * Compute the other collision position and velocity relative to the reference collision object. Expressed in the + * reference collision object inertial frame. + * + * @return other collision position and velocity relative to the reference collision object, expressed in the reference + * collision object inertial frame. + */ + public PVCoordinates computeOtherRelativeToReferencePVInReferenceInertial() { + + // Extract reference inertial frame + final Frame referenceInertial = referenceAtTCA.getFrame(); + + // Get PVCoordinates in the same frame + final PVCoordinates referencePV = referenceAtTCA.getPVCoordinates(); + final PVCoordinates otherPVInReferenceInertial = otherAtTCA.getPVCoordinates(referenceInertial); + + // Create relative pv expressed in the reference inertial frame + final Vector3D relativePosition = otherPVInReferenceInertial.getPosition().subtract(referencePV.getPosition()); + final Vector3D relativeVelocity = otherPVInReferenceInertial.getVelocity().subtract(referencePV.getVelocity()); + + return new PVCoordinates(relativePosition, relativeVelocity); + } + + /** + * Compute the projection matrix from the reference collision object inertial frame to the collision plane. + *

        + * Note that this matrix will only rotate from the reference collision object inertial frame to the encounter frame and + * project onto the collision plane, this is only a rotation. + *

        + * + * @return projection matrix from the reference collision object inertial frame to the collision plane + */ + public RealMatrix computeReferenceInertialToCollisionPlaneProjectionMatrix() { + + // Create transform from reference inertial frame to encounter local orbital frame + final Transform referenceInertialToEncounterFrameTransform = + new Transform(tca, + computeReferenceInertialToReferenceTNWTransform(), + computeReferenceTNWToEncounterFrameTransform()); + + // Create rotation matrix from reference inertial frame to encounter local orbital frame + final RealMatrix referenceInertialToEncounterFrameRotationMatrix = new Array2DRowRealMatrix( + referenceInertialToEncounterFrameTransform.getRotation().getMatrix()); + + // Create projection matrix from encounter frame to collision plane + final RealMatrix encounterFrameToCollisionPlaneProjectionMatrix = encounterFrame.computeProjectionMatrix(); + + // Create projection matrix from reference inertial frame to collision plane + return encounterFrameToCollisionPlaneProjectionMatrix.multiply(referenceInertialToEncounterFrameRotationMatrix); + } + + /** + * Compute the combined covariance matrix diagonalized and projected onto the collision plane. + *

        + * Diagonalize projected positional covariance matrix in a specific manner to have + * σxx2 ≤ σyy2. + * + * @return combined covariance matrix diagonalized and projected onto the collision plane + */ + public RealMatrix computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix() { + final RealMatrix covariance = computeProjectedCombinedPositionalCovarianceMatrix(); + final EigenDecompositionSymmetric ed = new EigenDecompositionSymmetric(covariance, DEFAULT_SYMMETRY_EPSILON, false); + return ed.getD(); + } + + /** + * Compute the projected combined covariance matrix onto the collision plane. + * + * @return projected combined covariance matrix onto the collision plane + */ + public RealMatrix computeProjectedCombinedPositionalCovarianceMatrix() { + + // Compute the positional covariance in the encounter local orbital frame + final RealMatrix combinedPositionalCovarianceMatrixInEncounterFrame = + computeCombinedCovarianceInEncounterFrame().getMatrix().getSubMatrix(0, 2, 0, 2); + + // Project it onto the collision plane + return encounterFrame.projectOntoCollisionPlane(combinedPositionalCovarianceMatrixInEncounterFrame); + } + + /** + * Compute the combined covariance expressed in the encounter frame. + * + * @return combined covariance expressed in the encounter frame + */ + public StateCovariance computeCombinedCovarianceInEncounterFrame() { + return computeCombinedCovarianceInReferenceTNW().changeCovarianceFrame(referenceAtTCA, encounterFrame); + } + + /** + * Compute the other collision object {@link Vector2D position} projected onto the collision plane. + * + * @return other collision object position projected onto the collision plane + */ + public Vector2D computeOtherPositionInCollisionPlane() { + + // Express other in reference inertial + final PVCoordinates otherInReferenceInertial = otherAtTCA.getPVCoordinates(referenceAtTCA.getFrame()); + + // Express other in reference TNW local orbital frame + final PVCoordinates otherPVInReferenceTNW = + computeReferenceInertialToReferenceTNWTransform().transformPVCoordinates(otherInReferenceInertial); + + // Express other in encounter local orbital frame + final PVCoordinates otherPVInEncounterFrame = + computeReferenceTNWToEncounterFrameTransform().transformPVCoordinates( + otherPVInReferenceTNW); + + return encounterFrame.projectOntoCollisionPlane(otherPVInEncounterFrame.getPosition()); + + } + + /** + * Compute the other collision object {@link Vector2D position} in the rotated collision plane. + *

        + * Uses a default zero threshold of 1e-15. + *

        + * The coordinates are often noted xm and ym in probability of collision related papers. + *

        + *

        + * The mentioned rotation concerns the rotation that diagonalize the combined covariance matrix inside the collision + * plane. + *

        + * + * @return other collision object position in the rotated collision plane + */ + public Vector2D computeOtherPositionInRotatedCollisionPlane() { + return computeOtherPositionInRotatedCollisionPlane(DEFAULT_ZERO_THRESHOLD); + + } + + /** + * Compute the other collision object {@link Vector2D position} in the rotated collision plane. + *

        + * The coordinates are often noted xm and ym in probability of collision related papers. + *

        + * The mentioned rotation concerns the rotation that diagonalize the combined covariance matrix inside the collision + * plane. + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return other collision object position in the rotated collision plane + */ + public Vector2D computeOtherPositionInRotatedCollisionPlane(final double zeroThreshold) { + + // Project the other position onto the collision plane + final RealMatrix otherPositionInCollisionPlaneMatrix = + new Array2DRowRealMatrix(computeOtherPositionInCollisionPlane().toArray()); + + // Express other in the rotated collision plane + final RealMatrix otherPositionRotatedInCollisionPlane = + computeEncounterPlaneRotationMatrix(zeroThreshold).multiply(otherPositionInCollisionPlaneMatrix); + + return new Vector2D(otherPositionRotatedInCollisionPlane.getColumn(0)); + + } + + /** + * Compute the Encounter duration (s) evaluated using Coppola's formula described in : "COPPOLA, Vincent, et al. + * Evaluating the short encounter assumption of the probability of collision formula. 2012." + *

        + * This method is to be used to check the validity of the short-term encounter model. The user is expected to compare the + * computed duration with the orbital period from both objects and draw its own conclusions. + *

        + * It uses γ = 1e-16 as the resolution of a double is nearly 1e-16 so γ smaller than that are not meaningful to compute. + * + * @return encounter duration (s) evaluated using Coppola's formula + */ + public double computeCoppolaEncounterDuration() { + + // Default value for γ = 1e-16 + final double DEFAULT_ALPHA_C = 5.864; + + final RealMatrix combinedPositionalCovarianceMatrix = computeCombinedCovarianceInEncounterFrame() + .getMatrix().getSubMatrix(0, 2, 0, 2); + + // Extract off-plane cross-term matrix + final RealMatrix projectionMatrix = encounterFrame.computeProjectionMatrix(); + final RealMatrix axisNormalToCollisionPlane = + new Array2DRowRealMatrix(encounterFrame.getAxisNormalToCollisionPlane().toArray()); + final RealMatrix offPlaneCrossTermMatrix = + projectionMatrix.multiply(combinedPositionalCovarianceMatrix.multiply(axisNormalToCollisionPlane)); + + // Covariance sub-matrix of the in-plane terms + final RealMatrix probabilityDensity = + encounterFrame.projectOntoCollisionPlane(combinedPositionalCovarianceMatrix); + final RealMatrix probabilityDensityInverse = + new LUDecomposition(probabilityDensity).getSolver().getInverse(); + + // Recurrent term in Coppola's paper : bᵀb + final RealMatrix b = offPlaneCrossTermMatrix.transposeMultiply(probabilityDensityInverse).transpose(); + final double recurrentTerm = b.multiplyTransposed(b).getEntry(0, 0); + + // Position uncertainty normal to collision plane + final double sigmaSqNormalToPlan = axisNormalToCollisionPlane.transposeMultiply( + combinedPositionalCovarianceMatrix.multiply(axisNormalToCollisionPlane)).getEntry(0, 0); + final double sigmaV = FastMath.sqrt( + sigmaSqNormalToPlan - b.multiplyTransposed(offPlaneCrossTermMatrix).getEntry(0, 0)); + + final double relativeVelocity = computeOtherRelativeToReferencePVInReferenceInertial().getVelocity().getNorm(); + + return (2 * FastMath.sqrt(2) * DEFAULT_ALPHA_C * sigmaV + combinedRadius * ( + FastMath.sqrt(1 + recurrentTerm) + FastMath.sqrt(recurrentTerm))) / relativeVelocity; + } + + /** + * Compute the miss distance at time of closest approach. + * + * @return miss distance + */ + public double computeMissDistance() { + + // Get positions expressed in the same frame at time of closest approach + final Vector3D referencePositionAtTCA = referenceAtTCA.getPosition(); + final Vector3D otherPositionAtTCA = otherAtTCA.getPosition(referenceAtTCA.getFrame()); + + // Compute relative position + final Vector3D relativePosition = otherPositionAtTCA.subtract(referencePositionAtTCA); + + return relativePosition.getNorm(); + } + + /** + * Compute the Mahalanobis distance computed with the other collision object projected onto the collision plane (commonly + * called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix is + * diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + *

        + * Uses a default zero threshold of 1e-15 for the computation of the diagonalizing of the projected covariance matrix. + * + * @return Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public double computeMahalanobisDistance() { + return computeMahalanobisDistance(DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the Mahalanobis distance computed with the other collision object projected onto the collision plane (commonly + * called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix is + * diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public double computeMahalanobisDistance(final double zeroThreshold) { + return FastMath.sqrt(computeSquaredMahalanobisDistance(zeroThreshold)); + } + + /** + * Compute the squared Mahalanobis distance computed with the other collision object projected onto the collision plane + * (commonly called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix + * is diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + *

        + * Uses a default zero threshold of 1e-15 for the computation of the diagonalizing of the projected covariance matrix. + * + * @return squared Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public double computeSquaredMahalanobisDistance() { + return computeSquaredMahalanobisDistance(DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the squared Mahalanobis distance computed with the other collision object projected onto the collision plane + * (commonly called B-Plane) and expressed in the rotated encounter frame (frame in which the combined covariance matrix + * is diagonalized, see {@link #computeEncounterPlaneRotationMatrix(double)} for more details). + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return squared Mahalanobis distance between the reference and other collision object + * + * @see Mahalanobis distance + */ + public double computeSquaredMahalanobisDistance(final double zeroThreshold) { + + final RealMatrix otherPositionAfterRotationInCollisionPlane = + new Array2DRowRealMatrix(computeOtherPositionInRotatedCollisionPlane(zeroThreshold).toArray()); + + final RealMatrix inverseCovarianceMatrix = + new LUDecomposition(computeProjectedAndDiagonalizedCombinedPositionalCovarianceMatrix()).getSolver() + .getInverse(); + + return otherPositionAfterRotationInCollisionPlane.transpose().multiply( + inverseCovarianceMatrix.multiply(otherPositionAfterRotationInCollisionPlane)).getEntry(0, 0); + } + + /** + * Takes both covariance matrices (expressed in their respective RTN local orbital frame) from reference and other + * collision object with which this instance was created and sum them in the reference collision object TNW local orbital + * frame. + * + * @return combined covariance matrix expressed in the reference collision object TNW local orbital frame + */ + public StateCovariance computeCombinedCovarianceInReferenceTNW() { + + // Express reference covariance in reference TNW local orbital frame + final RealMatrix referenceCovarianceMatrixInTNW = + referenceCovariance.changeCovarianceFrame(referenceAtTCA, LOFType.TNW_INERTIAL).getMatrix(); + + // Express other covariance in reference inertial frame + final RealMatrix otherCovarianceMatrixInReferenceInertial = + otherCovariance.changeCovarianceFrame(otherAtTCA, referenceAtTCA.getFrame()).getMatrix(); + + final StateCovariance otherCovarianceInReferenceInertial = new StateCovariance( + otherCovarianceMatrixInReferenceInertial, tca, referenceAtTCA.getFrame(), + OrbitType.CARTESIAN, PositionAngleType.MEAN); + + // Express other covariance in reference TNW local orbital frame + final RealMatrix otherCovarianceMatrixInReferenceTNW = otherCovarianceInReferenceInertial.changeCovarianceFrame( + referenceAtTCA, LOFType.TNW_INERTIAL).getMatrix(); + + // Return the combined covariance expressed in the reference TNW local orbital frame + return new StateCovariance(referenceCovarianceMatrixInTNW.add(otherCovarianceMatrixInReferenceTNW), tca, + LOFType.TNW_INERTIAL); + } + + /** + * Compute the {@link Transform transform} from the reference collision object inertial frame of reference to its TNW + * local orbital frame. + * + * @return transform from the reference collision object inertial frame of reference to its TNW local orbital frame + */ + private Transform computeReferenceInertialToReferenceTNWTransform() { + return LOFType.TNW.transformFromInertial(tca, referenceAtTCA.getPVCoordinates()); + } + + /** + * Compute the {@link Transform transform} from the reference collision object TNW local orbital frame to the encounter + * frame. + * + * @return transform from the reference collision object TNW local orbital frame to the encounter frame + */ + private Transform computeReferenceTNWToEncounterFrameTransform() { + return LOF.transformFromLOFInToLOFOut(LOFType.TNW_INERTIAL, encounterFrame, tca, + referenceAtTCA.getPVCoordinates()); + } + + /** + * Compute the rotation matrix that diagonalize the combined positional covariance matrix projected onto the collision + * plane. + * + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return rotation matrix that diagonalize the combined covariance matrix projected onto the collision plane + */ + private RealMatrix computeEncounterPlaneRotationMatrix(final double zeroThreshold) { + + final RealMatrix combinedCovarianceMatrixInEncounterFrame = + computeCombinedCovarianceInEncounterFrame().getMatrix(); + + final RealMatrix combinedPositionalCovarianceMatrixProjectedOntoBPlane = + encounterFrame.projectOntoCollisionPlane( + combinedCovarianceMatrixInEncounterFrame.getSubMatrix(0, 2, 0, 2)); + + final double sigmaXSquared = combinedPositionalCovarianceMatrixProjectedOntoBPlane.getEntry(0, 0); + final double sigmaYSquared = combinedPositionalCovarianceMatrixProjectedOntoBPlane.getEntry(1, 1); + final double crossTerm = combinedPositionalCovarianceMatrixProjectedOntoBPlane.getEntry(0, 1); + final double correlation = crossTerm / (FastMath.sqrt(sigmaXSquared * sigmaYSquared)); + + // If the matrix is not initially diagonalized + final double theta; + if (FastMath.abs(crossTerm) > zeroThreshold) { + final double recurrentTerm = (sigmaYSquared - sigmaXSquared) / (2 * crossTerm); + theta = FastMath.atan( + recurrentTerm - FastMath.signum(correlation) * FastMath.sqrt(1 + FastMath.pow(recurrentTerm, 2))); + } + // Else, the matrix is already diagonalized + else { + // Rotation in order to have sigmaXSquared < sigmaYSquared + if (sigmaXSquared - sigmaYSquared > 0) { + theta = MathUtils.SEMI_PI; + } + // Else, there is no need for a rotation + else { + theta = 0; + } + } + + final double[][] collisionPlaneRotationMatrixData = { { FastMath.cos(theta), FastMath.sin(theta) }, + { -FastMath.sin(theta), FastMath.cos(theta) } }; + + return new Array2DRowRealMatrix(collisionPlaneRotationMatrixData); + } + + /** + * Get the Time of Closest Approach. + *

        + * Commonly called TCA. + * + * @return time of closest approach + */ + public AbsoluteDate getTca() { + return tca; + } + + /** Get reference's orbit at time of closest approach. + * @return reference's orbit at time of closest approach + */ + public Orbit getReferenceAtTCA() { + return referenceAtTCA; + } + + /** Get other's orbit at time of closest approach. + * @return other's orbit at time of closest approach + */ + public Orbit getOtherAtTCA() { + return otherAtTCA; + } + + /** Get reference's covariance. + * @return reference's covariance + */ + public StateCovariance getReferenceCovariance() { + return referenceCovariance; + } + + /** Get other's covariance. + * @return other's covariance + */ + public StateCovariance getOtherCovariance() { + return otherCovariance; + } + + /** Get combined radius. + * @return combined radius (m) + */ + public double getCombinedRadius() { + return combinedRadius; + } + + /** Get encounter local orbital frame. + * @return encounter local orbital frame + */ + public EncounterLOF getEncounterFrame() { + return encounterFrame; + } + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethod.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethod.java new file mode 100644 index 0000000000..fbd897190e --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethod.java @@ -0,0 +1,355 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.files.ccsds.ndm.cdm.Cdm; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.FieldStateCovariance; +import org.orekit.propagation.StateCovariance; +import org.orekit.ssa.metrics.FieldProbabilityOfCollision; +import org.orekit.ssa.metrics.ProbabilityOfCollision; + +/** + * Interface common to all short-term encounter probability of collision computing methods. + *

        + * All the methods implementing this interface will at least assume the followings : + *

          + *
        • Short term encounter leading to a linear relative motion.
        • + *
        • Spherical collision object.
        • + *
        • Uncorrelated positional covariance.
        • + *
        • Gaussian distribution of the position uncertainties.
        • + *
        • Deterministic velocity i.e. no velocity uncertainties.
        • + *
        + * As listed in the assumptions, methods implementing this interface are to be used in short encounter, + * meaning that there must be a high relative velocity. For ease of computation, the resulting swept volume + * is extended to infinity so that the integral becomes bivariate instead of trivariate (conservative hypothesis). + *

        + * Consequently and if we consider Earth, methods implementing this interface are recommended for + * collision happening in Low/Medium Earth Orbit (LEO and MEO) but are not recommended for collision + * happening in Geostationary Earth Orbit (GEO). + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public interface ShortTermEncounter2DPOCMethod { + + /** Threshold below which values are considered equal to zero. */ + double DEFAULT_ZERO_THRESHOLD = 1e-15; + + /** + * Compute the probability of collision using a Conjunction Data Message (CDM). + * + * @param cdm conjunction data message input + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * + * @return probability of collision + */ + default ProbabilityOfCollision compute(Cdm cdm, double primaryRadius, double secondaryRadius) { + return compute(cdm, primaryRadius + secondaryRadius); + } + + /** + * Compute the probability of collision using a Conjunction Data Message (CDM). + * + * @param cdm conjunction data message input + * @param combinedRadius combined radius (m) + * + * @return probability of collision + */ + ProbabilityOfCollision compute(Cdm cdm, double combinedRadius); + + /** + * Compute the probability of collision using a Conjunction Data Message (CDM). + * + * @param cdm conjunction data message input + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * @param type of the field elements + * + * @return probability of collision + */ + default > FieldProbabilityOfCollision compute(Cdm cdm, + T primaryRadius, + T secondaryRadius) { + return compute(cdm, primaryRadius.add(secondaryRadius)); + } + + /** + * Compute the probability of collision using a Conjunction Data Message (CDM). + * + * @param cdm conjunction data message input + * @param combinedRadius combined radius (m) + * @param type of the field elements + * + * @return probability of collision + */ + default > FieldProbabilityOfCollision compute(Cdm cdm, + T combinedRadius) { + return compute(cdm, combinedRadius, DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the probability of collision using a Conjunction Data Message (CDM). + * + * @param cdm conjunction data message input + * @param combinedRadius combined radius (m) + * @param zeroThreshold threshold below which values are considered equal to zero + * @param type of the field elements + * + * @return probability of collision + */ + > FieldProbabilityOfCollision compute(Cdm cdm, T combinedRadius, + double zeroThreshold); + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * + * @return probability of collision + */ + default ProbabilityOfCollision compute(Orbit primaryAtTCA, + StateCovariance primaryCovariance, + double primaryRadius, + Orbit secondaryAtTCA, + StateCovariance secondaryCovariance, + double secondaryRadius) { + return compute(primaryAtTCA, primaryCovariance, secondaryAtTCA, secondaryCovariance, + primaryRadius + secondaryRadius); + } + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param combinedRadius combined radius (m) + * + * @return probability of collision + */ + default ProbabilityOfCollision compute(Orbit primaryAtTCA, + StateCovariance primaryCovariance, + Orbit secondaryAtTCA, + StateCovariance secondaryCovariance, + double combinedRadius) { + return compute(primaryAtTCA, primaryCovariance, secondaryAtTCA, secondaryCovariance, + combinedRadius, DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param combinedRadius combined radius (m) + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return probability of collision + */ + ProbabilityOfCollision compute(Orbit primaryAtTCA, StateCovariance primaryCovariance, + Orbit secondaryAtTCA, StateCovariance secondaryCovariance, + double combinedRadius, double zeroThreshold); + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param primaryRadius primary collision object equivalent sphere radius (m) + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param secondaryRadius secondary collision object equivalent sphere radius (m) + * @param type of the field elements + * + * @return probability of collision + */ + default > FieldProbabilityOfCollision compute(FieldOrbit primaryAtTCA, + FieldStateCovariance primaryCovariance, + T primaryRadius, + FieldOrbit secondaryAtTCA, + FieldStateCovariance secondaryCovariance, + T secondaryRadius) { + return compute(primaryAtTCA, primaryCovariance, secondaryAtTCA, secondaryCovariance, + primaryRadius.add(secondaryRadius)); + } + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param combinedRadius secondary collision object equivalent sphere radius (m) + * @param type of the field elements + * + * @return probability of collision + */ + default > FieldProbabilityOfCollision compute(FieldOrbit primaryAtTCA, + FieldStateCovariance primaryCovariance, + FieldOrbit secondaryAtTCA, + FieldStateCovariance secondaryCovariance, + T combinedRadius) { + return compute(primaryAtTCA, primaryCovariance, secondaryAtTCA, secondaryCovariance, + combinedRadius, DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the probability of collision using parameters necessary for creating a + * {@link ShortTermEncounter2DDefinition collision definition} instance. + * + * @param primaryAtTCA primary collision object spacecraft state at time of closest approach + * @param primaryCovariance primary collision object covariance + * @param secondaryAtTCA secondary collision object spacecraft state at time of closest approach + * @param secondaryCovariance secondary collision object covariance + * @param combinedRadius combined radius (m) + * @param zeroThreshold threshold below which values are considered equal to zero + * @param type of the field elements + * + * @return probability of collision + */ + > FieldProbabilityOfCollision compute(FieldOrbit primaryAtTCA, + FieldStateCovariance primaryCovariance, + FieldOrbit secondaryAtTCA, + FieldStateCovariance secondaryCovariance, + T combinedRadius, + double zeroThreshold); + + /** + * Compute the probability of collision using given collision definition. + * + * @param encounter encounter definition between a primary and a secondary collision object + * + * @return probability of collision + */ + default ProbabilityOfCollision compute(ShortTermEncounter2DDefinition encounter) { + return compute(encounter, DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the probability of collision using given collision definition. + * + * @param encounter encounter definition between a primary and a secondary collision object + * @param zeroThreshold threshold below which values are considered equal to zero + * + * @return probability of collision + */ + ProbabilityOfCollision compute(ShortTermEncounter2DDefinition encounter, double zeroThreshold); + + /** + * Compute the probability of collision using given collision definition. + * + * @param encounter encounter definition between a primary and a secondary collision object + * @param type of the field elements + * + * @return probability of collision + */ + default > FieldProbabilityOfCollision compute( + FieldShortTermEncounter2DDefinition encounter) { + return compute(encounter, DEFAULT_ZERO_THRESHOLD); + } + + /** + * Compute the probability of collision using given collision definition. + * + * @param encounter encounter definition between a primary and a secondary collision object + * @param zeroThreshold threshold below which values are considered equal to zero + * @param type of the field elements + * + * @return probability of collision + */ + > FieldProbabilityOfCollision compute( + FieldShortTermEncounter2DDefinition encounter, + double zeroThreshold); + + /** + * Compute the probability of collision using arguments specific to the rotated encounter frame. + *

        + * The rotated encounter frame is define by the initial encounter frame (defined in + * {@link ShortTermEncounter2DDefinition}) rotated by the rotation matrix which is used to diagonalize the combined + * covariance matrix. + *

        + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * + * @return probability of collision + */ + ProbabilityOfCollision compute(double xm, double ym, double sigmaX, double sigmaY, double radius); + + /** + * Compute the probability of collision using arguments specific to the rotated encounter frame. + *

        + * The rotated encounter frame is define by the initial encounter frame (defined in + * {@link ShortTermEncounter2DDefinition}) rotated by the rotation matrix which is used to diagonalize the combined + * covariance matrix. + *

        + * + * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis + * (m) + * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis + * (m) + * @param sigmaX square root of the x-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param sigmaY square root of the y-axis eigen value of the diagonalized combined covariance matrix projected onto the + * collision plane (m) + * @param radius sum of primary and secondary collision object equivalent sphere radii (m) + * @param type of the field elements + * + * @return probability of collision + */ + > FieldProbabilityOfCollision compute(T xm, T ym, T sigmaX, T sigmaY, T radius); + + /** Get type of the method. + * @return type of the method + */ + ShortTermEncounter2DPOCMethodType getType(); + + /** Get name of the method. + * @return name of the method + */ + String getName(); + + /** Get flag that defines if the method is a maximum probability of collision computing method. + * @return flag that defines if the method is a maximum probability of collision computing method + */ + boolean isAMaximumProbabilityOfCollisionMethod(); +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodType.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodType.java new file mode 100644 index 0000000000..7fb7e23ae0 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodType.java @@ -0,0 +1,170 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.collision.shorttermencounter.probability.twod; + +import org.orekit.files.ccsds.definitions.PocMethodType; + +/** + * This enum stores every probability of collision computing method using the short-term encounter model available in + * Orekit. + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public enum ShortTermEncounter2DPOCMethodType { + + /** + * Probability of collision computing method described in :"SERRA, Romain, ARZELIER, Denis, JOLDES, Mioara, et al. Fast + * and accurate computation of orbital collision probability for short-term encounters. Journal of Guidance, Control, and + * Dynamics, 2016, vol. 39, no 5, p. 1009-1021." + */ + LAAS_2015 { + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethod getMethod() { + return new Laas2015(); + } + + /** + * {@inheritDoc} + *

        + * This method is currently under approval by the SANA registry so its return type shall be updated once approved. + */ + @Override + public PocMethodType getCCSDSType() { + return null; + } + }, + + /** + * Probability of collision computing method described in :"S. Alfano. A numerical implementation of spherical objet + * collision probability. Journal of Astronautical Sciences, 53(1), January-March 2005." + */ + ALFANO_2005 { + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethod getMethod() { + return new Alfano2005(); + } + + /** {@inheritDoc} */ + @Override + public PocMethodType getCCSDSType() { + return PocMethodType.ALFANO_2005; + } + }, + + /** + * Probability of collision computing method described in :"PATERA, Russell P. Calculating collision probability for + * arbitrary space vehicle shapes via numerical quadrature. Journal of guidance, control, and dynamics, 2005, vol. 28, no + * 6, p. 1326-1328." + */ + PATERA_2005 { + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethod getMethod() { + return new Patera2005(); + } + + /** {@inheritDoc} */ + @Override + public PocMethodType getCCSDSType() { + return PocMethodType.PATERA_2005; + } + }, + + /** + * Probability of collision computing method described in :"Kyle Alfriend, Maruthi Akella, Joseph Frisbee, James Foster, + * Deok-Jin Lee, and Matthew Wilkins. Probability of ProbabilityOfCollision Error Analysis. Space Debris, 1(1):21–35, + * 1999." + */ + ALFRIEND_1999 { + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethod getMethod() { + return new Alfriend1999(); + } + + /** {@inheritDoc} */ + @Override + public PocMethodType getCCSDSType() { + return PocMethodType.ALFRIEND_1999; + } + }, + + /** + * Probability of collision computing method described in :"Kyle Alfriend, Maruthi Akella, Joseph Frisbee, James Foster, + * Deok-Jin Lee, and Matthew Wilkins. Probability of ProbabilityOfCollision Error Analysis. Space Debris, 1(1):21–35, + * 1999." + */ + ALFRIEND_1999_MAX { + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethod getMethod() { + return new Alfriend1999Max(); + } + + /** {@inheritDoc} */ + @Override + public PocMethodType getCCSDSType() { + return null; + } + }, + + /** + * Probability of collision computing method described in :
        "Chan,K. “Collision Probability Analyses for Earth + * Orbiting Satellites.” In Space Cooperation into the 21st Century: 7th AAS/JRS/CSA Symposium, International Space + * Conference of Pacific-Basin Societies (ISCOPS; formerly PISSTA) (July 15-18, 1997, Nagasaki, Japan), edited by Peter + * M. Bainum, et al., 1033-1048. Advances in the Astronautical Sciences Series 96. San Diego, California: Univelt, 1997. + * (Zeroth order analytical expression)". + *

        + * This method is also described in depth in :
        "CHAN, F. Kenneth, et al. Spacecraft collision probability. El + * Segundo, CA : Aerospace Press, 2008." + */ + CHAN_1997 { + /** {@inheritDoc} */ + @Override + public ShortTermEncounter2DPOCMethod getMethod() { + return new Chan1997(); + } + + /** {@inheritDoc} */ + @Override + public PocMethodType getCCSDSType() { + return PocMethodType.CHAN_1997; + } + }; + + /** + * Get the CCSDS type if used by the SANA registry. + *

        + * Note that it may return a null if the method is not used by the SANA registry. + *

        + * The list of available methods is available on the SANA website. + * + * @return probability of collision method + * + * @see SANA CDM Collision Probability Methods + */ + public abstract PocMethodType getCCSDSType(); + + /** Get probability of collision method. + * @return probability of collision method + */ + public abstract ShortTermEncounter2DPOCMethod getMethod(); + +} diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/package-info.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/package-info.java new file mode 100644 index 0000000000..fa2677ad04 --- /dev/null +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/package-info.java @@ -0,0 +1,27 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Package specific to calculus assuming a 2D short-term encounter model. + *

        + * Note that in the current implementation, the shape of the collision objects is assumed to be a sphere. + * + * @author Vincent Cucchietti + * @since 12.0 + */ + +package org.orekit.ssa.collision.shorttermencounter.probability.twod; diff --git a/src/main/java/org/orekit/ssa/metrics/FieldProbabilityOfCollision.java b/src/main/java/org/orekit/ssa/metrics/FieldProbabilityOfCollision.java new file mode 100644 index 0000000000..66fe02aaec --- /dev/null +++ b/src/main/java/org/orekit/ssa/metrics/FieldProbabilityOfCollision.java @@ -0,0 +1,154 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.metrics; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.MathUtils; +import org.orekit.ssa.collision.shorttermencounter.probability.twod.Alfriend1999Max; +import org.orekit.ssa.collision.shorttermencounter.probability.twod.Laas2015; + +/** + * Container for values relative to the probability of collision : + *

          + *
        • Value of the probability of collision.
        • + *
        • Name of the method with which it was computed.
        • + *
        • Upper and lower limit of the value if the method provides them (such as {@link Laas2015} for example).
        • + *
        • Flag defining if the probability was maximized in any way (such as {@link Alfriend1999Max} for example).
        • + *
        + * + * @author Vincent Cucchietti + * @since 12.0 + * @param type of the field elements + */ +public class FieldProbabilityOfCollision> { + + /** Value of the probability of collision. */ + private final T value; + + /** + * Lower limit of the probability of collision. + *

        + * 0 by default. + */ + private final T lowerLimit; + + /** + * Upper limit of the probability of collision. + *

        + * 0 by default. + */ + private final T upperLimit; + + /** Name of the probability computing method with which this probability was calculated. */ + private final String probabilityOfCollisionMethodName; + + /** + * Defines if this probability of collision can be considered a maximum probability of collision. + *

        + * It depends on what method was used to compute this probability. + *

        + * False by default. + */ + private final boolean isMaxProbability; + + /** + * Constructor with default values of 0 for the upper/lower limits and default false flag for maximum probability. + * + * @param value value of the probability of collision + * @param probabilityOfCollisionMethodName name of the probability computing method with which this probability was + * computed + */ + public FieldProbabilityOfCollision(final T value, final String probabilityOfCollisionMethodName) { + this(value, probabilityOfCollisionMethodName, false); + } + + /** + * Constructor with default values of 0 for the upper and lower limits. + * + * @param value value of the probability of collision + * @param probabilityOfCollisionMethodName name of the probability computing method with which this probability was + * computed + * @param isMaxProbability flag defining if it has been computed using a maximum probability of collision method + */ + public FieldProbabilityOfCollision(final T value, final String probabilityOfCollisionMethodName, + final boolean isMaxProbability) { + this(value, value.getField().getZero(), value.getField().getZero(), probabilityOfCollisionMethodName, + isMaxProbability); + } + + /** + * Constructor. + * + * @param value value of the probability of collision + * @param lowerLimit lower limit of the probability of collision + * @param upperLimit upper limit of the probability of collision + * @param probabilityOfCollisionMethodName name of the probability computing method with which this probability was + * computed + * @param isMaxProbability flag indicating if this method computes a maximum probability of collision + */ + public FieldProbabilityOfCollision(final T value, final T lowerLimit, final T upperLimit, + final String probabilityOfCollisionMethodName, final boolean isMaxProbability) { + + // Check that inputs are valid + MathUtils.checkRangeInclusive(value.getReal(), 0, 1); + MathUtils.checkRangeInclusive(lowerLimit.getReal(), 0, 1); + MathUtils.checkRangeInclusive(upperLimit.getReal(), 0, 1); + + // Initialization + this.value = value; + this.lowerLimit = lowerLimit; + this.upperLimit = upperLimit; + this.probabilityOfCollisionMethodName = probabilityOfCollisionMethodName; + this.isMaxProbability = isMaxProbability; + } + + /** Get value of the probability of collision. + * @return value of the probability of collision + */ + public T getValue() { + return value; + } + + /** Get lower limit of the probability of collision value. + * @return lower limit of the probability of collision value, 0 by default + */ + public T getLowerLimit() { + return lowerLimit; + } + + /** Get upper limit of the probability of collision value. + * @return upper limit of the probability of collision value, 0 by default + */ + public T getUpperLimit() { + return upperLimit; + } + + /** Get name of the probability computing method with which this probability was computed. + * @return name of the probability computing method with which this probability was computed + */ + public String getProbabilityOfCollisionMethodName() { + return probabilityOfCollisionMethodName; + } + + /** Get flag that defines if this probability of collision can be considered a maximum probability of collision. + * @return flag that defines if this probability of collision can be considered a maximum probability of collision + */ + public boolean isMaxProbability() { + return isMaxProbability; + } + +} diff --git a/src/main/java/org/orekit/ssa/metrics/ProbabilityOfCollision.java b/src/main/java/org/orekit/ssa/metrics/ProbabilityOfCollision.java new file mode 100644 index 0000000000..5f29d2562a --- /dev/null +++ b/src/main/java/org/orekit/ssa/metrics/ProbabilityOfCollision.java @@ -0,0 +1,151 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.ssa.metrics; + +import org.hipparchus.util.MathUtils; +import org.orekit.ssa.collision.shorttermencounter.probability.twod.Alfriend1999Max; +import org.orekit.ssa.collision.shorttermencounter.probability.twod.Laas2015; + +/** + * Container for values relative to the probability of collision : + *

          + *
        • Value of the probability of collision.
        • + *
        • Name of the method with which it was computed.
        • + *
        • Upper and lower limit of the value if the method provides them (such as {@link Laas2015} for example).
        • + *
        • Flag defining if the probability was maximized in any way (such as {@link Alfriend1999Max} for example).
        • + *
        + * + * @author Vincent Cucchietti + * @since 12.0 + */ +public class ProbabilityOfCollision { + + /** Value of the probability of collision. */ + private final double value; + + /** + * Lower limit of the probability of collision. + *

        + * 0 by default. + */ + private final double lowerLimit; + + /** + * Upper limit of the probability of collision. + *

        + * 0 by default. + */ + private final double upperLimit; + + /** Name of the probability computing method with which this probability was calculated. */ + private final String probabilityOfCollisionMethodName; + + /** + * Defines if this probability of collision can be considered a maximum probability of collision. + *

        + * It depends on what method was used to compute this probability. + *

        + * False by default. + */ + private final boolean isMaxProbability; + + /** + * Constructor with default values of 0 for the upper/lower limits and default false flag for maximum probability. + * + * @param value value of the probability of collision + * @param probabilityOfCollisionMethodName name of the probability computing method with which this probability was + * computed + */ + public ProbabilityOfCollision(final double value, final String probabilityOfCollisionMethodName) { + this(value, 0., 0., probabilityOfCollisionMethodName, false); + } + + /** + * Constructor with default values of 0 for the upper and lower limits. + * + * @param value value of the probability of collision + * @param probabilityOfCollisionMethodName name of the probability computing method with which this probability was + * computed + * @param isMaxProbability flag defining if it has been computed using a maximum probability of collision method + */ + public ProbabilityOfCollision(final double value, final String probabilityOfCollisionMethodName, + final boolean isMaxProbability) { + this(value, 0., 0., probabilityOfCollisionMethodName, isMaxProbability); + } + + /** + * Constructor. + * + * @param value value of the probability of collision + * @param lowerLimit lower limit of the probability of collision + * @param upperLimit upper limit of the probability of collision + * @param probabilityOfCollisionMethodName name of the probability computing method with which this probability was + * computed + * @param isMaxProbability flag indicating if this method computes a maximum probability of collision + */ + public ProbabilityOfCollision(final double value, final double lowerLimit, final double upperLimit, + final String probabilityOfCollisionMethodName, final boolean isMaxProbability) { + + // Check that inputs are valid + MathUtils.checkRangeInclusive(value, 0, 1); + MathUtils.checkRangeInclusive(lowerLimit, 0, 1); + MathUtils.checkRangeInclusive(upperLimit, 0, 1); + + // initialization + this.value = value; + this.lowerLimit = lowerLimit; + this.upperLimit = upperLimit; + this.probabilityOfCollisionMethodName = probabilityOfCollisionMethodName; + this.isMaxProbability = isMaxProbability; + } + + /** Get value of the probability of collision. + * @return value of the probability of collision + */ + public double getValue() { + return value; + } + + /** Get lower limit of the probability of collision value. + * @return lower limit of the probability of collision value, 0 by default + */ + public double getLowerLimit() { + return lowerLimit; + } + + /** Get upper limit of the probability of collision value. + * @return upper limit of the probability of collision value, 0 by default + */ + public double getUpperLimit() { + return upperLimit; + } + + /** Get name of the probability computing method with which this probability was computed. + * @return name of the probability computing method with which this probability was computed + */ + public String getProbabilityOfCollisionMethodName() { + return probabilityOfCollisionMethodName; + } + + /** Get flag that defines if this probability of collision can be considered a maximum probability of collision. + * @return flag that defines if this probability of collision can be considered a maximum probability of collision + */ + public boolean isMaxProbability() { + return isMaxProbability; + } + +} diff --git a/src/main/java/org/orekit/ssa/metrics/package-info.java b/src/main/java/org/orekit/ssa/metrics/package-info.java new file mode 100644 index 0000000000..d01065c586 --- /dev/null +++ b/src/main/java/org/orekit/ssa/metrics/package-info.java @@ -0,0 +1,25 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package stores space situational awareness evaluation metrics. + * + * @author Vincent Cucchietti + * @since 12.0 + */ + +package org.orekit.ssa.metrics; diff --git a/src/main/java/org/orekit/ssa/package-info.java b/src/main/java/org/orekit/ssa/package-info.java new file mode 100644 index 0000000000..05dfe9ee4d --- /dev/null +++ b/src/main/java/org/orekit/ssa/package-info.java @@ -0,0 +1,25 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Top level package for various space situational awareness fields as well as associated evaluation metrics. + * + * @author Vincent Cucchietti + * @since 12.0 + */ + +package org.orekit.ssa; diff --git a/src/main/java/org/orekit/time/AGILeapSecondFilesLoader.java b/src/main/java/org/orekit/time/AGILeapSecondFilesLoader.java index c297c66e05..b4dc1284a1 100644 --- a/src/main/java/org/orekit/time/AGILeapSecondFilesLoader.java +++ b/src/main/java/org/orekit/time/AGILeapSecondFilesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/AbsoluteDate.java b/src/main/java/org/orekit/time/AbsoluteDate.java index ebcf439036..e67b83250a 100644 --- a/src/main/java/org/orekit/time/AbsoluteDate.java +++ b/src/main/java/org/orekit/time/AbsoluteDate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.time; import java.io.Serializable; +import java.time.Instant; import java.util.Date; import java.util.TimeZone; @@ -211,11 +212,11 @@ public class AbsoluteDate DataContext.getDefault().getTimeScales().getGlonassEpoch(); /** J2000.0 Reference epoch: 2000-01-01T12:00:00 Terrestrial Time (not UTC). - * @see #createJulianEpoch(double) - * @see #createBesselianEpoch(double) * *

        This constant uses the {@link DataContext#getDefault() default data context}. * + * @see #createJulianEpoch(double) + * @see #createBesselianEpoch(double) * @see TimeScales#getJ2000Epoch() */ @DefaultDataContext @@ -417,6 +418,18 @@ public AbsoluteDate(final Date location, final TimeScale timeScale) { timeScale); } + /** Build an instance from an {@link Instant instant} in a {@link TimeScale time scale}. + * @param instant instant in the time scale + * @param timeScale time scale + * @since 12.0 + */ + public AbsoluteDate(final Instant instant, final TimeScale timeScale) { + this(new DateComponents(DateComponents.JAVA_EPOCH, + (int) (instant.getEpochSecond() / 86400l)), + instantToTimeComponents(instant), + timeScale); + } + /** Build an instance from an elapsed duration since to another instant. *

        It is important to note that the elapsed duration is not * the difference between two readings on a time scale. As an example, @@ -501,6 +514,15 @@ private static TimeComponents millisToTimeComponents(final int millisInDay) { return new TimeComponents(millisInDay / 1000, 0.001 * (millisInDay % 1000)); } + /** Extract time components from an instant within the day. + * @param instant instant to extract the number of seconds within the day + * @return time components + */ + private static TimeComponents instantToTimeComponents(final Instant instant) { + final int secInDay = (int) (instant.getEpochSecond() % 86400l); + return new TimeComponents(secInDay, 1.0e-9 * instant.getNano()); + } + /** Get the reference epoch in seconds from 2000-01-01T12:00:00 TAI. *

        * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}). diff --git a/src/main/java/org/orekit/time/AbstractFieldTimeInterpolator.java b/src/main/java/org/orekit/time/AbstractFieldTimeInterpolator.java new file mode 100644 index 0000000000..19dea6b68b --- /dev/null +++ b/src/main/java/org/orekit/time/AbstractFieldTimeInterpolator.java @@ -0,0 +1,288 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.utils.ImmutableFieldTimeStampedCache; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Abstract class for time interpolator. + * + * @param interpolated time stamped type + * @param type of the field element + * + * @author Vincent Cucchietti + */ +public abstract class AbstractFieldTimeInterpolator, KK extends CalculusFieldElement> + implements FieldTimeInterpolator { + + /** Default extrapolation time threshold: 1ms. */ + public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3; + + /** Default number of interpolation points. */ + public static final int DEFAULT_INTERPOLATION_POINTS = 2; + + /** The extrapolation threshold beyond which the propagation will fail. */ + private final double extrapolationThreshold; + + /** Neighbor size. */ + private final int interpolationPoints; + + /** + * Constructor. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public AbstractFieldTimeInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + this.interpolationPoints = interpolationPoints; + this.extrapolationThreshold = extrapolationThreshold; + } + + /** + * Method checking if given interpolator is compatible with given sample size. + * + * @param interpolator interpolator + * @param sampleSize sample size + * @param type of the field elements + */ + public static > void checkInterpolatorCompatibilityWithSampleSize( + final FieldTimeInterpolator, T> interpolator, + final int sampleSize) { + + // Retrieve all sub-interpolators (or a singleton list with given interpolator if there are no sub-interpolators) + final List, T>> subInterpolators = + interpolator.getSubInterpolators(); + for (final FieldTimeInterpolator, T> subInterpolator : subInterpolators) { + if (sampleSize < subInterpolator.getNbInterpolationPoints()) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sampleSize); + } + } + } + + /** {@inheritDoc} */ + @Override + public T interpolate(final FieldAbsoluteDate interpolationDate, final Stream sample) { + return interpolate(interpolationDate, sample.collect(Collectors.toList())); + } + + /** {@inheritDoc}. */ + @Override + public T interpolate(final FieldAbsoluteDate interpolationDate, final Collection sample) { + final InterpolationData interpolationData = new InterpolationData(interpolationDate, sample); + return interpolate(interpolationData); + } + + /** {@inheritDoc} */ + public List, KK>> getSubInterpolators() { + return Collections.singletonList(this); + } + + /** {@inheritDoc} */ + public int getNbInterpolationPoints() { + return interpolationPoints; + } + + /** {@inheritDoc} */ + public double getExtrapolationThreshold() { + return extrapolationThreshold; + } + + /** + * Add all lowest level sub interpolators to the sub interpolator list. + * + * @param subInterpolator optional sub interpolator to add + * @param subInterpolators list of sub interpolators + * @param type of the field element + */ + protected > void addOptionalSubInterpolatorIfDefined( + final FieldTimeInterpolator, S> subInterpolator, + final List, S>> subInterpolators) { + // Add all lowest level sub interpolators + if (subInterpolator != null) { + subInterpolators.addAll(subInterpolator.getSubInterpolators()); + } + } + + /** + * Interpolate instance from given interpolation data. + * + * @param interpolationData interpolation data + * + * @return interpolated instance from given interpolation data. + */ + protected abstract T interpolate(InterpolationData interpolationData); + + /** + * Get the time parameter which lies between [0:1] by normalizing the difference between interpolating time and previous + * date by the Δt between tabulated values. + * + * @param interpolatingTime time at which we want to interpolate a value (between previous and next tabulated dates) + * @param previousDate previous tabulated value date + * @param nextDate next tabulated value date + * + * @return time parameter which lies between [0:1] + */ + protected KK getTimeParameter(final FieldAbsoluteDate interpolatingTime, + final FieldAbsoluteDate previousDate, + final FieldAbsoluteDate nextDate) { + + return interpolatingTime.durationFrom(previousDate).divide(nextDate.getDate().durationFrom(previousDate)); + } + + /** + * Nested class used to store interpolation data. + *

        + * It makes the interpolator thread safe. + */ + public class InterpolationData { + + /** Interpolation date. */ + private final FieldAbsoluteDate interpolationDate; + + /** Immutable time stamped cached samples. */ + private final ImmutableFieldTimeStampedCache cachedSamples; + + /** Unmodifiable list of neighbors. */ + private final List neighborList; + + /** Field of the element. */ + private final Field field; + + /** Fielded zero. */ + private final KK zero; + + /** Fielded one. */ + private final KK one; + + /** + * Constructor. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + */ + protected InterpolationData(final FieldAbsoluteDate interpolationDate, final Collection sample) { + try { + // Handle specific case that is not handled by the immutable time stamped cache constructor + if (sample.size() < 2) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, + sample.size()); + } + + // Create immutable time stamped cache + cachedSamples = new ImmutableFieldTimeStampedCache<>(interpolationPoints, sample); + + // Find neighbors + final FieldAbsoluteDate central = getCentralDate(interpolationDate); + final Stream neighborsStream = cachedSamples.getNeighbors(central); + + // Extract field and useful terms + this.field = interpolationDate.getField(); + this.zero = field.getZero(); + this.one = field.getOne(); + + // Convert to unmodifiable list + neighborList = Collections.unmodifiableList(neighborsStream.collect(Collectors.toList())); + + // Store interpolation date + this.interpolationDate = interpolationDate; + } + catch (OrekitIllegalArgumentException exception) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sample.size()); + } + } + + /** + * Get the central date to use to find neighbors while taking into account extrapolation threshold. + * + * @param date interpolation date + * + * @return central date to use to find neighbors + */ + protected FieldAbsoluteDate getCentralDate(final FieldAbsoluteDate date) { + final FieldAbsoluteDate central; + final FieldAbsoluteDate minDate = cachedSamples.getEarliest().getDate(); + final FieldAbsoluteDate maxDate = cachedSamples.getLatest().getDate(); + + if (date.compareTo(minDate) < 0 && + FastMath.abs(date.durationFrom(minDate)).getReal() <= extrapolationThreshold) { + // avoid TimeStampedCacheException as we are still within the tolerance before minDate + central = minDate; + } else if (date.compareTo(maxDate) > 0 && + FastMath.abs(date.durationFrom(maxDate)).getReal() <= extrapolationThreshold) { + // avoid TimeStampedCacheException as we are still within the tolerance after maxDate + central = maxDate; + } else { + central = date; + } + + return central; + } + + /** Get interpolation date. + * @return interpolation date + */ + public FieldAbsoluteDate getInterpolationDate() { + return interpolationDate; + } + + /** Get cached samples. + * @return cached samples + */ + public ImmutableFieldTimeStampedCache getCachedSamples() { + return cachedSamples; + } + + /** Get neighbor list. + * @return neighbor list + */ + public List getNeighborList() { + return neighborList; + } + + /** Get field. + * @return field + */ + public Field getField() { + return field; + } + + /** Get zero. + * @return zero + */ + public KK getZero() { + return zero; + } + + /** Get one. + * @return one + */ + public KK getOne() { + return one; + } + } +} diff --git a/src/main/java/org/orekit/time/AbstractTimeInterpolator.java b/src/main/java/org/orekit/time/AbstractTimeInterpolator.java new file mode 100644 index 0000000000..c3a62f0c12 --- /dev/null +++ b/src/main/java/org/orekit/time/AbstractTimeInterpolator.java @@ -0,0 +1,239 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.utils.ImmutableTimeStampedCache; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Abstract class for time interpolator. + * + * @param interpolated time stamped type + * + * @author Vincent Cucchietti + */ +public abstract class AbstractTimeInterpolator implements TimeInterpolator { + + /** Default extrapolation time threshold: 1ms. */ + public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3; + + /** Default number of interpolation points. */ + public static final int DEFAULT_INTERPOLATION_POINTS = 2; + + /** The extrapolation threshold beyond which the propagation will fail. */ + private final double extrapolationThreshold; + + /** Neighbor size. */ + private final int interpolationPoints; + + /** + * Constructor. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public AbstractTimeInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + this.interpolationPoints = interpolationPoints; + this.extrapolationThreshold = extrapolationThreshold; + } + + /** + * Method checking if given interpolator is compatible with given sample size. + * + * @param interpolator interpolator + * @param sampleSize sample size + */ + public static void checkInterpolatorCompatibilityWithSampleSize( + final TimeInterpolator interpolator, + final int sampleSize) { + + // Retrieve all sub-interpolators (or a singleton list with given interpolator if there are no sub-interpolators) + final List> subInterpolators = interpolator.getSubInterpolators(); + for (final TimeInterpolator subInterpolator : subInterpolators) { + if (sampleSize < subInterpolator.getNbInterpolationPoints()) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sampleSize); + } + } + } + + /** {@inheritDoc} */ + @Override + public T interpolate(final AbsoluteDate interpolationDate, final Stream sample) { + return interpolate(interpolationDate, sample.collect(Collectors.toList())); + } + + /** {@inheritDoc}. */ + @Override + public T interpolate(final AbsoluteDate interpolationDate, final Collection sample) { + final InterpolationData interpolationData = new InterpolationData(interpolationDate, sample); + return interpolate(interpolationData); + } + + /** {@inheritDoc} */ + public List> getSubInterpolators() { + return Collections.singletonList(this); + } + + /** {@inheritDoc} */ + public int getNbInterpolationPoints() { + return interpolationPoints; + } + + /** {@inheritDoc} */ + public double getExtrapolationThreshold() { + return extrapolationThreshold; + } + + /** + * Add all lowest level sub interpolators to the sub interpolator list. + * + * @param subInterpolator optional sub interpolator to add + * @param subInterpolators list of sub interpolators + */ + protected void addOptionalSubInterpolatorIfDefined(final TimeInterpolator subInterpolator, + final List> subInterpolators) { + // Add all lowest level sub interpolators + if (subInterpolator != null) { + subInterpolators.addAll(subInterpolator.getSubInterpolators()); + } + } + + /** + * Interpolate instance from given interpolation data. + * + * @param interpolationData interpolation data + * + * @return interpolated instance from given interpolation data. + */ + protected abstract T interpolate(InterpolationData interpolationData); + + /** + * Get the time parameter which lies between [0:1] by normalizing the difference between interpolating time and previous + * date by the Δt between tabulated values. + * + * @param interpolatingTime time at which we want to interpolate a value (between previous and next tabulated dates) + * @param previousDate previous tabulated value date + * @param nextDate next tabulated value date + * + * @return time parameter which lies between [0:1] + */ + protected double getTimeParameter(final AbsoluteDate interpolatingTime, + final AbsoluteDate previousDate, + final AbsoluteDate nextDate) { + return interpolatingTime.durationFrom(previousDate) / nextDate.getDate().durationFrom(previousDate); + } + + /** + * Nested class used to store interpolation data. + *

        + * It makes the interpolator thread safe. + */ + public class InterpolationData { + + /** Interpolation date. */ + private final AbsoluteDate interpolationDate; + + /** Cached samples. */ + private final ImmutableTimeStampedCache cachedSamples; + + /** Neighbor list around interpolation date. */ + private final List neighborList; + + /** + * Constructor. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + */ + protected InterpolationData(final AbsoluteDate interpolationDate, final Collection sample) { + // Handle specific case that is not handled by the immutable time stamped cache constructor + if (sample.size() < 2) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sample.size()); + } + + // Create immutable time stamped cache + this.cachedSamples = new ImmutableTimeStampedCache<>(interpolationPoints, sample); + + // Find neighbors + final AbsoluteDate central = getCentralDate(interpolationDate); + final Stream neighborsStream = cachedSamples.getNeighbors(central); + + // Convert to unmodifiable list + this.neighborList = Collections.unmodifiableList(neighborsStream.collect(Collectors.toList())); + + // Store interpolation date + this.interpolationDate = interpolationDate; + } + + /** + * Get the central date to use to find neighbors while taking into account extrapolation threshold. + * + * @param date interpolation date + * + * @return central date to use to find neighbors + */ + protected AbsoluteDate getCentralDate(final AbsoluteDate date) { + final AbsoluteDate central; + final AbsoluteDate minDate = cachedSamples.getEarliest().getDate(); + final AbsoluteDate maxDate = cachedSamples.getLatest().getDate(); + + if (date.compareTo(minDate) < 0 && + FastMath.abs(date.durationFrom(minDate)) <= extrapolationThreshold) { + // avoid TimeStampedCacheException as we are still within the tolerance before minDate + central = minDate; + } else if (date.compareTo(maxDate) > 0 && + FastMath.abs(date.durationFrom(maxDate)) <= extrapolationThreshold) { + // avoid TimeStampedCacheException as we are still within the tolerance after maxDate + central = maxDate; + } else { + central = date; + } + + return central; + } + + /** Get interpolation date. + * @return interpolation date + */ + public AbsoluteDate getInterpolationDate() { + return interpolationDate; + } + + /** Get cached samples. + * @return cached samples + */ + public ImmutableTimeStampedCache getCachedSamples() { + return cachedSamples; + } + + /** Get neighbor list. + * @return neighbor list + */ + public List getNeighborList() { + return neighborList; + } + + } +} diff --git a/src/main/java/org/orekit/time/BDTScale.java b/src/main/java/org/orekit/time/BDTScale.java index ac04dc9e4f..a2ea177105 100644 --- a/src/main/java/org/orekit/time/BDTScale.java +++ b/src/main/java/org/orekit/time/BDTScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/BurstSelector.java b/src/main/java/org/orekit/time/BurstSelector.java index 31429e3ee3..2822f702c2 100644 --- a/src/main/java/org/orekit/time/BurstSelector.java +++ b/src/main/java/org/orekit/time/BurstSelector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/ChronologicalComparator.java b/src/main/java/org/orekit/time/ChronologicalComparator.java index ca99299dde..30a5a410bd 100644 --- a/src/main/java/org/orekit/time/ChronologicalComparator.java +++ b/src/main/java/org/orekit/time/ChronologicalComparator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/DateComponents.java b/src/main/java/org/orekit/time/DateComponents.java index e61fe73620..725faecd1d 100644 --- a/src/main/java/org/orekit/time/DateComponents.java +++ b/src/main/java/org/orekit/time/DateComponents.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/DateTimeComponents.java b/src/main/java/org/orekit/time/DateTimeComponents.java index 4623ba2ba3..f0ccc3d2fe 100644 --- a/src/main/java/org/orekit/time/DateTimeComponents.java +++ b/src/main/java/org/orekit/time/DateTimeComponents.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/DatesSelector.java b/src/main/java/org/orekit/time/DatesSelector.java index 0fbf18f941..42c8e65cc3 100644 --- a/src/main/java/org/orekit/time/DatesSelector.java +++ b/src/main/java/org/orekit/time/DatesSelector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/FieldAbsoluteDate.java b/src/main/java/org/orekit/time/FieldAbsoluteDate.java index d823cf528f..6d5e33053c 100644 --- a/src/main/java/org/orekit/time/FieldAbsoluteDate.java +++ b/src/main/java/org/orekit/time/FieldAbsoluteDate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,11 +16,15 @@ */ package org.orekit.time; +import java.time.Instant; import java.util.Date; import java.util.TimeZone; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.FieldElement; +import org.hipparchus.analysis.differentiation.Derivative; +import org.hipparchus.complex.Complex; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.hipparchus.util.MathUtils.FieldSumAndResidual; @@ -98,9 +102,10 @@ * @see TimeScale * @see TimeStamped * @see ChronologicalComparator + * @param type of the field elements */ public class FieldAbsoluteDate> - implements FieldTimeStamped, TimeShiftable>, Comparable> { + implements FieldTimeStamped, FieldTimeShiftable, T>, Comparable> { /** Reference epoch in seconds from 2000-01-01T12:00:00 TAI. *

        Beware, it is not {@link #getJ2000Epoch(Field)} since it is in TAI and not in TT.

        */ @@ -281,7 +286,7 @@ public FieldAbsoluteDate(final Field field, final int year, final Month month * are given (parameters out of range) */ public FieldAbsoluteDate(final Field field, final DateComponents date, final TimeScale timeScale) - throws IllegalArgumentException { + throws IllegalArgumentException { this(field, date, TimeComponents.H00, timeScale); } @@ -296,7 +301,7 @@ public FieldAbsoluteDate(final Field field, final DateComponents date, final * are given (parameters out of range) */ public FieldAbsoluteDate(final Field field, final int year, final int month, final int day, - final TimeScale timeScale) throws IllegalArgumentException { + final TimeScale timeScale) throws IllegalArgumentException { this(field, new DateComponents(year, month, day), TimeComponents.H00, timeScale); } @@ -311,7 +316,7 @@ public FieldAbsoluteDate(final Field field, final int year, final int month, * are given (parameters out of range) */ public FieldAbsoluteDate(final Field field, final int year, final Month month, final int day, - final TimeScale timeScale) throws IllegalArgumentException { + final TimeScale timeScale) throws IllegalArgumentException { this(field, new DateComponents(year, month, day), TimeComponents.H00, timeScale); } @@ -322,11 +327,23 @@ public FieldAbsoluteDate(final Field field, final int year, final Month month */ public FieldAbsoluteDate(final Field field, final Date location, final TimeScale timeScale) { this(field, new DateComponents(DateComponents.JAVA_EPOCH, - (int) (location.getTime() / 86400000l)), - new TimeComponents(0.001 * (location.getTime() % 86400000l)), + (int) (location.getTime() / 86400000l)), + new TimeComponents(0.001 * (location.getTime() % 86400000l)), timeScale); } + /** Build an instance from an {@link Instant instant} in a {@link TimeScale time scale}. + * @param field field utilized as default + * @param instant instant in the time scale + * @param timeScale time scale + * @since 12.0 + */ + public FieldAbsoluteDate(final Field field, final Instant instant, final TimeScale timeScale) { + this(field, new DateComponents(DateComponents.JAVA_EPOCH, + (int) (instant.getEpochSecond() / 86400l)), + instantToTimeComponents(instant), + timeScale); + } /** Build an instance from an elapsed duration since to another instant. *

        It is important to note that the elapsed duration is not @@ -395,7 +412,7 @@ private FieldAbsoluteDate(final long epoch, final double tA, final T tB) { this.epoch = epoch + dl; } else { // very rare case, the offset is just before a whole second - // we will loose some bits of accuracy when adding 1 second + // we will lose some bits of accuracy when adding 1 second // but this will ensure the offset remains in the [0.0; 1.0) interval this.offset = regularOffset.add(1.0); this.epoch = epoch + dl - 1; @@ -403,6 +420,15 @@ private FieldAbsoluteDate(final long epoch, final double tA, final T tB) { } } + /** Extract time components from an instant within the day. + * @param instant instant to extract the number of seconds within the day + * @return time components + */ + private static TimeComponents instantToTimeComponents(final Instant instant) { + final int secInDay = (int) (instant.getEpochSecond() % 86400l); + return new TimeComponents(secInDay, 1.0e-9 * instant.getNano()); + } + /** Build an instance from a CCSDS Unsegmented Time Code (CUC). *

        * CCSDS Unsegmented Time Code is defined in the blue book: @@ -436,15 +462,15 @@ private FieldAbsoluteDate(final long epoch, final double tA, final T tB) { */ @DefaultDataContext public static > FieldAbsoluteDate parseCCSDSUnsegmentedTimeCode(final Field field, - final byte preambleField1, - final byte preambleField2, - final byte[] timeField, - final FieldAbsoluteDate agencyDefinedEpoch) { + final byte preambleField1, + final byte preambleField2, + final byte[] timeField, + final FieldAbsoluteDate agencyDefinedEpoch) { return parseCCSDSUnsegmentedTimeCode(field, preambleField1, preambleField2, - timeField, agencyDefinedEpoch, - new FieldAbsoluteDate<>( - field, - DataContext.getDefault().getTimeScales().getCcsdsEpoch())); + timeField, agencyDefinedEpoch, + new FieldAbsoluteDate<>( + field, + DataContext.getDefault().getTimeScales().getCcsdsEpoch())); } /** @@ -480,12 +506,12 @@ public static > FieldAbsoluteDate parseCCSD * @since 10.1 */ public static > FieldAbsoluteDate parseCCSDSUnsegmentedTimeCode( - final Field field, - final byte preambleField1, - final byte preambleField2, - final byte[] timeField, - final FieldAbsoluteDate agencyDefinedEpoch, - final FieldAbsoluteDate ccsdsEpoch) { + final Field field, + final byte preambleField1, + final byte preambleField2, + final byte[] timeField, + final FieldAbsoluteDate agencyDefinedEpoch, + final FieldAbsoluteDate ccsdsEpoch) { // time code identification and reference epoch final FieldAbsoluteDate epochF; @@ -555,10 +581,10 @@ public static > FieldAbsoluteDate parseCCSD */ @DefaultDataContext public static > FieldAbsoluteDate parseCCSDSDaySegmentedTimeCode(final Field field, - final byte preambleField, final byte[] timeField, - final DateComponents agencyDefinedEpoch) { + final byte preambleField, final byte[] timeField, + final DateComponents agencyDefinedEpoch) { return parseCCSDSDaySegmentedTimeCode(field, preambleField, timeField, - agencyDefinedEpoch, DataContext.getDefault().getTimeScales().getUTC()); + agencyDefinedEpoch, DataContext.getDefault().getTimeScales().getUTC()); } /** @@ -582,11 +608,11 @@ public static > FieldAbsoluteDate parseCCSD * @since 10.1 */ public static > FieldAbsoluteDate parseCCSDSDaySegmentedTimeCode( - final Field field, - final byte preambleField, - final byte[] timeField, - final DateComponents agencyDefinedEpoch, - final TimeScale utc) { + final Field field, + final byte preambleField, + final byte[] timeField, + final DateComponents agencyDefinedEpoch, + final TimeScale utc) { // time code identification if ((preambleField & 0xF0) != 0x40) { @@ -663,7 +689,7 @@ public static > FieldAbsoluteDate parseCCSD @DefaultDataContext public FieldAbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField) { return parseCCSDSCalendarSegmentedTimeCode(preambleField, timeField, - DataContext.getDefault().getTimeScales().getUTC()); + DataContext.getDefault().getTimeScales().getUTC()); } /** @@ -681,9 +707,9 @@ public FieldAbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte pream * @since 10.1 */ public FieldAbsoluteDate parseCCSDSCalendarSegmentedTimeCode( - final byte preambleField, - final byte[] timeField, - final TimeScale utc) { + final byte preambleField, + final byte[] timeField, + final TimeScale utc) { // time code identification if ((preambleField & 0xF0) != 0x50) { @@ -756,9 +782,9 @@ private static String formatByte(final byte data) { * @param the type of the field elements */ public static > FieldAbsoluteDate createJDDate(final int jd, final T secondsSinceNoon, - final TimeScale timeScale) { + final TimeScale timeScale) { return new FieldAbsoluteDate<>(secondsSinceNoon.getField(), new DateComponents(DateComponents.JULIAN_EPOCH, jd), - TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon); + TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon); } /** Build an instance corresponding to a Modified Julian Day date. @@ -769,11 +795,11 @@ public static > FieldAbsoluteDate createJDD * @param the type of the field elements */ public static > FieldAbsoluteDate createMJDDate(final int mjd, final T secondsInDay, - final TimeScale timeScale) { + final TimeScale timeScale) { return new FieldAbsoluteDate<>(secondsInDay.getField(), - new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd), - TimeComponents.H00, - timeScale).shiftedBy(secondsInDay); + new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd), + TimeComponents.H00, + timeScale).shiftedBy(secondsInDay); } /** Build an instance corresponding to a GPS date. @@ -792,7 +818,7 @@ public static > FieldAbsoluteDate createMJD @DefaultDataContext public static > FieldAbsoluteDate createGPSDate(final int weekNumber, final T milliInWeek) { return createGPSDate(weekNumber, milliInWeek, - DataContext.getDefault().getTimeScales().getGPS()); + DataContext.getDefault().getTimeScales().getGPS()); } /** @@ -809,14 +835,14 @@ public static > FieldAbsoluteDate createGPS * @since 10.1 */ public static > FieldAbsoluteDate createGPSDate( - final int weekNumber, - final T milliInWeek, - final TimeScale gps) { + final int weekNumber, + final T milliInWeek, + final TimeScale gps) { final int day = (int) FastMath.floor(milliInWeek.getReal() / (1000.0 * Constants.JULIAN_DAY)); final T secondsInDay = milliInWeek.divide(1000.0).subtract(day * Constants.JULIAN_DAY); return new FieldAbsoluteDate<>(milliInWeek.getField(), new DateComponents(DateComponents.GPS_EPOCH, weekNumber * 7 + day), - TimeComponents.H00, gps).shiftedBy(secondsInDay); + TimeComponents.H00, gps).shiftedBy(secondsInDay); } /** Build an instance corresponding to a Julian Epoch (JE). @@ -839,7 +865,7 @@ public static > FieldAbsoluteDate createGPS @DefaultDataContext public static > FieldAbsoluteDate createJulianEpoch(final T julianEpoch) { return createJulianEpoch(julianEpoch, - DataContext.getDefault().getTimeScales()); + DataContext.getDefault().getTimeScales()); } /** @@ -864,12 +890,12 @@ public static > FieldAbsoluteDate createJul * @since 10.1 */ public static > FieldAbsoluteDate createJulianEpoch( - final T julianEpoch, - final TimeScales timeScales) { + final T julianEpoch, + final TimeScales timeScales) { final Field field = julianEpoch.getField(); return new FieldAbsoluteDate<>(new FieldAbsoluteDate<>(field, timeScales.getJ2000Epoch()), - julianEpoch.subtract(2000.0).multiply(Constants.JULIAN_YEAR)); + julianEpoch.subtract(2000.0).multiply(Constants.JULIAN_YEAR)); } /** Build an instance corresponding to a Besselian Epoch (BE). @@ -895,7 +921,7 @@ public static > FieldAbsoluteDate createJul @DefaultDataContext public static > FieldAbsoluteDate createBesselianEpoch(final T besselianEpoch) { return createBesselianEpoch(besselianEpoch, - DataContext.getDefault().getTimeScales()); + DataContext.getDefault().getTimeScales()); } /** @@ -923,13 +949,13 @@ public static > FieldAbsoluteDate createBes * @since 10.1 */ public static > FieldAbsoluteDate createBesselianEpoch( - final T besselianEpoch, - final TimeScales timeScales) { + final T besselianEpoch, + final TimeScales timeScales) { final Field field = besselianEpoch.getField(); return new FieldAbsoluteDate<>(new FieldAbsoluteDate<>(field, timeScales.getJ2000Epoch()), - besselianEpoch.subtract(1900).multiply(Constants.BESSELIAN_YEAR).add( - Constants.JULIAN_DAY * (-36525) + Constants.JULIAN_DAY * 0.31352)); + besselianEpoch.subtract(1900).multiply(Constants.BESSELIAN_YEAR).add( + Constants.JULIAN_DAY * (-36525) + Constants.JULIAN_DAY * 0.31352)); } /** Reference epoch for julian dates: -4712-01-01T12:00:00 Terrestrial Time. @@ -950,7 +976,7 @@ public static > FieldAbsoluteDate createBes @DefaultDataContext public static > FieldAbsoluteDate getJulianEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getJulianEpoch()); + DataContext.getDefault().getTimeScales().getJulianEpoch()); } /** Reference epoch for modified julian dates: 1858-11-17T00:00:00 Terrestrial Time. @@ -966,7 +992,7 @@ public static > FieldAbsoluteDate getJulian @DefaultDataContext public static > FieldAbsoluteDate getModifiedJulianEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getModifiedJulianEpoch()); + DataContext.getDefault().getTimeScales().getModifiedJulianEpoch()); } /** Reference epoch for 1950 dates: 1950-01-01T00:00:00 Terrestrial Time. @@ -982,7 +1008,7 @@ public static > FieldAbsoluteDate getModifi @DefaultDataContext public static > FieldAbsoluteDate getFiftiesEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getFiftiesEpoch()); + DataContext.getDefault().getTimeScales().getFiftiesEpoch()); } /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4): @@ -999,7 +1025,7 @@ public static > FieldAbsoluteDate getFiftie @DefaultDataContext public static > FieldAbsoluteDate getCCSDSEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getCcsdsEpoch()); + DataContext.getDefault().getTimeScales().getCcsdsEpoch()); } /** Reference epoch for Galileo System Time: 1999-08-22T00:00:00 UTC. @@ -1015,7 +1041,7 @@ public static > FieldAbsoluteDate getCCSDSE @DefaultDataContext public static > FieldAbsoluteDate getGalileoEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getGalileoEpoch()); + DataContext.getDefault().getTimeScales().getGalileoEpoch()); } /** Reference epoch for GPS weeks: 1980-01-06T00:00:00 GPS time. @@ -1031,7 +1057,7 @@ public static > FieldAbsoluteDate getGalile @DefaultDataContext public static > FieldAbsoluteDate getGPSEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getGpsEpoch()); + DataContext.getDefault().getTimeScales().getGpsEpoch()); } /** J2000.0 Reference epoch: 2000-01-01T12:00:00 Terrestrial Time (not UTC). @@ -1048,7 +1074,7 @@ public static > FieldAbsoluteDate getGPSEpo @DefaultDataContext public static > FieldAbsoluteDate getJ2000Epoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getJ2000Epoch()); + DataContext.getDefault().getTimeScales().getJ2000Epoch()); } /** Java Reference epoch: 1970-01-01T00:00:00 Universal Time Coordinate. @@ -1068,7 +1094,7 @@ public static > FieldAbsoluteDate getJ2000E @DefaultDataContext public static > FieldAbsoluteDate getJavaEpoch(final Field field) { return new FieldAbsoluteDate<>(field, - DataContext.getDefault().getTimeScales().getJavaEpoch()); + DataContext.getDefault().getTimeScales().getJavaEpoch()); } /** Dummy date at infinity in the past direction. @@ -1101,8 +1127,7 @@ public static > FieldAbsoluteDate getFuture * @param field field for the components * @return an arbitrary date. */ - public static > FieldAbsoluteDate getArbitraryEpoch( - final Field field) { + public static > FieldAbsoluteDate getArbitraryEpoch(final Field field) { return new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); } @@ -1114,11 +1139,12 @@ public static > FieldAbsoluteDate getArbitr *

        * @param dt time shift in seconds * @return a new date, shifted with respect to instance (which is immutable) - * @see org.orekit.utils.PVCoordinates#shiftedBy(double) - * @see org.orekit.attitudes.Attitude#shiftedBy(double) - * @see org.orekit.orbits.Orbit#shiftedBy(double) - * @see org.orekit.propagation.SpacecraftState#shiftedBy(double) + * @see org.orekit.utils.FieldPVCoordinates#shiftedBy(double) + * @see org.orekit.attitudes.FieldAttitude#shiftedBy(double) + * @see org.orekit.orbits.FieldOrbit#shiftedBy(double) + * @see org.orekit.propagation.FieldSpacecraftState#shiftedBy(double) */ + @Override public FieldAbsoluteDate shiftedBy(final T dt) { return new FieldAbsoluteDate<>(this, dt); } @@ -1194,7 +1220,7 @@ public T durationFrom(final AbsoluteDate instant) { public T offsetFrom(final FieldAbsoluteDate instant, final TimeScale timeScale) { final long elapsedDurationA = epoch - instant.epoch; final T elapsedDurationB = offset.add(timeScale.offsetFromTAI(this)). - subtract(instant.offset.add(timeScale.offsetFromTAI(instant))); + subtract(instant.offset.add(timeScale.offsetFromTAI(instant))); return elapsedDurationB.add(elapsedDurationA); } @@ -1263,11 +1289,9 @@ public DateTimeComponents getComponents(final TimeScale timeScale) { // extract calendar elements final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date); // extract time element, accounting for leap seconds - final double leap = - timeScale.insideLeap(this) ? timeScale.getLeap(this.toAbsoluteDate()) : 0; + final double leap = timeScale.insideLeap(this) ? timeScale.getLeap(this.toAbsoluteDate()) : 0; final int minuteDuration = timeScale.minuteDuration(this); - final TimeComponents timeComponents = - TimeComponents.fromSeconds((int) time, offset2000B, leap, minuteDuration); + final TimeComponents timeComponents = TimeComponents.fromSeconds((int) time, offset2000B, leap, minuteDuration); // build the components return new DateTimeComponents(dateComponents, timeComponents); @@ -1286,7 +1310,7 @@ public DateTimeComponents getComponents(final TimeScale timeScale) { @DefaultDataContext public DateTimeComponents getComponents(final int minutesFromUTC) { return getComponents(minutesFromUTC, - DataContext.getDefault().getTimeScales().getUTC()); + DataContext.getDefault().getTimeScales().getUTC()); } /** @@ -1334,6 +1358,7 @@ public DateTimeComponents getComponents(final int minutesFromUTC, } /** {@inheritDoc} */ + @Override public FieldAbsoluteDate getDate() { return this; } @@ -1367,7 +1392,7 @@ public DateTimeComponents getComponents(final TimeZone timeZone) { public DateTimeComponents getComponents(final TimeZone timeZone, final TimeScale utc) { final FieldAbsoluteDate javaEpoch = - new FieldAbsoluteDate<>(field, DateComponents.JAVA_EPOCH, utc); + new FieldAbsoluteDate<>(field, DateComponents.JAVA_EPOCH, utc); final long milliseconds = FastMath.round((offsetFrom(javaEpoch, utc).getReal()) * 1000); return getComponents(timeZone.getOffset(milliseconds) / 60000, utc); } @@ -1557,7 +1582,7 @@ public String toString(final TimeScale timeScale) { @DefaultDataContext public String toString(final int minutesFromUTC) { return toString(minutesFromUTC, - DataContext.getDefault().getTimeScales().getUTC()); + DataContext.getDefault().getTimeScales().getUTC()); } /** @@ -1605,16 +1630,15 @@ public String toString(final TimeZone timeZone, final TimeScale utc) { /** Get a time-shifted date. *

        - * Calling this method is equivalent to call new AbsoluteDate(this, dt). + * Calling this method is equivalent to call new FieldAbsoluteDate(this, dt). *

        * @param dt time shift in seconds * @return a new date, shifted with respect to instance (which is immutable) - * @see org.orekit.utils.PVCoordinates#shiftedBy(double) - * @see org.orekit.attitudes.Attitude#shiftedBy(double) - * @see org.orekit.orbits.Orbit#shiftedBy(double) - * @see org.orekit.propagation.SpacecraftState#shiftedBy(double) + * @see org.orekit.utils.FieldPVCoordinates#shiftedBy(double) + * @see org.orekit.attitudes.FieldAttitude#shiftedBy(double) + * @see org.orekit.orbits.FieldOrbit#shiftedBy(double) + * @see org.orekit.propagation.FieldSpacecraftState#shiftedBy(double) */ - @Override public FieldAbsoluteDate shiftedBy(final double dt) { return new FieldAbsoluteDate<>(this, dt); @@ -1628,6 +1652,16 @@ public AbsoluteDate toAbsoluteDate() { return new AbsoluteDate(epoch, offset.getReal()); } + /** Check if the Field is semantically equal to zero. + * + *

        Using {@link FieldElement#isZero()} + * + * @return true the Field is semantically equal to zero + * @since 12.0 + */ + public boolean hasZeroField() { + return (offset instanceof Derivative || offset instanceof Complex) && offset.subtract(offset.getReal()).isZero(); + } } diff --git a/src/main/java/org/orekit/time/FieldChronologicalComparator.java b/src/main/java/org/orekit/time/FieldChronologicalComparator.java new file mode 100644 index 0000000000..d9055845ce --- /dev/null +++ b/src/main/java/org/orekit/time/FieldChronologicalComparator.java @@ -0,0 +1,57 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.CalculusFieldElement; + +import java.io.Serializable; +import java.util.Comparator; + +/** + * Comparator for {@link FieldTimeStamped} instance. + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see FieldAbsoluteDate + * @see FieldTimeStamped + * @param type of the field elements + */ +public class FieldChronologicalComparator> + implements Comparator>, Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = -5373507372120707293L; + + /** Simple constructor. */ + public FieldChronologicalComparator() { + // nothing to do + } + + /** + * Compare two time-stamped instances. + * + * @param timeStamped1 first time-stamped instance + * @param timeStamped2 second time-stamped instance + * + * @return a negative integer, zero, or a positive integer as the first instance is before, simultaneous, or after the + * second one. + */ + public int compare(final FieldTimeStamped timeStamped1, + final FieldTimeStamped timeStamped2) { + return timeStamped1.getDate().compareTo(timeStamped2.getDate()); + } +} diff --git a/src/main/java/org/orekit/time/FieldTimeInterpolable.java b/src/main/java/org/orekit/time/FieldTimeInterpolable.java deleted file mode 100644 index abc77200fc..0000000000 --- a/src/main/java/org/orekit/time/FieldTimeInterpolable.java +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.time; - -import org.hipparchus.CalculusFieldElement; - -import java.util.Collection; -import java.util.stream.Stream; - -/** This interface represents objects that can be interpolated in time. - * @param Type of the object. - * @param type of the field elements - * @author Luc Maisonobe - */ -public interface FieldTimeInterpolable , KK extends CalculusFieldElement> { - - /** Get an interpolated instance. - *

        - * Note that the state of the current instance may not be used - * in the interpolation process, only its type and non interpolable - * fields are used (for example central attraction coefficient or - * frame when interpolating orbits). The interpolable fields taken - * into account are taken only from the states of the sample points. - * So if the state of the instance must be used, the instance should - * be included in the sample points. - *

        - *

        - * Note that this method is designed for small samples only (say up - * to about 10-20 points) so it can be implemented using polynomial - * interpolation (typically Hermite interpolation). Using too much - * points may induce Runge's - * phenomenon and numerical problems (including NaN appearing). - *

        - * @param date interpolation date - * @param sample sample points on which interpolation should be done - * @return a new instance, interpolated at specified date - */ - default T interpolate(FieldAbsoluteDate date, Collection sample) { - return interpolate(date, sample.stream()); - } - - /** Get an interpolated instance. - *

        - * Note that the state of the current instance may not be used - * in the interpolation process, only its type and non interpolable - * fields are used (for example central attraction coefficient or - * frame when interpolating orbits). The interpolable fields taken - * into account are taken only from the states of the sample points. - * So if the state of the instance must be used, the instance should - * be included in the sample points. - *

        - *

        - * Note that this method is designed for small samples only (say up - * to about 10-20 points) so it can be implemented using polynomial - * interpolation (typically Hermite interpolation). Using too much - * points may induce Runge's - * phenomenon and numerical problems (including NaN appearing). - *

        - * @param date interpolation date - * @param sample sample points on which interpolation should be done - * @return a new instance, interpolated at specified date - */ - T interpolate(FieldAbsoluteDate date, Stream sample); - -} diff --git a/src/main/java/org/orekit/time/FieldTimeInterpolator.java b/src/main/java/org/orekit/time/FieldTimeInterpolator.java new file mode 100644 index 0000000000..443548db9a --- /dev/null +++ b/src/main/java/org/orekit/time/FieldTimeInterpolator.java @@ -0,0 +1,118 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * This interface represents objects that can interpolate a time stamped value with respect to time. + * + * @param type of the interpolated instance + * @param type of the field element + * + * @author Vincent Cucchietti + * @see FieldAbsoluteDate + * @see FieldTimeStamped + * @see CalculusFieldElement + */ +public interface FieldTimeInterpolator, KK extends CalculusFieldElement> { + + /** + * Get an interpolated instance. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + * + * @return a new instance, interpolated at specified date + * + * @see TimeStamped + * @see AbsoluteDate + */ + default T interpolate(AbsoluteDate interpolationDate, Stream sample) { + return interpolate(interpolationDate, sample.collect(Collectors.toList())); + } + + /** + * Get an interpolated instance. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + * + * @return a new instance, interpolated at specified date + */ + default T interpolate(AbsoluteDate interpolationDate, Collection sample) { + final Optional optionalElement = sample.stream().findAny(); + if (optionalElement.isPresent()) { + final T element = optionalElement.get(); + return interpolate(new FieldAbsoluteDate<>(element.getDate().getField(), interpolationDate), sample); + } + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0); + } + + /** + * Get an interpolated instance. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + * + * @return a new instance, interpolated at specified date + * + * @see TimeStamped + * @see AbsoluteDate + */ + T interpolate(FieldAbsoluteDate interpolationDate, Stream sample); + + /** + * Get an interpolated instance. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + * + * @return a new instance, interpolated at specified date + */ + T interpolate(FieldAbsoluteDate interpolationDate, Collection sample); + + /** + * Get all lowest level interpolators implemented by this instance, otherwise return a list with this instance only. + *

        + * An example would be the spacecraft state interpolator which can use different interpolators for each of its attributes + * (orbit, absolute position-velocity-acceleration coordinates, mass...). In this case, it would return the list of all + * of these interpolators (or possibly all of their sub-interpolators if they were to use multiple interpolators + * themselves). + * + * @return list of interpolators + */ + List, KK>> getSubInterpolators(); + + /** Get the number of interpolation points. + * @return the number of interpolation points. + */ + int getNbInterpolationPoints(); + + /** Get the extrapolation threshold. + * @return get the extrapolation threshold. + */ + double getExtrapolationThreshold(); +} diff --git a/src/main/java/org/orekit/time/FieldTimeShiftable.java b/src/main/java/org/orekit/time/FieldTimeShiftable.java index 125b927b01..025cf6fe30 100644 --- a/src/main/java/org/orekit/time/FieldTimeShiftable.java +++ b/src/main/java/org/orekit/time/FieldTimeShiftable.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -24,13 +24,8 @@ * @author Luc Maisonobe * @since 9.0 */ -public interface FieldTimeShiftable, KK extends CalculusFieldElement> { - - /** Get a time-shifted instance. - * @param dt time shift in seconds - * @return a new instance, shifted with respect to instance (which is not changed) - */ - T shiftedBy(double dt); +public interface FieldTimeShiftable, KK extends CalculusFieldElement> + extends TimeShiftable { /** Get a time-shifted instance. * @param dt time shift in seconds diff --git a/src/main/java/org/orekit/time/FieldTimeStamped.java b/src/main/java/org/orekit/time/FieldTimeStamped.java index d38094dfbe..cd9376bc78 100644 --- a/src/main/java/org/orekit/time/FieldTimeStamped.java +++ b/src/main/java/org/orekit/time/FieldTimeStamped.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -38,6 +38,7 @@ * @see ChronologicalComparator * @see org.orekit.utils.TimeStampedCache * @author Luc Maisonobe + * @param type of the field elements */ public interface FieldTimeStamped> { @@ -46,4 +47,21 @@ public interface FieldTimeStamped> { */ FieldAbsoluteDate getDate(); + /** Compute the physically elapsed duration between two instants. + *

        The returned duration is the number of seconds physically + * elapsed between the two instants, measured in a regular time + * scale with respect to surface of the Earth (i.e either the {@link + * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link + * GPSScale GPS scale}). It is the only method that gives a + * duration with a physical meaning.

        + * @param other instant to subtract from the instance + * @return offset in seconds between the two instants (positive + * if the instance is posterior to the argument) + * @see FieldAbsoluteDate#durationFrom(FieldAbsoluteDate) + * @since 12.0 + */ + default T durationFrom(FieldTimeStamped other) { + return getDate().durationFrom(other.getDate()); + } + } diff --git a/src/main/java/org/orekit/time/FieldTimeStampedPair.java b/src/main/java/org/orekit/time/FieldTimeStampedPair.java new file mode 100644 index 0000000000..df27d557c8 --- /dev/null +++ b/src/main/java/org/orekit/time/FieldTimeStampedPair.java @@ -0,0 +1,90 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.CalculusFieldElement; + +/** + * Pair of time stamped values being defined at the same date. + * + * @param first time stamped value + * @param second time stamped value + * @param type of the field element + * + * @author Vincent Cucchietti + * @see FieldTimeStamped + */ +public class FieldTimeStampedPair, S extends FieldTimeStamped, + KK extends CalculusFieldElement> implements FieldTimeStamped { + + /** Default date equality threshold of 1 ns. */ + public static final double DEFAULT_DATE_EQUALITY_THRESHOLD = 1e-9; + + /** First time stamped value. */ + private final F first; + + /** Second time stamped value. */ + private final S second; + + /** + * Constructor. + *

        + * First and second value must have the same date. + * + * @param first first time stamped value + * @param second second time stamped value + */ + public FieldTimeStampedPair(final F first, final S second) { + this(first, second, DEFAULT_DATE_EQUALITY_THRESHOLD); + } + + /** + * Constructor. + *

        + * First and second value must have the same date. + * + * @param first first time stamped value + * @param second second time stamped value + * @param dateEqualityThreshold threshold below which dates are considered equal + */ + public FieldTimeStampedPair(final F first, final S second, final double dateEqualityThreshold) { + TimeStampedPair.checkDatesConsistency(first.getDate().toAbsoluteDate(), second.getDate().toAbsoluteDate(), + dateEqualityThreshold); + this.first = first; + this.second = second; + } + + /** {@inheritDoc} */ + @Override + public FieldAbsoluteDate getDate() { + return first.getDate(); + } + + /** Get first time stamped value. + * @return first time stamped value + */ + public F getFirst() { + return first; + } + + /** Get second time stamped value. + * @return second time stamped value + */ + public S getSecond() { + return second; + } +} diff --git a/src/main/java/org/orekit/time/FixedStepSelector.java b/src/main/java/org/orekit/time/FixedStepSelector.java index 85f8152bf0..9ce205cc0a 100644 --- a/src/main/java/org/orekit/time/FixedStepSelector.java +++ b/src/main/java/org/orekit/time/FixedStepSelector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/GLONASSDate.java b/src/main/java/org/orekit/time/GLONASSDate.java index 9adcc9d3df..45e30b9d31 100644 --- a/src/main/java/org/orekit/time/GLONASSDate.java +++ b/src/main/java/org/orekit/time/GLONASSDate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/GLONASSScale.java b/src/main/java/org/orekit/time/GLONASSScale.java index 87cf46befc..dc418bcb7d 100644 --- a/src/main/java/org/orekit/time/GLONASSScale.java +++ b/src/main/java/org/orekit/time/GLONASSScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/GMSTScale.java b/src/main/java/org/orekit/time/GMSTScale.java index dbafb04277..3e50b86bce 100644 --- a/src/main/java/org/orekit/time/GMSTScale.java +++ b/src/main/java/org/orekit/time/GMSTScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/GNSSDate.java b/src/main/java/org/orekit/time/GNSSDate.java index e4f109c4ff..3c989ff6f5 100644 --- a/src/main/java/org/orekit/time/GNSSDate.java +++ b/src/main/java/org/orekit/time/GNSSDate.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -43,7 +43,7 @@ public class GNSSDate implements Serializable, TimeStamped { /** Serializable UID. */ - private static final long serialVersionUID = 201902141L; + private static final long serialVersionUID = 20221228L; /** Duration of a week in days. */ private static final int WEEK_D = 7; @@ -51,9 +51,6 @@ public class GNSSDate implements Serializable, TimeStamped { /** Duration of a week in seconds. */ private static final double WEEK_S = WEEK_D * Constants.JULIAN_DAY; - /** Conversion factor from seconds to milliseconds. */ - private static final double S_TO_MS = 1000.0; - /** Reference date for ensuring continuity across GNSS week rollover. * @since 9.3.1 */ @@ -62,8 +59,8 @@ public class GNSSDate implements Serializable, TimeStamped { /** Week number since the GNSS reference epoch. */ private final int weekNumber; - /** Number of milliseconds since week start. */ - private final double milliInWeek; + /** Number of seconds since week start. */ + private final double secondsInWeek; /** Satellite system to consider. */ private final SatelliteSystem system; @@ -74,7 +71,7 @@ public class GNSSDate implements Serializable, TimeStamped { /** Build an instance corresponding to a GNSS date. *

        * GNSS dates are provided as a week number starting at - * the GNSS reference epoch and as a number of milliseconds + * the GNSS reference epoch and as a number of seconds * since week start. *

        *

        @@ -88,21 +85,21 @@ public class GNSSDate implements Serializable, TimeStamped { *

        This method uses the {@link DataContext#getDefault() default data context}. * * @param weekNumber week number - * @param milliInWeek number of milliseconds since week start + * @param secondsInWeek number of seconds since week start * @param system satellite system to consider * @see #GNSSDate(int, double, SatelliteSystem, TimeScales) + * @since 12.0 */ @DefaultDataContext - public GNSSDate(final int weekNumber, final double milliInWeek, - final SatelliteSystem system) { - this(weekNumber, milliInWeek, system, DataContext.getDefault().getTimeScales()); + public GNSSDate(final int weekNumber, final double secondsInWeek, final SatelliteSystem system) { + this(weekNumber, secondsInWeek, system, DataContext.getDefault().getTimeScales()); } /** * Build an instance corresponding to a GNSS date. *

        * GNSS dates are provided as a week number starting at the GNSS reference epoch and - * as a number of milliseconds since week start. + * as a number of seconds since week start. *

        *

        * Many interfaces provide week number modulo the constellation week cycle. In order @@ -114,20 +111,18 @@ public GNSSDate(final int weekNumber, final double milliInWeek, * correction. *

        * - * @param weekNumber week number - * @param milliInWeek number of milliseconds since week start - * @param system satellite system to consider - * @param timeScales the set of time scales. Used to retrieve the appropriate time - * scale for the given {@code system}. - * @since 10.1 + * @param weekNumber week number + * @param secondsInWeek number of seconds since week start + * @param system satellite system to consider + * @param timeScales the set of time scales. Used to retrieve the appropriate time + * scale for the given {@code system}. + * @since 12.0 */ - public GNSSDate(final int weekNumber, - final double milliInWeek, - final SatelliteSystem system, - final TimeScales timeScales) { + public GNSSDate(final int weekNumber, final double secondsInWeek, + final SatelliteSystem system, final TimeScales timeScales) { - final int day = (int) FastMath.floor(milliInWeek / (Constants.JULIAN_DAY * S_TO_MS)); - final double secondsInDay = milliInWeek / S_TO_MS - day * Constants.JULIAN_DAY; + final int day = (int) FastMath.floor(secondsInWeek / Constants.JULIAN_DAY); + final double secondsInDay = secondsInWeek - day * Constants.JULIAN_DAY; int w = weekNumber; DateComponents dc = new DateComponents(getWeekReferenceDateComponents(system), weekNumber * 7 + day); @@ -153,9 +148,54 @@ public GNSSDate(final int weekNumber, } - this.weekNumber = w; - this.milliInWeek = milliInWeek; - this.system = system; + this.weekNumber = w; + this.secondsInWeek = secondsInWeek; + this.system = system; + + date = new AbsoluteDate(dc, new TimeComponents(secondsInDay), getTimeScale(system, timeScales)); + + } + + /** + * Build an instance corresponding to a GNSS date. + *

        + * GNSS dates are provided as a week number starting at the GNSS reference epoch and + * as a number of seconds since week start. + *

        + * + * @param weekNumber week number + * @param secondsInWeek number of seconds since week start + * @param system satellite system to consider + * @param reference reference date for rollover, the generated date will be less + * than one half cycle from this date + * @param timeScales the set of time scales. Used to retrieve the appropriate time + * scale for the given {@code system}. + * @since 12.0 + */ + public GNSSDate(final int weekNumber, final double secondsInWeek, + final SatelliteSystem system, final DateComponents reference, + final TimeScales timeScales) { + + final int day = (int) FastMath.floor(secondsInWeek / Constants.JULIAN_DAY); + final double secondsInDay = secondsInWeek - day * Constants.JULIAN_DAY; + + int w = weekNumber; + DateComponents dc = new DateComponents(getWeekReferenceDateComponents(system), weekNumber * 7 + day); + final int cycleW = GNSSDateType.getRollOverWeek(system); + if (weekNumber < cycleW) { + + // fix GNSS week rollover + final int cycleD = WEEK_D * cycleW; + while (dc.getJ2000Day() < reference.getJ2000Day() - cycleD / 2) { + dc = new DateComponents(dc, cycleD); + w += cycleW; + } + + } + + this.weekNumber = w; + this.secondsInWeek = secondsInWeek; + this.system = system; date = new AbsoluteDate(dc, new TimeComponents(secondsInDay), getTimeScale(system, timeScales)); @@ -191,14 +231,14 @@ public GNSSDate(final AbsoluteDate date, final AbsoluteDate epoch = getWeekReferenceAbsoluteDate(system, timeScales); this.weekNumber = (int) FastMath.floor(date.durationFrom(epoch) / WEEK_S); final AbsoluteDate weekStart = new AbsoluteDate(epoch, WEEK_S * weekNumber); - this.milliInWeek = date.durationFrom(weekStart) * S_TO_MS; - this.date = date; + this.secondsInWeek = date.durationFrom(weekStart); + this.date = date; } /** Set a reference date for ensuring continuity across GNSS week rollover. *

        - * Instance created using the {@link #GNSSDate(int, double, SatelliteSystem) GNSSDate(weekNumber, milliInWeek, system)} + * Instance created using the {@link #GNSSDate(int, double, SatelliteSystem) GNSSDate(weekNumber, secondsInWeek, system)} * constructor and with a week number between 0 and the constellation week cycle (cycleW) after this method has been called will * fix the week number to ensure they correspond to dates between {@code reference - cycleW / 2 weeks} * and {@code reference + cycleW / 2 weeks}. @@ -242,7 +282,15 @@ public int getWeekNumber() { * @return number of milliseconds since week start */ public double getMilliInWeek() { - return milliInWeek; + return getSecondsInWeek() * 1000.0; + } + + /** Get the number of seconds since week start. + * @return number of seconds since week start + * @since 12.0 + */ + public double getSecondsInWeek() { + return secondsInWeek; } /** {@inheritDoc} */ @@ -310,7 +358,7 @@ private DateComponents getWeekReferenceDateComponents(final SatelliteSystem sate */ @DefaultDataContext private Object writeReplace() { - return new DataTransferObject(weekNumber, milliInWeek, system); + return new DataTransferObject(weekNumber, secondsInWeek, system); } /** Internal class used only for serialization. */ @@ -318,34 +366,34 @@ private Object writeReplace() { private static class DataTransferObject implements Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 201902141L; + private static final long serialVersionUID = 20221228L; /** Week number since the GNSS reference epoch. */ private final int weekNumber; - /** Number of milliseconds since week start. */ - private final double milliInWeek; + /** Number of seconds since week start. */ + private final double secondsInWeek; /** Satellite system to consider. */ private final SatelliteSystem system; /** Simple constructor. * @param weekNumber week number since the GNSS reference epoch - * @param milliInWeek number of milliseconds since week start + * @param secondsInWeek number of seconds since week start * @param system satellite system to consider */ - DataTransferObject(final int weekNumber, final double milliInWeek, + DataTransferObject(final int weekNumber, final double secondsInWeek, final SatelliteSystem system) { - this.weekNumber = weekNumber; - this.milliInWeek = milliInWeek; - this.system = system; + this.weekNumber = weekNumber; + this.secondsInWeek = secondsInWeek; + this.system = system; } /** Replace the deserialized data transfer object with a {@link GNSSDate}. * @return replacement {@link GNSSDate} */ private Object readResolve() { - return new GNSSDate(weekNumber, milliInWeek, system); + return new GNSSDate(weekNumber, secondsInWeek, system); } } diff --git a/src/main/java/org/orekit/time/GPSScale.java b/src/main/java/org/orekit/time/GPSScale.java index 2bfef5071b..1832bfa42b 100644 --- a/src/main/java/org/orekit/time/GPSScale.java +++ b/src/main/java/org/orekit/time/GPSScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/GalileoScale.java b/src/main/java/org/orekit/time/GalileoScale.java index aa057b0797..de32a8c9e5 100644 --- a/src/main/java/org/orekit/time/GalileoScale.java +++ b/src/main/java/org/orekit/time/GalileoScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/IRNSSScale.java b/src/main/java/org/orekit/time/IRNSSScale.java index 4df9d7ad71..403ed8ceb8 100644 --- a/src/main/java/org/orekit/time/IRNSSScale.java +++ b/src/main/java/org/orekit/time/IRNSSScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/Month.java b/src/main/java/org/orekit/time/Month.java index 9859a6a4d0..97e0396f19 100644 --- a/src/main/java/org/orekit/time/Month.java +++ b/src/main/java/org/orekit/time/Month.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/OffsetModel.java b/src/main/java/org/orekit/time/OffsetModel.java index cd938f93d6..c73b499a40 100644 --- a/src/main/java/org/orekit/time/OffsetModel.java +++ b/src/main/java/org/orekit/time/OffsetModel.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,13 +16,17 @@ */ package org.orekit.time; +import java.io.Serializable; /** TAI UTC offset model. * @see UTCTAIOffsetsLoader * @author Luc Maisonobe * @since 7.1 */ -public class OffsetModel { +public class OffsetModel implements Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = 20230302L; /** Date of the offset start. */ private final DateComponents start; diff --git a/src/main/java/org/orekit/time/PreloadedTimeScales.java b/src/main/java/org/orekit/time/PreloadedTimeScales.java index d9c4cd2131..48631ef9ed 100644 --- a/src/main/java/org/orekit/time/PreloadedTimeScales.java +++ b/src/main/java/org/orekit/time/PreloadedTimeScales.java @@ -110,11 +110,10 @@ public UTCScale getUTC() { @Override protected EOPHistory getEopHistory(final IERSConventions conventions, final boolean simpleEOP) { - return new EOPHistory( - conventions, - eopMap.computeIfAbsent(conventions, c -> eopSupplier.apply(c, this)), - simpleEOP, - this); + return new EOPHistory(conventions, EOPHistory.DEFAULT_INTERPOLATION_DEGREE, + eopMap.computeIfAbsent(conventions, c -> eopSupplier.apply(c, this)), + simpleEOP, + this); } @Override diff --git a/src/main/java/org/orekit/time/QZSSScale.java b/src/main/java/org/orekit/time/QZSSScale.java index c5897fcce7..65e2f49b90 100644 --- a/src/main/java/org/orekit/time/QZSSScale.java +++ b/src/main/java/org/orekit/time/QZSSScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/SatelliteClockScale.java b/src/main/java/org/orekit/time/SatelliteClockScale.java index b309889252..1f3823db4d 100644 --- a/src/main/java/org/orekit/time/SatelliteClockScale.java +++ b/src/main/java/org/orekit/time/SatelliteClockScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TAIScale.java b/src/main/java/org/orekit/time/TAIScale.java index e8f2629b0f..92b9a2678b 100644 --- a/src/main/java/org/orekit/time/TAIScale.java +++ b/src/main/java/org/orekit/time/TAIScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TAIUTCDatFilesLoader.java b/src/main/java/org/orekit/time/TAIUTCDatFilesLoader.java index 1ef6a5f515..c6263e40ff 100644 --- a/src/main/java/org/orekit/time/TAIUTCDatFilesLoader.java +++ b/src/main/java/org/orekit/time/TAIUTCDatFilesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TCBScale.java b/src/main/java/org/orekit/time/TCBScale.java index ae6346ad51..a1d9f9f02a 100644 --- a/src/main/java/org/orekit/time/TCBScale.java +++ b/src/main/java/org/orekit/time/TCBScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TCGScale.java b/src/main/java/org/orekit/time/TCGScale.java index 5201fc29cf..c2d76360cc 100644 --- a/src/main/java/org/orekit/time/TCGScale.java +++ b/src/main/java/org/orekit/time/TCGScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TDBScale.java b/src/main/java/org/orekit/time/TDBScale.java index 793105c486..09cc220517 100644 --- a/src/main/java/org/orekit/time/TDBScale.java +++ b/src/main/java/org/orekit/time/TDBScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TTScale.java b/src/main/java/org/orekit/time/TTScale.java index 560467ae39..d62b689be4 100644 --- a/src/main/java/org/orekit/time/TTScale.java +++ b/src/main/java/org/orekit/time/TTScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TimeComponents.java b/src/main/java/org/orekit/time/TimeComponents.java index 78da5c3890..cb715b2ffd 100644 --- a/src/main/java/org/orekit/time/TimeComponents.java +++ b/src/main/java/org/orekit/time/TimeComponents.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TimeInterpolable.java b/src/main/java/org/orekit/time/TimeInterpolable.java deleted file mode 100644 index 3ea76a473a..0000000000 --- a/src/main/java/org/orekit/time/TimeInterpolable.java +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2002-2022 CS GROUP - * Licensed to CS GROUP (CS) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * CS licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.orekit.time; - -import java.util.Collection; -import java.util.stream.Stream; - -/** This interface represents objects that can be interpolated in time. - * @param Type of the object. - * @author Luc Maisonobe - */ -public interface TimeInterpolable> { - - /** Get an interpolated instance. - *

        - * Note that the state of the current instance may not be used - * in the interpolation process, only its type and non interpolable - * fields are used (for example central attraction coefficient or - * frame when interpolating orbits). The interpolable fields taken - * into account are taken only from the states of the sample points. - * So if the state of the instance must be used, the instance should - * be included in the sample points. - *

        - *

        - * Note that this method is designed for small samples only (say up - * to about 10-20 points) so it can be implemented using polynomial - * interpolation (typically Hermite interpolation). Using too much - * points may induce Runge's - * phenomenon and numerical problems (including NaN appearing). - *

        - * @param date interpolation date - * @param sample sample points on which interpolation should be done - * @return a new instance, interpolated at specified date - */ - default T interpolate(AbsoluteDate date, Collection sample) { - return interpolate(date, sample.stream()); - } - - /** Get an interpolated instance. - *

        - * Note that the state of the current instance may not be used - * in the interpolation process, only its type and non interpolable - * fields are used (for example central attraction coefficient or - * frame when interpolating orbits). The interpolable fields taken - * into account are taken only from the states of the sample points. - * So if the state of the instance must be used, the instance should - * be included in the sample points. - *

        - *

        - * Note that this method is designed for small samples only (say up - * to about 10-20 points) so it can be implemented using polynomial - * interpolation (typically Hermite interpolation). Using too much - * points may induce Runge's - * phenomenon and numerical problems (including NaN appearing). - *

        - * @param date interpolation date - * @param sample sample points on which interpolation should be done - * @return a new instance, interpolated at specified date - * @since 9.0 - */ - T interpolate(AbsoluteDate date, Stream sample); - -} diff --git a/src/main/java/org/orekit/time/TimeInterpolator.java b/src/main/java/org/orekit/time/TimeInterpolator.java new file mode 100644 index 0000000000..1add1db172 --- /dev/null +++ b/src/main/java/org/orekit/time/TimeInterpolator.java @@ -0,0 +1,78 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + +/** + * This interface represents objects that can interpolate a time stamped value with respect to time. + * + * @param type of the interpolated instance + * + * @author Vincent Cucchietti + * @see AbsoluteDate + * @see TimeStamped + */ +public interface TimeInterpolator { + + /** + * Get an interpolated instance. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + * + * @return a new instance, interpolated at specified date + * + * @see TimeStamped + * @see AbsoluteDate + */ + T interpolate(AbsoluteDate interpolationDate, Stream sample); + + /** + * Get an interpolated instance. + * + * @param interpolationDate interpolation date + * @param sample time stamped sample + * + * @return a new instance, interpolated at specified date + */ + T interpolate(AbsoluteDate interpolationDate, Collection sample); + + /** + * Get all lowest level interpolators implemented by this instance, otherwise return a list with this instance only. + *

        + * An example would be the spacecraft state interpolator which can use different interpolators for each of its attributes + * (orbit, absolute position-velocity-acceleration coordinates, mass...). In this case, it would return the list of all + * of these interpolators (or possibly all of their sub-interpolators if they were to use multiple interpolators + * themselves). + * + * @return list of interpolators + */ + List> getSubInterpolators(); + + /** Get the number of interpolation points. + * @return get the number of interpolation points + */ + int getNbInterpolationPoints(); + + /** Get the extrapolation threshold. + * @return get the extrapolation threshold + */ + double getExtrapolationThreshold(); +} diff --git a/src/main/java/org/orekit/time/TimeScalarFunction.java b/src/main/java/org/orekit/time/TimeScalarFunction.java index 70adee698b..bbaae3cc61 100644 --- a/src/main/java/org/orekit/time/TimeScalarFunction.java +++ b/src/main/java/org/orekit/time/TimeScalarFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TimeScale.java b/src/main/java/org/orekit/time/TimeScale.java index 1dd49c956f..8958a022a9 100644 --- a/src/main/java/org/orekit/time/TimeScale.java +++ b/src/main/java/org/orekit/time/TimeScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TimeScales.java b/src/main/java/org/orekit/time/TimeScales.java index f83a8789d8..1393787d02 100644 --- a/src/main/java/org/orekit/time/TimeScales.java +++ b/src/main/java/org/orekit/time/TimeScales.java @@ -317,7 +317,7 @@ public interface TimeScales { * collection. * @return a set of time scales based on the given data. * @see UTCTAIOffsetsLoader.Parser - * @see org.orekit.frames.EOPHistoryLoader.Parser + * @see org.orekit.frames.EopHistoryLoader.Parser */ static TimeScales of( final Collection utcMinusTai, diff --git a/src/main/java/org/orekit/time/TimeScalesFactory.java b/src/main/java/org/orekit/time/TimeScalesFactory.java index 06e88f567d..3ca31093e2 100644 --- a/src/main/java/org/orekit/time/TimeScalesFactory.java +++ b/src/main/java/org/orekit/time/TimeScalesFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TimeShiftable.java b/src/main/java/org/orekit/time/TimeShiftable.java index 69adfd6ac5..c7686f8b2f 100644 --- a/src/main/java/org/orekit/time/TimeShiftable.java +++ b/src/main/java/org/orekit/time/TimeShiftable.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/TimeStamped.java b/src/main/java/org/orekit/time/TimeStamped.java index 956f12d188..491e982f15 100644 --- a/src/main/java/org/orekit/time/TimeStamped.java +++ b/src/main/java/org/orekit/time/TimeStamped.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -44,4 +44,21 @@ public interface TimeStamped { */ AbsoluteDate getDate(); + /** Compute the physically elapsed duration between two instants. + *

        The returned duration is the number of seconds physically + * elapsed between the two instants, measured in a regular time + * scale with respect to surface of the Earth (i.e either the {@link + * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link + * GPSScale GPS scale}). It is the only method that gives a + * duration with a physical meaning.

        + * @param other instant to subtract from the instance + * @return offset in seconds between the two instants (positive + * if the instance is posterior to the argument) + * @see AbsoluteDate#durationFrom(AbsoluteDate) + * @since 12.0 + */ + default double durationFrom(TimeStamped other) { + return getDate().durationFrom(other.getDate()); + } + } diff --git a/src/main/java/org/orekit/time/TimeStampedDouble.java b/src/main/java/org/orekit/time/TimeStampedDouble.java new file mode 100644 index 0000000000..a0ed733f00 --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedDouble.java @@ -0,0 +1,57 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +/** + * Class that associates a double with a date. + * + * @author Vincent Cucchietti + * @see AbsoluteDate + */ +public class TimeStampedDouble implements TimeStamped { + + /** Date. */ + private final AbsoluteDate date; + + /** Value. */ + private final double value; + + /** + * Constructor. + * + * @param value value + * @param date date associated to value + */ + public TimeStampedDouble(final double value, final AbsoluteDate date) { + this.date = date; + this.value = value; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return date; + } + + /** Get value. + * @return value + */ + public double getValue() { + return value; + } + +} diff --git a/src/main/java/org/orekit/time/TimeStampedDoubleHermiteInterpolator.java b/src/main/java/org/orekit/time/TimeStampedDoubleHermiteInterpolator.java new file mode 100644 index 0000000000..0179c4ec9a --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedDoubleHermiteInterpolator.java @@ -0,0 +1,88 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; + +import java.util.List; + +/** + * Hermite interpolator of time stamped double value. + * + * @author Vincent Cucchietti + * @see HermiteInterpolator + * @see TimeInterpolator + */ +public class TimeStampedDoubleHermiteInterpolator extends AbstractTimeInterpolator { + + /** + * Constructor with : + *
          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + */ + public TimeStampedDoubleHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedDoubleHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public TimeStampedDoubleHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + super(interpolationPoints, extrapolationThreshold); + } + + /** {@inheritDoc} */ + @Override + protected TimeStampedDouble interpolate(final InterpolationData interpolationData) { + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Fill interpolator with sample + final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final List neighborList = interpolationData.getNeighborList(); + for (TimeStampedDouble value : neighborList) { + final double deltaT = value.getDate().durationFrom(interpolationDate); + interpolator.addSamplePoint(deltaT, new double[] { value.getValue() }); + } + + return new TimeStampedDouble(interpolator.value(0)[0], interpolationDate); + } +} diff --git a/src/main/java/org/orekit/time/TimeStampedField.java b/src/main/java/org/orekit/time/TimeStampedField.java new file mode 100644 index 0000000000..b0ead94541 --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedField.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.CalculusFieldElement; + +/** + * Class that associates a field with a date. + * + * @author Vincent Cucchietti + * @see FieldAbsoluteDate + * @see CalculusFieldElement + * @param type of the field elements + */ +public class TimeStampedField> implements FieldTimeStamped { + + /** Date. */ + private final FieldAbsoluteDate date; + + /** Value. */ + private final KK value; + + /** + * Constructor with normal date. + * + * @param value value + * @param date date associated to value + */ + public TimeStampedField(final KK value, final AbsoluteDate date) { + this(value, new FieldAbsoluteDate<>(value.getField(), date)); + } + + /** + * Constructor. + * + * @param value value + * @param date date associated to value + */ + public TimeStampedField(final KK value, final FieldAbsoluteDate date) { + this.date = date; + this.value = value; + } + + /** {@inheritDoc} */ + @Override + public FieldAbsoluteDate getDate() { + return date; + } + + /** Get value. + * @return value + */ + public KK getValue() { + return value; + } + +} diff --git a/src/main/java/org/orekit/time/TimeStampedFieldHermiteInterpolator.java b/src/main/java/org/orekit/time/TimeStampedFieldHermiteInterpolator.java new file mode 100644 index 0000000000..f68b27dc7d --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedFieldHermiteInterpolator.java @@ -0,0 +1,109 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; +import org.hipparchus.util.MathArrays; + +import java.util.List; + +/** + * Hermite interpolator of time stamped field value. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @author Vincent Cucchietti + * @see FieldHermiteInterpolator + * @see FieldTimeInterpolator + * @param type of the field elements + */ +public class TimeStampedFieldHermiteInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + */ + public TimeStampedFieldHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedFieldHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public TimeStampedFieldHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + super(interpolationPoints, extrapolationThreshold); + } + + /** + * {@inheritDoc} + *

        + * As this implementation of interpolation is polynomial, it should be used only with small samples (about 10-20 points) + * in order to avoid Runge's phenomenon and numerical + * problems (including NaN appearing). + */ + @Override + protected TimeStampedField interpolate(final InterpolationData interpolationData) { + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // Fill interpolator with sample + final Field field = interpolationData.getField(); + final KK zero = interpolationData.getZero(); + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final List> neighborList = interpolationData.getNeighborList(); + for (TimeStampedField value : neighborList) { + final KK deltaT = value.getDate().durationFrom(interpolationDate); + final KK[] tempArray = MathArrays.buildArray(field, 1); + tempArray[0] = value.getValue(); + interpolator.addSamplePoint(deltaT, tempArray); + } + + return new TimeStampedField<>(interpolator.value(zero)[0], interpolationDate); + } +} diff --git a/src/main/java/org/orekit/time/TimeStampedPair.java b/src/main/java/org/orekit/time/TimeStampedPair.java new file mode 100644 index 0000000000..504d2dc6ae --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedPair.java @@ -0,0 +1,103 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.time; + +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; + +/** + * Pair of time stamped values being defined at the same date. + * + * @param first time stamped value + * @param second time stamped value + * + * @author Vincent Cucchietti + * @see TimeStamped + */ +public class TimeStampedPair implements TimeStamped { + + /** Default date equality threshold of 1 ns. */ + public static final double DEFAULT_DATE_EQUALITY_THRESHOLD = 1e-9; + + /** First time stamped value. */ + private final K first; + + /** Second time stamped value. */ + private final V second; + + /** + * Constructor. + *

        + * First and second value must have the same date. + * + * @param first first time stamped value + * @param second second time stamped value + */ + public TimeStampedPair(final K first, final V second) { + this(first, second, DEFAULT_DATE_EQUALITY_THRESHOLD); + } + + /** + * Constructor. + *

        + * First and second value must have the same date. + * + * @param first first time stamped value + * @param second second time stamped value + * @param dateEqualityThreshold threshold below which dates are considered equal + */ + public TimeStampedPair(final K first, final V second, final double dateEqualityThreshold) { + checkDatesConsistency(first.getDate(), second.getDate(), dateEqualityThreshold); + this.first = first; + this.second = second; + } + + /** + * Check date consistency. + * + * @param firstDate first date + * @param secondDate second date + * @param dateEqualityThreshold threshold below which dates are considered equal + */ + public static void checkDatesConsistency(final AbsoluteDate firstDate, final AbsoluteDate secondDate, + final double dateEqualityThreshold) { + if (!firstDate.isCloseTo(secondDate, dateEqualityThreshold)) { + throw new OrekitIllegalArgumentException(OrekitMessages.DATES_MISMATCH, firstDate, secondDate); + } + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return first.getDate(); + } + + /** Get first time stamped value. + * @return first time stamped value + */ + public K getFirst() { + return first; + } + + /** Get second time stamped value. + * @return second time stamped value + */ + public V getSecond() { + return second; + } + +} diff --git a/src/main/java/org/orekit/time/TimeVectorFunction.java b/src/main/java/org/orekit/time/TimeVectorFunction.java index bac899ec5f..428ae439bb 100644 --- a/src/main/java/org/orekit/time/TimeVectorFunction.java +++ b/src/main/java/org/orekit/time/TimeVectorFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/UT1Scale.java b/src/main/java/org/orekit/time/UT1Scale.java index 4c82f106c9..182ea7bccf 100644 --- a/src/main/java/org/orekit/time/UT1Scale.java +++ b/src/main/java/org/orekit/time/UT1Scale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/UTCScale.java b/src/main/java/org/orekit/time/UTCScale.java index ef5dec5ec1..456d81508a 100644 --- a/src/main/java/org/orekit/time/UTCScale.java +++ b/src/main/java/org/orekit/time/UTCScale.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,7 +25,6 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; -import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; import org.orekit.utils.Constants; @@ -50,21 +49,31 @@ public class UTCScale implements TimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20150402L; + private static final long serialVersionUID = 20230302L; + + /** International Atomic Scale. */ + private final TimeScale tai; + + /** base UTC-TAI offsets (may lack the pre-1975 offsets). */ + private final Collection baseOffsets; /** UTC-TAI offsets. */ - private UTCTAIOffset[] offsets; + private final UTCTAIOffset[] offsets; /** Package private constructor for the factory. * Used to create the prototype instance of this class that is used to * clone all subsequent instances of {@link UTCScale}. Initializes the offset * table that is shared among all instances. * @param tai TAI time scale this UTC time scale references. - * @param offsets UTC-TAI offsets + * @param baseOffsets UTC-TAI base offsets (may lack the pre-1975 offsets) */ - UTCScale(final TimeScale tai, final Collection offsets) { + UTCScale(final TimeScale tai, final Collection baseOffsets) { + + this.tai = tai; + this.baseOffsets = baseOffsets; + // copy input so the original list is unmodified - final List offsetModels = new ArrayList<>(offsets); + final List offsetModels = new ArrayList<>(baseOffsets); offsetModels.sort(Comparator.comparing(OffsetModel::getStart)); if (offsetModels.get(0).getStart().getYear() > 1968) { // the pre-1972 linear offsets are missing, add them manually @@ -133,6 +142,14 @@ public class UTCScale implements TimeScale { } + /** Get the base offsets. + * @return base offsets (may lack the pre-1975 offsets) + * @since 12.0 + */ + public Collection getBaseOffsets() { + return baseOffsets; + } + /** * Returns the UTC-TAI offsets underlying this UTC scale. *

        @@ -344,7 +361,7 @@ private UTCTAIOffset findOffset(final int mjd) { */ @DefaultDataContext private Object writeReplace() { - return new DataTransferObject(); + return new DataTransferObject(tai, baseOffsets); } /** Internal class used only for serialization. */ @@ -352,14 +369,29 @@ private Object writeReplace() { private static class DataTransferObject implements Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 20131209L; + private static final long serialVersionUID = 20230302L; + + /** International Atomic Scale. */ + private final TimeScale tai; + + /** base UTC-TAI offsets (may lack the pre-1975 offsets). */ + private final Collection baseOffsets; + + /** Simple constructor. + * @param tai TAI time scale this UTC time scale references. + * @param baseOffsets UTC-TAI base offsets (may lack the pre-1975 offsets) + */ + DataTransferObject(final TimeScale tai, final Collection baseOffsets) { + this.tai = tai; + this.baseOffsets = baseOffsets; + } /** Replace the deserialized data transfer object with a {@link UTCScale}. * @return replacement {@link UTCScale} */ private Object readResolve() { try { - return DataContext.getDefault().getTimeScales().getUTC(); + return new UTCScale(tai, baseOffsets); } catch (OrekitException oe) { throw new OrekitInternalError(oe); } diff --git a/src/main/java/org/orekit/time/UTCTAIBulletinAFilesLoader.java b/src/main/java/org/orekit/time/UTCTAIBulletinAFilesLoader.java index 0357c3878b..4d4583d69b 100644 --- a/src/main/java/org/orekit/time/UTCTAIBulletinAFilesLoader.java +++ b/src/main/java/org/orekit/time/UTCTAIBulletinAFilesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/UTCTAIHistoryFilesLoader.java b/src/main/java/org/orekit/time/UTCTAIHistoryFilesLoader.java index ddece15d0b..7c53cca267 100644 --- a/src/main/java/org/orekit/time/UTCTAIHistoryFilesLoader.java +++ b/src/main/java/org/orekit/time/UTCTAIHistoryFilesLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/UTCTAIOffset.java b/src/main/java/org/orekit/time/UTCTAIOffset.java index 622c5b7e65..073bdd5400 100644 --- a/src/main/java/org/orekit/time/UTCTAIOffset.java +++ b/src/main/java/org/orekit/time/UTCTAIOffset.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/UTCTAIOffsetsLoader.java b/src/main/java/org/orekit/time/UTCTAIOffsetsLoader.java index 4f5e2daa9d..2cd7f4ade7 100644 --- a/src/main/java/org/orekit/time/UTCTAIOffsetsLoader.java +++ b/src/main/java/org/orekit/time/UTCTAIOffsetsLoader.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/time/package-info.java b/src/main/java/org/orekit/time/package-info.java index 19dec2f943..b4fc6d00c1 100644 --- a/src/main/java/org/orekit/time/package-info.java +++ b/src/main/java/org/orekit/time/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/AbsolutePVCoordinates.java b/src/main/java/org/orekit/utils/AbsolutePVCoordinates.java index a7cc9dcdbd..4ec47e1be2 100644 --- a/src/main/java/org/orekit/utils/AbsolutePVCoordinates.java +++ b/src/main/java/org/orekit/utils/AbsolutePVCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,10 +17,8 @@ package org.orekit.utils; import java.io.Serializable; -import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.Derivative; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; @@ -28,19 +26,17 @@ import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; -import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeInterpolable; import org.orekit.time.TimeStamped; /** Position - Velocity - Acceleration linked to a date and a frame. */ public class AbsolutePVCoordinates extends TimeStampedPVCoordinates - implements TimeStamped, TimeInterpolable, - Serializable, PVCoordinatesProvider { + implements TimeStamped, Serializable, PVCoordinatesProvider { /** Serializable UID. */ private static final long serialVersionUID = 20150824L; @@ -246,6 +242,12 @@ public AbsolutePVCoordinates shiftedBy(final double dt) { */ public PVCoordinatesProvider toTaylorProvider() { return new PVCoordinatesProvider() { + /** {@inheritDoc} */ + public Vector3D getPosition(final AbsoluteDate d, final Frame f) { + final TimeStampedPVCoordinates shifted = shiftedBy(d.durationFrom(getDate())); + final StaticTransform transform = frame.getStaticTransformTo(f, d); + return transform.transformPosition(shifted.getPosition()); + } /** {@inheritDoc} */ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate d, final Frame f) { final TimeStampedPVCoordinates shifted = shiftedBy(d.durationFrom(getDate())); @@ -269,6 +271,24 @@ public TimeStampedPVCoordinates getPVCoordinates() { return this; } + /** Get the position in a specified frame. + * @param outputFrame frame in which the position coordinates shall be computed + * @return position + * @see #getPVCoordinates(Frame) + * @since 12.0 + */ + public Vector3D getPosition(final Frame outputFrame) { + // If output frame requested is the same as definition frame, + // Position vector is returned directly + if (outputFrame == frame) { + return getPosition(); + } + + // Else, position vector is transformed to output frame + final StaticTransform t = frame.getStaticTransformTo(outputFrame, getDate()); + return t.transformPosition(getPosition()); + } + /** Get the TimeStampedPVCoordinates in a specified frame. * @param outputFrame frame in which the position/velocity coordinates shall be computed * @return TimeStampedPVCoordinates @@ -292,83 +312,6 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate otherDate, f return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(outputFrame); } - @Override - public AbsolutePVCoordinates interpolate(final AbsoluteDate date, final Stream sample) { - return interpolate(getFrame(), date, CartesianDerivativesFilter.USE_PVA, sample); - } - - /** Interpolate position-velocity. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * Note that even if first time derivatives (velocities) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the positions. - *

        - * @param frame frame for the interpolted instance - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @return a new position-velocity, interpolated at specified date - * @exception OrekitIllegalArgumentException if some elements in the sample do not - * have the same defining frame as other - */ - public static AbsolutePVCoordinates interpolate(final Frame frame, final AbsoluteDate date, - final CartesianDerivativesFilter filter, - final Stream sample) { - - - // set up an interpolator taking derivatives into account - final HermiteInterpolator interpolator = new HermiteInterpolator(); - - // add sample points - switch (filter) { - case USE_P : - // populate sample with position data, ignoring velocity - sample.forEach(pv -> { - final Vector3D position = pv.getPosition(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray()); - }); - break; - case USE_PV : - // populate sample with position and velocity data - sample.forEach(pv -> { - final Vector3D position = pv.getPosition(); - final Vector3D velocity = pv.getVelocity(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray()); - }); - break; - case USE_PVA : - // populate sample with position, velocity and acceleration data - sample.forEach(pv -> { - final Vector3D position = pv.getPosition(); - final Vector3D velocity = pv.getVelocity(); - final Vector3D acceleration = pv.getAcceleration(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray(), acceleration.toArray()); - }); - break; - default : - // this should never happen - throw new OrekitInternalError(null); - } - - // interpolate - final double[][] p = interpolator.derivatives(0.0, 2); - - // build a new interpolated instance - return new AbsolutePVCoordinates(frame, date, new Vector3D(p[0]), new Vector3D(p[1]), new Vector3D(p[2])); - - } - /** Replace the instance with a data transfer object for serialization. * @return data transfer object that will be serialized */ diff --git a/src/main/java/org/orekit/utils/AbsolutePVCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/AbsolutePVCoordinatesHermiteInterpolator.java new file mode 100644 index 0000000000..b1985822d3 --- /dev/null +++ b/src/main/java/org/orekit/utils/AbsolutePVCoordinatesHermiteInterpolator.java @@ -0,0 +1,197 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; + +import java.util.List; + +/** + * Class using a Hermite interpolator to interpolate absolute position-velocity-acceleration coordinates. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see HermiteInterpolator + * @see AbsolutePVCoordinates + */ +public class AbsolutePVCoordinatesHermiteInterpolator extends AbstractTimeInterpolator { + + /** Filter for derivatives from the sample to use in interpolation. */ + private final CartesianDerivativesFilter filter; + + /** Output frame for the interpolated instance. */ + private final Frame outputFrame; + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param outputFrame frame for the interpolated instance + */ + public AbsolutePVCoordinatesHermiteInterpolator(final Frame outputFrame) { + this(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, + CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputFrame frame for the interpolated instance + */ + public AbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final Frame outputFrame) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputFrame frame for the interpolated instance + * @param filter filter for derivatives from the sample to use in interpolation + */ + public AbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final Frame outputFrame, + final CartesianDerivativesFilter filter) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, filter); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame frame for the interpolated instance + * @param filter filter for derivatives from the sample to use in interpolation + */ + public AbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputFrame, final CartesianDerivativesFilter filter) { + super(interpolationPoints, extrapolationThreshold); + this.outputFrame = outputFrame; + this.filter = filter; + } + + /** Get the filter for derivatives from the sample to use in interpolation. + * @return filter for derivatives from the sample to use in interpolation. + */ + public CartesianDerivativesFilter getFilter() { + return filter; + } + + /** Get output frame for the interpolated instance. + * @return output frame for the interpolated instance + */ + public Frame getOutputFrame() { + return outputFrame; + } + + /** + * {@inheritDoc} + *

        + * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact + * derivative of position. + *

        + * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always + * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be + * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and + * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the + * positions. + */ + @Override + protected AbsolutePVCoordinates interpolate(final InterpolationData interpolationData) { + + // Get date + final AbsoluteDate date = interpolationData.getInterpolationDate(); + + // Get sample + final List sample = interpolationData.getNeighborList(); + + // Set up an interpolator taking derivatives into account + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Add sample points + switch (filter) { + case USE_P: + // Populate sample with position data, ignoring velocity + sample.forEach(pv -> { + final Vector3D position = pv.getPosition(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray()); + }); + break; + case USE_PV: + // Populate sample with position and velocity data + sample.forEach(pv -> { + final Vector3D position = pv.getPosition(); + final Vector3D velocity = pv.getVelocity(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray(), velocity.toArray()); + }); + break; + case USE_PVA: + // Populate sample with position, velocity and acceleration data + sample.forEach(pv -> { + final Vector3D position = pv.getPosition(); + final Vector3D velocity = pv.getVelocity(); + final Vector3D acceleration = pv.getAcceleration(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray(), velocity.toArray(), acceleration.toArray()); + }); + break; + default: + // this should never happen + throw new OrekitInternalError(null); + } + + // interpolate + final double[][] pva = interpolator.derivatives(0.0, 2); + + // build a new interpolated instance + return new AbsolutePVCoordinates(outputFrame, date, new Vector3D(pva[0]), new Vector3D(pva[1]), + new Vector3D(pva[2])); + } +} diff --git a/src/main/java/org/orekit/utils/AbstractMultipleShooting.java b/src/main/java/org/orekit/utils/AbstractMultipleShooting.java index 62774a5d43..78a08c3be7 100644 --- a/src/main/java/org/orekit/utils/AbstractMultipleShooting.java +++ b/src/main/java/org/orekit/utils/AbstractMultipleShooting.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -22,14 +22,15 @@ import java.util.List; import java.util.Map; +import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.LUDecomposition; import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.QRDecomposition; import org.hipparchus.linear.RealMatrix; import org.hipparchus.linear.RealVector; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; @@ -38,45 +39,48 @@ * Multiple shooting method using only constraints on state vectors of patch points (and possibly on epoch and integration time). * @see "TRAJECTORY DESIGN AND ORBIT MAINTENANCE STRATEGIES IN MULTI-BODY DYNAMICAL REGIMES by Thomas A. Pavlak, Purdue University" * @author William Desprats + * @author Alberto Fossà * @since 10.2 */ public abstract class AbstractMultipleShooting implements MultipleShooting { /** Patch points along the trajectory. */ - private List patchedSpacecraftStates; - - /** Derivatives linked to the Propagators. - * @deprecated as of 11.1 not used anymore - */ - @Deprecated - private List additionalEquations; + private final List patchedSpacecraftStates; /** List of Propagators. */ private final List propagatorList; - /** Duration of propagation along each arcs. */ + /** Duration of propagation along each arc. */ private final double[] propagationTime; /** Free components of patch points. */ - private final boolean[] freePatchPointMap; + private final boolean[] freeCompsMap; - /** Free epoch of patch points. */ + /** Free epochs of patch points. */ private final boolean[] freeEpochMap; - /** Number of free variables. */ - private int nFree; + /** Number of free state components. */ + private int nComps; + + /** Number of free arc duration. */ + // TODO add possibility to fix integration time span? + private final int nDuration; - /** Number of free epoch. */ + /** Number of free epochs. */ private int nEpoch; - /** Number of constraints. */ - private int nConstraints; + /** Scale time for update computation. */ + private double scaleTime; + + /** Scale length for update computation. */ + private double scaleLength; /** Patch points components which are constrained. */ private final Map mapConstraints; - /** True if orbit is closed. */ - private boolean isClosedOrbit; + /** True if the dynamical system is autonomous. + * In this case epochs and epochs constraints are omitted from the problem formulation. */ + private final boolean isAutonomous; /** Tolerance on the constraint vector. */ private final double tolerance; @@ -89,59 +93,58 @@ public abstract class AbstractMultipleShooting implements MultipleShooting { /** Simple Constructor. *

        Standard constructor for multiple shooting

        - * @param initialGuessList initial patch points to be corrected. - * @param propagatorList list of propagators associated to each patch point. - * @param additionalEquations list of additional equations linked to propagatorList. - * @param arcDuration initial guess of the duration of each arc. - * @param tolerance convergence tolerance on the constraint vector. - * @param additionalName name of the additional equations - * @deprecated as of 11.1, replaced by {@link #AbstractMultipleShooting(List, List, double, double, int, String)} - */ - @Deprecated - protected AbstractMultipleShooting(final List initialGuessList, final List propagatorList, - final List additionalEquations, - final double arcDuration, final double tolerance, final String additionalName) { - this(initialGuessList, propagatorList, arcDuration, tolerance, 1, additionalName); - this.additionalEquations = additionalEquations; - } - - /** Simple Constructor. - *

        Standard constructor for multiple shooting

        - * @param initialGuessList initial patch points to be corrected. - * @param propagatorList list of propagators associated to each patch point. - * @param arcDuration initial guess of the duration of each arc. - * @param tolerance convergence tolerance on the constraint vector. + * @param initialGuessList initial patch points to be corrected + * @param propagatorList list of propagators associated to each patch point + * @param tolerance convergence tolerance on the constraint vector * @param maxIter maximum number of iterations + * @param isAutonomous true if the dynamical system is autonomous (i.e. not dependent on the epoch) * @param additionalName name of the additional equations * @since 11.1 */ - protected AbstractMultipleShooting(final List initialGuessList, final List propagatorList, - final double arcDuration, final double tolerance, final int maxIter, final String additionalName) { + protected AbstractMultipleShooting(final List initialGuessList, + final List propagatorList, + final double tolerance, final int maxIter, + final boolean isAutonomous, final String additionalName) { + this.patchedSpacecraftStates = initialGuessList; - this.propagatorList = propagatorList; - this.additionalName = additionalName; - // Should check if propagatorList.size() = initialGuessList.size() - 1 + this.propagatorList = propagatorList; + this.isAutonomous = isAutonomous; + this.additionalName = additionalName; + + // propagation duration final int propagationNumber = initialGuessList.size() - 1; propagationTime = new double[propagationNumber]; - Arrays.fill(propagationTime, arcDuration); - - // All the patch points are set initially as free variables - this.freePatchPointMap = new boolean[6 * initialGuessList.size()]; // epoch - Arrays.fill(freePatchPointMap, true); + for (int i = 0; i < propagationNumber; i++) { + propagationTime[i] = initialGuessList.get(i + 1).getDate().durationFrom(initialGuessList.get(i).getDate()); + } - //Except the first one, the epochs of the patch points are set free. - this.freeEpochMap = new boolean[initialGuessList.size()]; - Arrays.fill(freeEpochMap, true); - freeEpochMap[0] = false; - this.nEpoch = initialGuessList.size() - 1; + // states components freedom + this.freeCompsMap = new boolean[6 * initialGuessList.size()]; + Arrays.fill(freeCompsMap, true); + + // epochs freedom + if (isAutonomous) { + // epochs omitted from problem formulation + this.freeEpochMap = new boolean[0]; + } else { + this.freeEpochMap = new boolean[initialGuessList.size()]; + Arrays.fill(freeEpochMap, true); + } - this.nConstraints = 6 * propagationNumber; - this.nFree = 6 * initialGuessList.size() + 1; + // number of free variables + this.nComps = 6 * initialGuessList.size(); + this.nDuration = propagationNumber; + this.nEpoch = freeEpochMap.length; + // convergence criteria this.tolerance = tolerance; this.maxIter = maxIter; - // All the additional constraints must be set afterward + // scaling + this.scaleTime = 1.0; + this.scaleLength = 1.0; + + // all additional constraints must be set afterward this.mapConstraints = new HashMap<>(); } @@ -155,75 +158,93 @@ protected SpacecraftState getPatchPoint(final int i) { } /** Set a component of a patch point to free or not. - * @param patchNumber Patch point with constraint - * @param componentIndex Component of the patch points which are constrained. + * @param patchIndex Patch point index (zero-based) + * @param componentIndex Index of the component to be constrained (zero-based) * @param isFree constraint value */ - public void setPatchPointComponentFreedom(final int patchNumber, final int componentIndex, final boolean isFree) { - if (freePatchPointMap[6 * (patchNumber - 1) + componentIndex] != isFree) { - nFree += isFree ? 1 : -1; - freePatchPointMap[6 * (patchNumber - 1) + componentIndex] = isFree; + public void setPatchPointComponentFreedom(final int patchIndex, final int componentIndex, final boolean isFree) { + if (freeCompsMap[6 * patchIndex + componentIndex] != isFree) { + freeCompsMap[6 * patchIndex + componentIndex] = isFree; + nComps += isFree ? 1 : -1; } } - /** Add a constraint on one component of one patch point. - * @param patchNumber Patch point with constraint - * @param componentIndex Component of the patch points which are constrained. - * @param constraintValue constraint value + /** Set the epoch of a patch point to free or not. + * @param patchIndex Patch point index (zero-based) + * @param isFree constraint value */ - public void addConstraint(final int patchNumber, final int componentIndex, final double constraintValue) { - final int contraintIndex = (patchNumber - 1) * 6 + componentIndex; - if (!mapConstraints.containsKey(contraintIndex)) { - nConstraints++; + public void setEpochFreedom(final int patchIndex, final boolean isFree) { + if (freeEpochMap[patchIndex] != isFree) { + freeEpochMap[patchIndex] = isFree; + nEpoch += isFree ? 1 : -1; } - mapConstraints.put(contraintIndex, constraintValue); } - /** Set the epoch a patch point to free or not. - * @param patchNumber Patch point - * @param isFree constraint value + /** Set the scale time. + * @param scaleTime scale time in seconds */ - public void setEpochFreedom(final int patchNumber, final boolean isFree) { - if (freeEpochMap[patchNumber - 1] != isFree) { - freeEpochMap[patchNumber - 1] = isFree; - final int eps = isFree ? 1 : -1; - nEpoch = nEpoch + eps; - } + public void setScaleTime(final double scaleTime) { + this.scaleTime = scaleTime; + } + + /** Set the scale length. + * @param scaleLength scale length in meters + */ + public void setScaleLength(final double scaleLength) { + this.scaleLength = scaleLength; + } + + /** Add a constraint on one component of one patch point. + * @param patchIndex Patch point index (zero-based) + * @param componentIndex Index of the component which is constrained (zero-based) + * @param constraintValue constraint value + */ + public void addConstraint(final int patchIndex, final int componentIndex, final double constraintValue) { + mapConstraints.put(patchIndex * 6 + componentIndex, constraintValue); } /** {@inheritDoc} */ @Override public List compute() { - if (nFree > nConstraints) { - throw new OrekitException(OrekitMessages.MULTIPLE_SHOOTING_UNDERCONSTRAINED, nFree, nConstraints); - } - int iter = 0; // number of iteration + double fxNorm; - double fxNorm = 0; + while (iter < maxIter) { - do { + iter++; - final List propagatedSP = propagatePatchedSpacecraftState(); // multi threading see PropagatorsParallelizer - final RealMatrix M = computeJacobianMatrix(propagatedSP); + // propagation (multi-threading see PropagatorsParallelizer) + final List propagatedSP = propagatePatchedSpacecraftState(); + + // constraints computation final RealVector fx = MatrixUtils.createRealVector(computeConstraint(propagatedSP)); - // Solve linear system using minimum norm approach + // convergence check + fxNorm = fx.getNorm(); + // System.out.printf(Locale.US, "Iter: %3d Error: %.16e%n", iter, fxNorm); + if (fxNorm < tolerance) { + break; + } + + // Jacobian matrix computation + final RealMatrix M = computeJacobianMatrix(propagatedSP); + + // correction computation using minimum norm approach // (i.e. minimize difference between solutions from successive iterations, - // in other word try to stay close to initial guess; this is *not* a least squares solution) + // in other word try to stay close to initial guess. This is *not* a least-squares solution) // see equation 3.12 in Pavlak's thesis final RealMatrix MMt = M.multiplyTransposed(M); - final RealVector dx = M.transposeMultiply(MatrixUtils.inverse(MMt)).operate(fx); + RealVector dx; + try { + dx = M.transpose().operate(new LUDecomposition(MMt, 0.0).getSolver().solve(fx)); + } catch (MathIllegalArgumentException e) { + dx = M.transpose().operate(new QRDecomposition(MMt, 0.0).getSolver().solve(fx)); + } - // Apply correction from the free variable vector to all the variables (propagation time, pacthSpaceraftState) + // trajectory update updateTrajectory(dx); - - fxNorm = fx.getNorm() / fx.getDimension(); - - iter++; - - } while (fxNorm > tolerance && iter < maxIter); // Converge within tolerance and under max iterations + } return patchedSpacecraftStates; } @@ -234,86 +255,82 @@ public List compute() { */ private RealMatrix computeJacobianMatrix(final List propagatedSP) { - final int npoints = patchedSpacecraftStates.size(); - final int epochConstraint = nEpoch == 0 ? 0 : npoints - 1; - final int nrows = getNumberOfConstraints() + epochConstraint; - final int ncolumns = getNumberOfFreeVariables() + nEpoch; - - final RealMatrix M = MatrixUtils.createRealMatrix(nrows, ncolumns); - - int index = 0; - int indexEpoch = nFree; - for (int i = 0; i < npoints - 1; i++) { + // The Jacobian matrix has the following form: + // + // [ | | ] + // [ A | B | C ] + // DF(X) = [ | | ] + // [-----------------] + // [ 0 | D | E ] + // [-----------------] + // [ F | 0 | 0 ] + // + // For a problem in which all the components of each patch points are free, A is detailed below: + // + // [ phi1 -I ] + // [ phi2 -I ] + // A = [ .... .... ] 6(n-1)x6n + // [ .... .... ] + // [ phin-1 -I ] + // + // If the duration of the propagation of each arc is the same: + // + // [ xdot1f ] + // [ xdot2f ] [ -1 ] + // B = [ .... ] 6(n-1)x1 and D = [ ... ] + // [ .... ] [ -1 ] + // [xdotn-1f] + // + // Otherwise: + // + // [ xdot1f ] + // [ xdot2f ] + // B = [ .... ] 6(n-1)x(n-1) and D = -I + // [ .... ] + // [ xdotn-1f ] + // + // If the problem is not dependant on the epoch (e.g. CR3BP), the C, D and E matrices are not computed. + // + // Otherwise: + // [ dx1f/dtau1 0 ] + // [ dx2f/dtau2 0 ] + // C = [ .... 0 ] 6(n-1)xn + // [ .... 0 ] + // [ dxn-1f/dtaun-1 0 ] + // + // [ -1 1 0 ] + // [ -1 1 0 ] + // E = [ .. .. ] n-1xn + // [ .. .. ] + // [ -1 1 ] + // + // F is computed according to additional constraints + // (for now, closed orbit, or a component of a patch point equals to a specified value) + + final int nArcs = patchedSpacecraftStates.size() - 1; + final double scaleVel = scaleLength / scaleTime; + final double scaleAcc = scaleVel / scaleTime; + final RealMatrix M = MatrixUtils.createRealMatrix(getNumberOfConstraints(), getNumberOfFreeVariables()); + + int index = 0; // first column index for matrix A + int indexDuration = nComps; // first column index for matrix B + int indexEpoch = indexDuration + nDuration; // first column index for matrix C + for (int i = 0; i < nArcs; i++) { final SpacecraftState finalState = propagatedSP.get(i); - // The Jacobian matrix has the following form: - - // [ | | ] - // [ A | B | C ] - // DF(X) = [ | | ] - // [-----------------] - // [ 0 | D | E ] - // [-----------------] - // [ F | 0 | 0 ] - - // [ | ] - // [ A | B ] - // DF(X) = [ | ] - // [-----------] - // [ C | 0 ] - // - // For a problem with all the components of each patch points is free, A is detailed below : - // [ phi1 -I ] - // [ phi2 -I ] - // A = [ .... .... ] 6(n-1)x6n - // [ .... .... ] - // [ phin-1 -I ] - - // D is computing according to additional constraints - // (for now, closed orbit, or a component of a patch point equals to a specified value) - // - - // If the duration of the propagation of each arc is the same : - // [ xdot1f ] - // [ xdot2f ] [ -1 ] - // B = [ .... ] 6(n-1)x1 and D = [ ... ] - // [ .... ] [ -1 ] - // [xdotn-1f] - - // Otherwise : - // [ xdot1f ] - // [ xdot2f ] - // B = [ .... ] 6(n-1)x(n-1) and D = -I - // [ .... ] - // [ xdotn-1f ] - // - // If the problem is not dependant of the epoch (e.g. CR3BP), the C and E matrices are not computed. - // Otherwise : - // [ -dx1f/dtau1 0 ] - // [ -dx2f/dtau2 0 ] - // C = [ .... 0 ] 6(n-1)xn - // [ .... 0 ] - // [ -dxn-1f/dtaun-1 0 ] - // - // [ -1 1 0 ] - // [ -1 1 0 ] - // E = [ .. .. ] n-1xn - // [ .. .. ] - // [ -1 1 ] + // PV coordinates and state transition matrix at final time final PVCoordinates pvf = finalState.getPVCoordinates(); - - // Get State Transition Matrix phi - final double[][] phi = getStateTransitionMatrix(finalState); + final double[][] phi = getStateTransitionMatrix(finalState); // already scaled // Matrix A for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point - if (freePatchPointMap[6 * i + j]) { // If this component is free + if (freeCompsMap[6 * i + j]) { // If this component is free for (int k = 0; k < 6; k++) { // Loop on the 6 component of the patch point constraint M.setEntry(6 * i + k, index, phi[k][j]); } if (i > 0) { - M.setEntry(6 * (i - 1) + j, index, -1); + M.setEntry(6 * (i - 1) + j, index, -1.0); } index++; } @@ -321,45 +338,51 @@ private RealMatrix computeJacobianMatrix(final List propagatedS // Matrix B final double[][] pvfArray = new double[][] { - {pvf.getVelocity().getX()}, - {pvf.getVelocity().getY()}, - {pvf.getVelocity().getZ()}, - {pvf.getAcceleration().getX()}, - {pvf.getAcceleration().getY()}, - {pvf.getAcceleration().getZ()}}; + {pvf.getVelocity().getX() / scaleVel}, + {pvf.getVelocity().getY() / scaleVel}, + {pvf.getVelocity().getZ() / scaleVel}, + {pvf.getAcceleration().getX() / scaleAcc}, + {pvf.getAcceleration().getY() / scaleAcc}, + {pvf.getAcceleration().getZ() / scaleAcc}}; - M.setSubMatrix(pvfArray, 6 * i, nFree - 1); + M.setSubMatrix(pvfArray, 6 * i, indexDuration); + indexDuration++; // Matrix C - if (freeEpochMap[i]) { // If this component is free - final double[] derivatives = finalState.getAdditionalState("derivatives"); - final double[][] subC = new double[6][1]; - for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point - subC[j][0] = -derivatives[derivatives.length - 6 + j]; + // there is a typo in Pavlak's thesis, equations 3.48-3.49: + // the sign in front of the partials of the states with respect to epochs should be plus + if (!isAutonomous) { + if (freeEpochMap[i]) { // If this component is free + final double[] derivatives = finalState.getAdditionalState(additionalName); + final double[][] subC = new double[6][1]; + for (int j = 0; j < 3; j++) { + subC[j][0] = derivatives[derivatives.length - 6 + j] / scaleVel; + subC[j + 3][0] = derivatives[derivatives.length - 3 + j] / scaleAcc; + } + M.setSubMatrix(subC, 6 * i, indexEpoch); + indexEpoch++; } - M.setSubMatrix(subC, 6 * i, indexEpoch); - indexEpoch++; } } + // complete Matrix A for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point - if (freePatchPointMap[6 * (npoints - 1) + j]) { // If this component is free - M.setEntry(6 * (npoints - 2) + j, index, -1); + if (freeCompsMap[6 * nArcs + j]) { // If this component is free + M.setEntry(6 * (nArcs - 1) + j, index, -1.0); index++; } } - - // Matrices D and E. - if (nEpoch > 0) { + // Matrices D and E + if (!isAutonomous) { final double[][] subDE = computeEpochJacobianMatrix(propagatedSP); - M.setSubMatrix(subDE, 6 * (npoints - 1), nFree - 1); + M.setSubMatrix(subDE, 6 * nArcs, nComps); } - // Matrices F. + // Matrix F final double[][] subF = computeAdditionalJacobianMatrix(propagatedSP); if (subF.length > 0) { - M.setSubMatrix(subF, 6 * (npoints - 1) + epochConstraint, 0); + M.setSubMatrix(subF, isAutonomous ? 6 * nArcs : 7 * nArcs, 0); } return M; @@ -372,7 +395,7 @@ private RealMatrix computeJacobianMatrix(final List propagatedS private double[] computeConstraint(final List propagatedSP) { // The Constraint vector has the following form : - + // // [ x1f - x2i ]--- // [ x2f - x3i ] | // F(X) = [ ... ] vectors' equality for a continuous trajectory @@ -384,123 +407,118 @@ private double[] computeConstraint(final List propagatedSP) { // [ ... ] additional // [ ... ] constraints - final int npoints = patchedSpacecraftStates.size(); - - final double[] additionalConstraints = computeAdditionalConstraints(propagatedSP); - final boolean epoch = getNumberOfFreeEpoch() > 0; - - final int nrows = epoch ? getNumberOfConstraints() + npoints - 1 : getNumberOfConstraints(); + final int nPoints = patchedSpacecraftStates.size(); + final double scaleVel = scaleLength / scaleTime; + final double[] fx = new double[getNumberOfConstraints()]; - final double[] fx = new double[nrows]; - for (int i = 0; i < npoints - 1; i++) { + // state continuity + for (int i = 0; i < nPoints - 1; i++) { final AbsolutePVCoordinates absPvi = patchedSpacecraftStates.get(i + 1).getAbsPVA(); final AbsolutePVCoordinates absPvf = propagatedSP.get(i).getAbsPVA(); - final double[] ecartPos = absPvf.getPosition().subtract(absPvi.getPosition()).toArray(); - final double[] ecartVel = absPvf.getVelocity().subtract(absPvi.getVelocity()).toArray(); + final double[] deltaPos = absPvf.getPosition().subtract(absPvi.getPosition()).toArray(); + final double[] deltaVel = absPvf.getVelocity().subtract(absPvi.getVelocity()).toArray(); for (int j = 0; j < 3; j++) { - fx[6 * i + j] = ecartPos[j]; - fx[6 * i + 3 + j] = ecartVel[j]; + fx[6 * i + j] = deltaPos[j] / scaleLength; + fx[6 * i + 3 + j] = deltaVel[j] / scaleVel; } } - int index = 6 * (npoints - 1); + int index = 6 * (nPoints - 1); - if (epoch) { - for (int i = 0; i < npoints - 1; i++) { - final double deltaEpoch = patchedSpacecraftStates.get(i + 1).getDate().durationFrom(patchedSpacecraftStates.get(i).getDate()); - fx[index] = deltaEpoch - propagationTime[i]; + // epoch constraints + if (!isAutonomous) { + for (int i = 0; i < nPoints - 1; i++) { + final double deltaEpoch = patchedSpacecraftStates.get(i + 1).getDate() + .durationFrom(patchedSpacecraftStates.get(i).getDate()); + fx[index] = (deltaEpoch - propagationTime[i]) / scaleTime; index++; } } - for (int i = 0; i < additionalConstraints.length; i++) { - fx[index] = additionalConstraints[i]; + // additional constraints + final double[] additionalConstraints = computeAdditionalConstraints(propagatedSP); + for (double constraint : additionalConstraints) { + fx[index] = constraint; index++; } return fx; } - /** Update the trajectory, and the propagation time. * @param dx correction on the initial vector */ private void updateTrajectory(final RealVector dx) { // X = [x1, ..., xn, T1, ..., Tn, d1, ..., dn] - // X = [x1, ..., xn, T, d2, ..., dn] - - final int n = getNumberOfFreeVariables(); - final boolean epochFree = getNumberOfFreeEpoch() > 0; + final double scaleVel = scaleLength / scaleTime; // Update propagation time - //------------------------------------------------------ - final double deltaT = dx.getEntry(n - 1); - for (int i = 0; i < propagationTime.length; i++) { - propagationTime[i] = propagationTime[i] - deltaT; + int indexDuration = nComps; + for (int i = 0; i < nDuration; i++) { + propagationTime[i] -= dx.getEntry(indexDuration) * scaleTime; + indexDuration++; } - // Update patch points through SpacecrafStates - //-------------------------------------------------------------------------------- - + // Update patch points through SpacecraftStates int index = 0; - int indexEpoch = 0; + int indexEpoch = nComps + nDuration; for (int i = 0; i < patchedSpacecraftStates.size(); i++) { + // Get delta in position and velocity final double[] deltaPV = new double[6]; for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point - if (freePatchPointMap[6 * i + j]) { // If this component is free (is to be updated) + if (freeCompsMap[6 * i + j]) { // If this component is free (is to be updated) deltaPV[j] = dx.getEntry(index); index++; } } - final Vector3D deltaP = new Vector3D(deltaPV[0], deltaPV[1], deltaPV[2]); - final Vector3D deltaV = new Vector3D(deltaPV[3], deltaPV[4], deltaPV[5]); + final Vector3D deltaP = new Vector3D(deltaPV[0], deltaPV[1], deltaPV[2]).scalarMultiply(scaleLength); + final Vector3D deltaV = new Vector3D(deltaPV[3], deltaPV[4], deltaPV[5]).scalarMultiply(scaleVel); // Update the PVCoordinates of the patch point final AbsolutePVCoordinates currentAPV = patchedSpacecraftStates.get(i).getAbsPVA(); final Vector3D position = currentAPV.getPosition().subtract(deltaP); final Vector3D velocity = currentAPV.getVelocity().subtract(deltaV); - final PVCoordinates pv = new PVCoordinates(position, velocity); + final PVCoordinates pv = new PVCoordinates(position, velocity); - //Update epoch in the AbsolutePVCoordinates + // Update epoch in the AbsolutePVCoordinates AbsoluteDate epoch = currentAPV.getDate(); - if (epochFree) { + if (!isAutonomous) { if (freeEpochMap[i]) { - final double deltaEpoch = dx.getEntry(n + indexEpoch); - epoch = epoch.shiftedBy(-deltaEpoch); + epoch = epoch.shiftedBy(-dx.getEntry(indexEpoch) * scaleTime); indexEpoch++; } } else { + // for autonomous systems we arbitrarily fix the date of the first patch point if (i > 0) { epoch = patchedSpacecraftStates.get(i - 1).getDate().shiftedBy(propagationTime[i - 1]); } } final AbsolutePVCoordinates updatedAPV = new AbsolutePVCoordinates(currentAPV.getFrame(), epoch, pv); - //Update attitude epoch + // Update attitude epoch // Last point does not have an associated propagator. The previous one is then selected. final int iAttitude = i < getPropagatorList().size() ? i : getPropagatorList().size() - 1; final AttitudeProvider attitudeProvider = getPropagatorList().get(iAttitude).getAttitudeProvider(); final Attitude attitude = attitudeProvider.getAttitude(updatedAPV, epoch, currentAPV.getFrame()); - //Update the SpacecraftState using previously updated attitude and AbsolutePVCoordinates + // Update the SpacecraftState using previously updated attitude and AbsolutePVCoordinates patchedSpacecraftStates.set(i, new SpacecraftState(updatedAPV, attitude)); } } - /** Compute the Jacobian matrix of the problem. + /** Propagate the patch point states. * @return propagatedSP propagated SpacecraftStates */ private List propagatePatchedSpacecraftState() { - final int n = patchedSpacecraftStates.size() - 1; - - final ArrayList propagatedSP = new ArrayList<>(n); + final int nArcs = patchedSpacecraftStates.size() - 1; + final ArrayList propagatedSP = new ArrayList<>(nArcs); - for (int i = 0; i < n; i++) { + for (int i = 0; i < nArcs; i++) { // SpacecraftState initialization final SpacecraftState augmentedInitialState = getAugmentedInitialState(i); @@ -508,10 +526,9 @@ private List propagatePatchedSpacecraftState() { // Propagator initialization propagatorList.get(i).setInitialState(augmentedInitialState); - final double integrationTime = propagationTime[i]; - // Propagate trajectory - final SpacecraftState finalState = propagatorList.get(i).propagate(augmentedInitialState.getDate().shiftedBy(integrationTime)); + final AbsoluteDate target = augmentedInitialState.getDate().shiftedBy(propagationTime[i]); + final SpacecraftState finalState = propagatorList.get(i).propagate(target); propagatedSP.add(finalState); } @@ -536,9 +553,20 @@ private double[][] getStateTransitionMatrix(final SpacecraftState s) { final String name = entry.getKey(); if (additionalName.equals(name)) { final double[] stm = entry.getValue(); - for (int i = 0; i < dim; i++) { - for (int j = 0; j < 6; j++) { - phiM[i][j] = stm[dim * i + j]; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + + // partials of final position w.r.t. initial position + phiM[i][j] = stm[6 * i + j]; + + // partials of final position w.r.t. initial velocity + phiM[i][j + 3] = stm[6 * i + j + 3] / scaleTime; + + // partials of final velocity w.r.t. initial position + phiM[i + 3][j] = stm[6 * i + j + 18] * scaleTime; + + // partials of final velocity w.r.t. initial velocity + phiM[i + 3][j + 3] = stm[6 * i + j + 21]; } } } @@ -555,31 +583,32 @@ private double[][] getStateTransitionMatrix(final SpacecraftState s) { */ protected double[][] computeEpochJacobianMatrix(final List propagatedSP) { - final boolean[] map = getFreeEpochMap(); + final int nRows = patchedSpacecraftStates.size() - 1; + final double[][] M = new double[nRows][nDuration + nEpoch]; - final int nFreeEpoch = getNumberOfFreeEpoch(); - final int ncolumns = 1 + nFreeEpoch; - final int nrows = patchedSpacecraftStates.size() - 1; + // The D and E sub-matrices have the following form: + // + // D = -I + // + // [-1 +1 0 ] + // [ -1 +1 0 ] + // F = [ .. .. ] + // [ .. .. 0 ] + // [ -1 +1 ] - final double[][] M = new double[nrows][ncolumns]; + int index = nDuration; + for (int i = 0; i < nRows; i++) { - // The Jacobian matrix has the following form: + // components of D matrix + M[i][i] = -1.0; - // [-1 -1 1 0 ] - // [-1 -1 1 0 ] - // F = [.. .. .. ] - // [.. .. .. 0 ] - // [-1 -1 1 ] - - int index = 1; - for (int i = 0; i < nrows; i++) { - M[i][0] = -1; - if (map[i]) { - M[i][index] = -1; + // components of E matrix + if (freeEpochMap[i]) { + M[i][index] = -1.0; index++; } - if (map[i + 1]) { - M[i][index] = 1; + if (freeEpochMap[i + 1]) { + M[i][index] = 1.0; } } @@ -592,6 +621,7 @@ protected double[][] computeEpochJacobianMatrix(final List prop */ protected void updateAdditionalConstraints(final int startIndex, final double[] fxAdditional) { int iter = startIndex; + final double scaleVel = scaleLength / scaleTime; for (final Map.Entry entry : getConstraintsMap().entrySet()) { // Extract entry values final int key = entry.getKey(); @@ -600,9 +630,9 @@ protected void updateAdditionalConstraints(final int startIndex, final double[] final int nc = key % 6; final AbsolutePVCoordinates absPv = getPatchedSpacecraftState().get(np).getAbsPVA(); if (nc < 3) { - fxAdditional[iter] = absPv.getPosition().toArray()[nc] - value; + fxAdditional[iter] = (absPv.getPosition().toArray()[nc] - value) / scaleLength; } else { - fxAdditional[iter] = absPv.getVelocity().toArray()[nc - 3] - value; + fxAdditional[iter] = (absPv.getVelocity().toArray()[nc - 3] - value) / scaleVel; } iter++; } @@ -610,7 +640,7 @@ protected void updateAdditionalConstraints(final int startIndex, final double[] /** Compute the additional constraints. * @param propagatedSP propagated SpacecraftState - * @return fxAdditionnal additional constraints + * @return fxAdditional additional constraints */ protected abstract double[] computeAdditionalConstraints(List propagatedSP); @@ -620,75 +650,33 @@ protected void updateAdditionalConstraints(final int startIndex, final double[] */ protected abstract double[][] computeAdditionalJacobianMatrix(List propagatedSP); - - /** Compute the additional state from the additionalEquations. - * @param initialState SpacecraftState without the additional state - * @param additionalEquations2 Additional Equations. - * @return augmentedSP SpacecraftState with the additional state within. - * @deprecated as of 11.1, replaced by {@link #getAugmentedInitialState(int)} - */ - @Deprecated - protected SpacecraftState getAugmentedInitialState(final SpacecraftState initialState, - final org.orekit.propagation.integration.AdditionalEquations additionalEquations2) { - // should never be called, only implementations by derived classes should be called - throw new UnsupportedOperationException(); - } - /** Compute the additional state from the additionalEquations. * @param i index of the state * @return augmentedSP SpacecraftState with the additional state within. * @since 11.1 */ - protected SpacecraftState getAugmentedInitialState(final int i) { - // FIXME: this base implementation is only intended for version 11.1 to delegate to a deprecated method - // it should be removed in 12.0 when getAugmentedInitialState(SpacecraftState, AdditionalDerivativesProvider) is removed - // and the method should remain abstract in this class and be implemented by derived classes only - return getAugmentedInitialState(patchedSpacecraftStates.get(i), additionalEquations.get(i)); - } - - /** Set the constraint of a closed orbit or not. - * @param isClosed true if orbit should be closed - */ - public void setClosedOrbitConstraint(final boolean isClosed) { - if (this.isClosedOrbit != isClosed) { - nConstraints = nConstraints + (isClosed ? 6 : -6); - this.isClosedOrbit = isClosed; - } - } - - /** Get the number of free variables. - * @return the number of free variables - */ - protected int getNumberOfFreeVariables() { - return nFree; - } + protected abstract SpacecraftState getAugmentedInitialState(int i); - /** Get the number of free epoch. - * @return the number of free epoch + /** Get the number of free state components. + * @return number of free components */ - protected int getNumberOfFreeEpoch() { - return nEpoch; + protected int getNumberOfFreeComponents() { + return nComps; } - /** Get the number of constraints. - * @return the number of constraints + /** Get the total number of constraints. + * @return the total number of constraints */ protected int getNumberOfConstraints() { - return nConstraints; + final int nArcs = patchedSpacecraftStates.size() - 1; + return (isAutonomous ? 6 * nArcs : 7 * nArcs) + mapConstraints.size(); } - /** Get the flags representing the free components of patch points. - * @return an array of flags representing the free components of patch points + /** Get the map of free state components. + * @return map of free state components */ - protected boolean[] getFreePatchPointMap() { - return freePatchPointMap; - } - - /** Get the flags representing the free epoch of patch points. - * @return an array of flags representing the free epoch of patch points - */ - protected boolean[] getFreeEpochMap() { - return freeEpochMap; + protected boolean[] getFreeCompsMap() { + return freeCompsMap; } /** Get the map of patch points components which are constrained. @@ -708,15 +696,15 @@ protected List getPatchedSpacecraftState() { /** Get the list of propagators. * @return a list of propagators */ - protected List getPropagatorList() { + private List getPropagatorList() { return propagatorList; } - /** Get he flag representing if the orbit is closed. - * @return true if orbit is closed + /** Get the number of free variables. + * @return the number of free variables */ - protected boolean isClosedOrbit() { - return isClosedOrbit; + private int getNumberOfFreeVariables() { + return nComps + nDuration + nEpoch; } } diff --git a/src/main/java/org/orekit/utils/AccurateFormatter.java b/src/main/java/org/orekit/utils/AccurateFormatter.java index 1c17be4956..21917fceca 100644 --- a/src/main/java/org/orekit/utils/AccurateFormatter.java +++ b/src/main/java/org/orekit/utils/AccurateFormatter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/AggregatedPVCoordinatesProvider.java b/src/main/java/org/orekit/utils/AggregatedPVCoordinatesProvider.java index 7ce46b949e..feb8d24d10 100644 --- a/src/main/java/org/orekit/utils/AggregatedPVCoordinatesProvider.java +++ b/src/main/java/org/orekit/utils/AggregatedPVCoordinatesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Joseph Reed +/* Copyright 2002-2023 Joseph Reed * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ import java.util.Objects; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; @@ -82,10 +83,18 @@ public AbsoluteDate getMaxDate() { return maxDate; } + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + if (date.isBefore(minDate) || date.isAfter(maxDate)) { + throw new OrekitIllegalArgumentException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate); + } + return pvProvMap.get(date).getPosition(date, frame); + } + @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { if (date.isBefore(minDate) || date.isAfter(maxDate)) { - throw new OrekitIllegalArgumentException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE, date, minDate, maxDate); + throw new OrekitIllegalArgumentException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate); } return pvProvMap.get(date).getPVCoordinates(date, frame); } @@ -193,9 +202,22 @@ public AggregatedPVCoordinatesProvider build() { */ public static class InvalidPVProvider implements PVCoordinatesProvider { + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public InvalidPVProvider() { + // nothing to do + } + @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { throw new IllegalStateException(); } + } + } diff --git a/src/main/java/org/orekit/utils/AngularCoordinates.java b/src/main/java/org/orekit/utils/AngularCoordinates.java index 70c0aa909f..f0b9e58eaa 100644 --- a/src/main/java/org/orekit/utils/AngularCoordinates.java +++ b/src/main/java/org/orekit/utils/AngularCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -729,7 +729,7 @@ public AngularCoordinates subtractOffset(final AngularCoordinates offset) { /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation */ public PVCoordinates applyTo(final PVCoordinates pv) { @@ -750,7 +750,7 @@ public PVCoordinates applyTo(final PVCoordinates pv) { /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation */ public TimeStampedPVCoordinates applyTo(final TimeStampedPVCoordinates pv) { @@ -772,7 +772,7 @@ public TimeStampedPVCoordinates applyTo(final TimeStampedPVCoordinates pv) { /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to * @param type of the field elements - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation * @since 9.0 */ public > FieldPVCoordinates applyTo(final FieldPVCoordinates pv) { @@ -795,7 +795,7 @@ public > FieldPVCoordinates applyTo(final F /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to * @param type of the field elements - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation * @since 9.0 */ public > TimeStampedFieldPVCoordinates applyTo(final TimeStampedFieldPVCoordinates pv) { diff --git a/src/main/java/org/orekit/utils/AngularDerivativesFilter.java b/src/main/java/org/orekit/utils/AngularDerivativesFilter.java index 7bfbf5dd62..2d52f34d7d 100644 --- a/src/main/java/org/orekit/utils/AngularDerivativesFilter.java +++ b/src/main/java/org/orekit/utils/AngularDerivativesFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,11 +18,13 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; /** Enumerate for selecting which derivatives to use in {@link TimeStampedAngularCoordinates} * and {@link TimeStampedFieldAngularCoordinates} interpolation. - * @see TimeStampedAngularCoordinates#interpolate(org.orekit.time.AbsoluteDate, AngularDerivativesFilter, java.util.Collection) - * @see TimeStampedFieldAngularCoordinates#interpolate(org.orekit.time.AbsoluteDate, AngularDerivativesFilter, java.util.Collection) + * @see TimeStampedAngularCoordinatesHermiteInterpolator#interpolate(AbsoluteDate, java.util.Collection) + * @see TimeStampedFieldAngularCoordinatesHermiteInterpolator#interpolate(FieldAbsoluteDate, java.util.Collection) * @see CartesianDerivativesFilter * @author Luc Maisonobe * @since 7.0 diff --git a/src/main/java/org/orekit/utils/CartesianDerivativesFilter.java b/src/main/java/org/orekit/utils/CartesianDerivativesFilter.java index ccfa448d9c..af5d21a542 100644 --- a/src/main/java/org/orekit/utils/CartesianDerivativesFilter.java +++ b/src/main/java/org/orekit/utils/CartesianDerivativesFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,11 +18,13 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; /** Enumerate for selecting which derivatives to use in {@link TimeStampedPVCoordinates} and * {@link TimeStampedFieldPVCoordinates} interpolation. - * @see TimeStampedPVCoordinates#interpolate(org.orekit.time.AbsoluteDate, CartesianDerivativesFilter, java.util.Collection) - * @see TimeStampedFieldPVCoordinates#interpolate(org.orekit.time.FieldAbsoluteDate, CartesianDerivativesFilter, java.util.Collection) + * @see TimeStampedPVCoordinatesHermiteInterpolator#interpolate(AbsoluteDate, java.util.Collection) + * @see TimeStampedFieldPVCoordinatesHermiteInterpolator#interpolate(FieldAbsoluteDate, java.util.Collection) * @see AngularDerivativesFilter * @author Luc Maisonobe * @since 7.0 diff --git a/src/main/java/org/orekit/utils/ConstantPVCoordinatesProvider.java b/src/main/java/org/orekit/utils/ConstantPVCoordinatesProvider.java index cbcf2733e6..4e9e665ded 100644 --- a/src/main/java/org/orekit/utils/ConstantPVCoordinatesProvider.java +++ b/src/main/java/org/orekit/utils/ConstantPVCoordinatesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Joseph Reed +/* Copyright 2002-2023 Joseph Reed * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -72,6 +72,11 @@ public ConstantPVCoordinatesProvider(final PVCoordinates pva, final Frame frame) this.sourceFrame = frame; } + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + return sourceFrame.getStaticTransformTo(frame, date).transformPosition(pva.getPosition()); + } + @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { final PVCoordinates pv = sourceFrame.getTransformTo(frame, date).transformPVCoordinates(pva); diff --git a/src/main/java/org/orekit/utils/Constants.java b/src/main/java/org/orekit/utils/Constants.java index fc18de61e5..d02b504613 100644 --- a/src/main/java/org/orekit/utils/Constants.java +++ b/src/main/java/org/orekit/utils/Constants.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -82,8 +82,8 @@ public interface Constants { double G0_STANDARD_GRAVITY = 9.80665; - /** Sun radius: 695500000 m. */ - double SUN_RADIUS = 6.955e8; + /** Sun radius: 695700000 m (source: resolution B3 from IAU 2015). */ + double SUN_RADIUS = 6.957e8; /** Moon equatorial radius: 1737400 m. */ double MOON_EQUATORIAL_RADIUS = 1737400.0; diff --git a/src/main/java/org/orekit/utils/DateDriver.java b/src/main/java/org/orekit/utils/DateDriver.java index 8a0eeb861a..8540ad97a6 100644 --- a/src/main/java/org/orekit/utils/DateDriver.java +++ b/src/main/java/org/orekit/utils/DateDriver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -67,7 +67,11 @@ public boolean isStart() { * @return shifted date */ public AbsoluteDate getDate() { - return base.shiftedBy(getValue()); + // date driver has no validity period, only 1 value is estimated + // over the all interval so there is no problem for calling getValue with null argument + // or any date, it would give the same result as there is only 1 span on the valueSpanMap + // of the driver + return base.shiftedBy(getValue(base)); } } diff --git a/src/main/java/org/orekit/utils/Differentiation.java b/src/main/java/org/orekit/utils/Differentiation.java index b00d205850..04b7855d07 100644 --- a/src/main/java/org/orekit/utils/Differentiation.java +++ b/src/main/java/org/orekit/utils/Differentiation.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -25,9 +25,10 @@ import org.orekit.attitudes.AttitudeProvider; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; +import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; /** Utility class for differentiating various kinds of functions. * @author Luc Maisonobe @@ -61,21 +62,21 @@ public static ParameterFunction differentiate(final ParameterFunction function, /** {@inheritDoc} */ @Override - public double value(final ParameterDriver driver) { + public double value(final ParameterDriver driver, final AbsoluteDate date) { final UnivariateFunction uf = new UnivariateFunction() { /** {@inheritDoc} */ @Override public double value(final double value) { - final double saved = driver.getValue(); - driver.setValue(value); - final double functionValue = function.value(driver); - driver.setValue(saved); + final double saved = driver.getValue(date); + driver.setValue(value, date); + final double functionValue = function.value(driver, date); + driver.setValue(saved, date); return functionValue; } }; - final DerivativeStructure dsParam = FACTORY.variable(0, driver.getValue()); + final DerivativeStructure dsParam = FACTORY.variable(0, driver.getValue(date)); final DerivativeStructure dsValue = differentiator.differentiate(uf).value(dsParam); return dsValue.getPartialDerivative(1); @@ -89,14 +90,14 @@ public double value(final double value) { * @param dimension dimension of the vector value of the function * @param provider attitude provider to use for modified states * @param orbitType type used to map the orbit to a one dimensional array - * @param positionAngle type of the position angle used for orbit mapping to array + * @param positionAngleType type of the position angle used for orbit mapping to array * @param dP user specified position error, used for step size computation for finite differences * @param nbPoints number of points used for finite differences * @return matrix function evaluating to the Jacobian of the original function */ public static StateJacobian differentiate(final StateFunction function, final int dimension, final AttitudeProvider provider, - final OrbitType orbitType, final PositionAngle positionAngle, + final OrbitType orbitType, final PositionAngleType positionAngleType, final double dP, final int nbPoints) { return new StateJacobian() { @@ -110,7 +111,7 @@ public double[][] value(final SpacecraftState state) { // compute partial derivatives with respect to state component j final UnivariateVectorFunction componentJ = new StateComponentFunction(j, function, provider, state, - orbitType, positionAngle); + orbitType, positionAngleType); final FiniteDifferencesDifferentiator differentiator = new FiniteDifferencesDifferentiator(nbPoints, tolerances[j]); final UnivariateDifferentiableVectorFunction differentiatedJ = @@ -146,7 +147,7 @@ private static class StateComponentFunction implements UnivariateVectorFunction private final OrbitType orbitType; /** Tpe of the position angle used for orbit mapping to array. */ - private final PositionAngle positionAngle; + private final PositionAngleType positionAngleType; /** Base state, of which only one component will change. */ private final SpacecraftState baseState; @@ -160,16 +161,16 @@ private static class StateComponentFunction implements UnivariateVectorFunction * @param provider attitude provider to use for modified states * @param baseState base state, of which only one component will change * @param orbitType type used to map the orbit to a one dimensional array - * @param positionAngle type of the position angle used for orbit mapping to array + * @param positionAngleType type of the position angle used for orbit mapping to array */ StateComponentFunction(final int index, final StateFunction f, final AttitudeProvider provider, final SpacecraftState baseState, - final OrbitType orbitType, final PositionAngle positionAngle) { + final OrbitType orbitType, final PositionAngleType positionAngleType) { this.index = index; this.f = f; this.provider = provider; this.orbitType = orbitType; - this.positionAngle = positionAngle; + this.positionAngleType = positionAngleType; this.baseState = baseState; } @@ -178,10 +179,10 @@ private static class StateComponentFunction implements UnivariateVectorFunction public double[] value(final double x) { final double[] array = new double[6]; final double[] arrayDot = new double[6]; - orbitType.mapOrbitToArray(baseState.getOrbit(), positionAngle, array, arrayDot); + orbitType.mapOrbitToArray(baseState.getOrbit(), positionAngleType, array, arrayDot); array[index] += x; final Orbit orbit = orbitType.mapArrayToOrbit(array, arrayDot, - positionAngle, + positionAngleType, baseState.getDate(), baseState.getMu(), baseState.getFrame()); diff --git a/src/main/java/org/orekit/utils/DoubleArrayDictionary.java b/src/main/java/org/orekit/utils/DoubleArrayDictionary.java index 1b02af0e51..810fd47ced 100644 --- a/src/main/java/org/orekit/utils/DoubleArrayDictionary.java +++ b/src/main/java/org/orekit/utils/DoubleArrayDictionary.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProvider.java b/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProvider.java index 0130c4a81a..ae1e7f34a6 100644 --- a/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProvider.java +++ b/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,8 +17,9 @@ package org.orekit.utils; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.orekit.frames.Frame; import org.orekit.time.FieldAbsoluteDate; @@ -38,6 +39,17 @@ default > FieldPVCoordinatesProvider toFiel return this::getPVCoordinates; } + /** Get the position of the body in the selected frame. + * @param date current date + * @param frame the frame where to define the position + * @param type for the field elements + * @return position of the body (m and) + * @since 12.0 + */ + default > FieldVector3D getPosition(final FieldAbsoluteDate date, final Frame frame) { + return getPVCoordinates(date, frame).getPosition(); + } + /** Get the {@link FieldPVCoordinates} of the body in the selected frame. * @param date current date * @param frame the frame where to define the position @@ -45,6 +57,6 @@ default > FieldPVCoordinatesProvider toFiel * @return time-stamped position/velocity of the body (m and m/s) */ >TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, - Frame frame); + Frame frame); } diff --git a/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProviderAdapter.java b/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProviderAdapter.java new file mode 100644 index 0000000000..8ff17bdba3 --- /dev/null +++ b/src/main/java/org/orekit/utils/ExtendedPVCoordinatesProviderAdapter.java @@ -0,0 +1,73 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.frames.FieldTransform; +import org.orekit.frames.Frame; +import org.orekit.frames.Transform; +import org.orekit.frames.TransformProvider; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Adapter from {@link ExtendedPVCoordinatesProvider} to {@link TransformProvider}. + *

        + * The transform provider is a simple translation from a defining frame such + * that the origin of the transformed frame corresponds to the moving point. + *

        + *

        + * This class is roughly the inverse of {@link FrameAdapter} + *

        + * @see FrameAdapter + * @since 12.0 + * @author Luc Maisonobe + */ +public class ExtendedPVCoordinatesProviderAdapter extends Frame { + + /** Serializable UID. */ + private static final long serialVersionUID = 20221215L; + + /** Simple constructor. + * @param parent parent frame (must be non-null) + * @param provider coordinates provider defining the position of origin of the transformed frame + * @param name name of the frame + */ + public ExtendedPVCoordinatesProviderAdapter(final Frame parent, + final ExtendedPVCoordinatesProvider provider, + final String name) { + super(parent, new TransformProvider() { + + /** Serializable UID. */ + private static final long serialVersionUID = 20221215L; + + /** {@inheritDoc} */ + @Override + public Transform getTransform(final AbsoluteDate date) { + return new Transform(date, provider.getPVCoordinates(date, parent).negate()); + } + + /** {@inheritDoc} */ + @Override + public > FieldTransform getTransform(final FieldAbsoluteDate date) { + return new FieldTransform<>(date, provider.getPVCoordinates(date, parent).negate()); + } + + }, name); + } + +} diff --git a/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java b/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java index 9f263ef60b..22e705734e 100644 --- a/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,30 +16,27 @@ */ package org.orekit.utils; -import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.FieldDerivative; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; -import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.time.FieldTimeInterpolable; import org.orekit.time.FieldTimeStamped; /** Field implementation of AbsolutePVCoordinates. * @see AbsolutePVCoordinates * @author Vincent Mouraux + * @param type of the field elements */ public class FieldAbsolutePVCoordinates> extends TimeStampedFieldPVCoordinates - implements FieldTimeStamped, FieldTimeInterpolable, T>, - FieldPVCoordinatesProvider { + implements FieldTimeStamped, FieldPVCoordinatesProvider { /** Frame in which are defined the coordinates. */ private final Frame frame; @@ -258,6 +255,12 @@ public FieldAbsolutePVCoordinates shiftedBy(final double dt) { */ public FieldPVCoordinatesProvider toTaylorProvider() { return new FieldPVCoordinatesProvider() { + /** {@inheritDoc} */ + public FieldVector3D getPosition(final FieldAbsoluteDate d, final Frame f) { + final TimeStampedFieldPVCoordinates shifted = shiftedBy(d.durationFrom(getDate())); + final FieldStaticTransform transform = frame.getStaticTransformTo(f, d); + return transform.transformPosition(shifted.getPosition()); + } /** {@inheritDoc} */ public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate d, final Frame f) { final TimeStampedFieldPVCoordinates shifted = shiftedBy(d.durationFrom(getDate())); @@ -281,6 +284,24 @@ public TimeStampedFieldPVCoordinates getPVCoordinates() { return this; } + /** Get the position in a specified frame. + * @param outputFrame frame in which the position coordinates shall be computed + * @return position + * @see #getPVCoordinates(Frame) + * @since 12.0 + */ + public FieldVector3D getPosition(final Frame outputFrame) { + // If output frame requested is the same as definition frame, + // Position vector is returned directly + if (outputFrame == frame) { + return getPosition(); + } + + // Else, position vector is transformed to output frame + final FieldStaticTransform t = frame.getStaticTransformTo(outputFrame, getDate()); + return t.transformPosition(getPosition()); + } + /** Get the TimeStampedFieldPVCoordinates in a specified frame. * @param outputFrame frame in which the position/velocity coordinates shall be computed * @return TimeStampedFieldPVCoordinates @@ -304,83 +325,6 @@ public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(outputFrame); } - @Override - public FieldAbsolutePVCoordinates interpolate(final FieldAbsoluteDate date, final Stream> sample) { - return interpolate(getFrame(), date, CartesianDerivativesFilter.USE_PVA, sample); - } - - /** Interpolate position-velocity. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * Note that even if first time derivatives (velocities) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the positions. - *

        - * @param frame frame for the interpolted instance - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @param the type of the field elements - * @return a new position-velocity, interpolated at specified date - * @exception OrekitIllegalArgumentException if some elements in the sample do not - * have the same defining frame as other - */ - public static > FieldAbsolutePVCoordinates interpolate(final Frame frame, final FieldAbsoluteDate date, - final CartesianDerivativesFilter filter, - final Stream> sample) { - - - // set up an interpolator taking derivatives into account - final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); - - // add sample points - switch (filter) { - case USE_P : - // populate sample with position data, ignoring velocity - sample.forEach(pv -> { - final FieldVector3D position = pv.getPosition(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray()); - }); - break; - case USE_PV : - // populate sample with position and velocity data - sample.forEach(pv -> { - final FieldVector3D position = pv.getPosition(); - final FieldVector3D velocity = pv.getVelocity(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray()); - }); - break; - case USE_PVA : - // populate sample with position, velocity and acceleration data - sample.forEach(pv -> { - final FieldVector3D position = pv.getPosition(); - final FieldVector3D velocity = pv.getVelocity(); - final FieldVector3D acceleration = pv.getAcceleration(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray(), acceleration.toArray()); - }); - break; - default : - // this should never happen - throw new OrekitInternalError(null); - } - - // interpolate - final T[][] p = interpolator.derivatives(date.getField().getZero(), 2); - - // build a new interpolated instance - return new FieldAbsolutePVCoordinates<>(frame, date, new FieldVector3D<>(p[0]), new FieldVector3D<>(p[1]), new FieldVector3D<>(p[2])); - } - /** * Converts to an AbsolutePVCoordinates instance. * @return AbsolutePVCoordinates with same properties diff --git a/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolator.java new file mode 100644 index 0000000000..7d2ec7ebe5 --- /dev/null +++ b/src/main/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolator.java @@ -0,0 +1,202 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.Frame; +import org.orekit.time.AbstractFieldTimeInterpolator; +import org.orekit.time.FieldAbsoluteDate; + +import java.util.List; + +/** + * Class using a Hermite interpolator to interpolate absolute position-velocity-acceleration coordinates. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see FieldHermiteInterpolator + * @see FieldAbsolutePVCoordinates + * @param type of the field elements + */ +public class FieldAbsolutePVCoordinatesHermiteInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** Filter for derivatives from the sample to use in interpolation. */ + private final CartesianDerivativesFilter filter; + + /** Output frame for the interpolated instance. */ + private final Frame outputFrame; + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param outputFrame frame for the interpolated instance + */ + public FieldAbsolutePVCoordinatesHermiteInterpolator(final Frame outputFrame) { + this(DEFAULT_INTERPOLATION_POINTS, outputFrame); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and two time derivatives during interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputFrame frame for the interpolated instance + */ + public FieldAbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final Frame outputFrame) { + this(interpolationPoints, outputFrame, CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param outputFrame frame for the interpolated instance + * @param filter filter for derivatives from the sample to use in interpolation + */ + public FieldAbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final Frame outputFrame, + final CartesianDerivativesFilter filter) { + super(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + this.outputFrame = outputFrame; + this.filter = filter; + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame frame for the interpolated instance + * @param filter filter for derivatives from the sample to use in interpolation + */ + public FieldAbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold, + final Frame outputFrame, final CartesianDerivativesFilter filter) { + super(interpolationPoints, extrapolationThreshold); + this.outputFrame = outputFrame; + this.filter = filter; + } + + /** Get filter for derivatives from the sample to use in interpolation. + * @return filter for derivatives from the sample to use in interpolation + */ + public CartesianDerivativesFilter getFilter() { + return filter; + } + + /** Get output frame for the interpolated instance. + * @return output frame for the interpolated instance + */ + public Frame getOutputFrame() { + return outputFrame; + } + + /** + * {@inheritDoc} + *

        + * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact + * derivative of position. + *

        + * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always + * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be + * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and + * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the + * positions. + */ + @Override + protected FieldAbsolutePVCoordinates interpolate(final InterpolationData interpolationData) { + + // Get interpolation date + final FieldAbsoluteDate date = interpolationData.getInterpolationDate(); + + // Get sample + final List> sample = interpolationData.getNeighborList(); + + // Set up an interpolator taking derivatives into account + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // Add sample points + switch (filter) { + case USE_P: + // Populate sample with position data, ignoring velocity + sample.forEach(pv -> { + final FieldVector3D position = pv.getPosition(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray()); + }); + break; + case USE_PV: + // Populate sample with position and velocity data + sample.forEach(pv -> { + final FieldVector3D position = pv.getPosition(); + final FieldVector3D velocity = pv.getVelocity(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray(), velocity.toArray()); + }); + break; + case USE_PVA: + // Populate sample with position, velocity and acceleration data + sample.forEach(pv -> { + final FieldVector3D position = pv.getPosition(); + final FieldVector3D velocity = pv.getVelocity(); + final FieldVector3D acceleration = pv.getAcceleration(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray(), velocity.toArray(), acceleration.toArray()); + }); + break; + default: + // this should never happen + throw new OrekitInternalError(null); + } + + // interpolate + final KK zero = interpolationData.getZero(); + final KK[][] p = interpolator.derivatives(zero, 2); + + // build a new interpolated instance + return new FieldAbsolutePVCoordinates<>(outputFrame, date, new FieldVector3D<>(p[0]), new FieldVector3D<>(p[1]), + new FieldVector3D<>(p[2])); + } +} diff --git a/src/main/java/org/orekit/utils/FieldAngularCoordinates.java b/src/main/java/org/orekit/utils/FieldAngularCoordinates.java index 16553b1af6..0081ea6cb3 100644 --- a/src/main/java/org/orekit/utils/FieldAngularCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldAngularCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -38,6 +38,7 @@ import org.hipparchus.util.MathArrays; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.time.FieldTimeShiftable; /** Simple container for rotation / rotation rate pairs, using {@link * CalculusFieldElement}. @@ -56,8 +57,8 @@ * @since 6.0 * @see AngularCoordinates */ -public class FieldAngularCoordinates> { - +public class FieldAngularCoordinates> + implements FieldTimeShiftable, T> { /** rotation. */ private final FieldRotation rotation; @@ -82,7 +83,7 @@ public FieldAngularCoordinates(final FieldRotation rotation, /** Builds a rotation / rotation rate / rotation acceleration triplet. * @param rotation i.e. the orientation of the vehicle - * @param rotationRate rotation rate rate Ω, i.e. the spin vector (rad/s) + * @param rotationRate rotation rate Ω, i.e. the spin vector (rad/s) * @param rotationAcceleration angular acceleration vector dΩ/dt (rad/s²) */ public FieldAngularCoordinates(final FieldRotation rotation, @@ -557,6 +558,68 @@ public FieldAngularCoordinates revert() { rotation.applyInverseTo(rotationAcceleration.negate())); } + /** Get a time-shifted rotation. Same as {@link #shiftedBy(double)} except + * only the shifted rotation is computed. + *

        + * The state can be slightly shifted to close dates. This shift is based on + * an approximate solution of the fixed acceleration motion. It is not + * intended as a replacement for proper attitude propagation but should be + * sufficient for either small time shifts or coarse accuracy. + *

        + * @param dt time shift in seconds + * @return a new state, shifted with respect to the instance (which is immutable) + * @see #shiftedBy(CalculusFieldElement) + * @since 11.2 + */ + public FieldRotation rotationShiftedBy(final T dt) { + + // the shiftedBy method is based on a local approximation. + // It considers separately the contribution of the constant + // rotation, the linear contribution or the rate and the + // quadratic contribution of the acceleration. The rate + // and acceleration contributions are small rotations as long + // as the time shift is small, which is the crux of the algorithm. + // Small rotations are almost commutative, so we append these small + // contributions one after the other, as if they really occurred + // successively, despite this is not what really happens. + + // compute the linear contribution first, ignoring acceleration + // BEWARE: there is really a minus sign here, because if + // the target frame rotates in one direction, the vectors in the origin + // frame seem to rotate in the opposite direction + final T rate = rotationRate.getNorm(); + final FieldRotation rateContribution = (rate.getReal() == 0.0) ? + FieldRotation.getIdentity(dt.getField()) : + new FieldRotation<>(rotationRate, rate.multiply(dt), RotationConvention.FRAME_TRANSFORM); + + // append rotation and rate contribution + final FieldRotation linearPart = + rateContribution.compose(rotation, RotationConvention.VECTOR_OPERATOR); + + final T acc = rotationAcceleration.getNorm(); + if (acc.getReal() == 0.0) { + // no acceleration, the linear part is sufficient + return linearPart; + } + + // compute the quadratic contribution, ignoring initial rotation and rotation rate + // BEWARE: there is really a minus sign here, because if + // the target frame rotates in one direction, the vectors in the origin + // frame seem to rotate in the opposite direction + final FieldRotation quadraticContribution = + new FieldRotation<>(rotationAcceleration, + acc.multiply(dt).multiply(dt).multiply(0.5), + RotationConvention.FRAME_TRANSFORM); + + // the quadratic contribution is a small rotation: + // its initial angle and angular rate are both zero. + // small rotations are almost commutative, so we append the small + // quadratic part after the linear part as a simple offset + return quadraticContribution + .compose(linearPart, RotationConvention.VECTOR_OPERATOR); + + } + /** Get a time-shifted state. *

        * The state can be slightly shifted to close dates. This shift is based on @@ -567,6 +630,7 @@ public FieldAngularCoordinates revert() { * @param dt time shift in seconds * @return a new state, shifted with respect to the instance (which is immutable) */ + @Override public FieldAngularCoordinates shiftedBy(final double dt) { return shiftedBy(rotation.getQ0().getField().getZero().add(dt)); } @@ -581,6 +645,7 @@ public FieldAngularCoordinates shiftedBy(final double dt) { * @param dt time shift in seconds * @return a new state, shifted with respect to the instance (which is immutable) */ + @Override public FieldAngularCoordinates shiftedBy(final T dt) { // the shiftedBy method is based on a local approximation. @@ -717,7 +782,7 @@ public AngularCoordinates toAngularCoordinates() { /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation */ public FieldPVCoordinates applyTo(final PVCoordinates pv) { @@ -738,7 +803,7 @@ public FieldPVCoordinates applyTo(final PVCoordinates pv) { /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation */ public TimeStampedFieldPVCoordinates applyTo(final TimeStampedPVCoordinates pv) { @@ -759,7 +824,7 @@ public TimeStampedFieldPVCoordinates applyTo(final TimeStampedPVCoordinates p /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation * @since 9.0 */ public FieldPVCoordinates applyTo(final FieldPVCoordinates pv) { @@ -781,7 +846,7 @@ public FieldPVCoordinates applyTo(final FieldPVCoordinates pv) { /** Apply the rotation to a pv coordinates. * @param pv vector to apply the rotation to - * @return a new pv coordinates which is the image of u by the rotation + * @return a new pv coordinates which is the image of pv by the rotation * @since 9.0 */ public TimeStampedFieldPVCoordinates applyTo(final TimeStampedFieldPVCoordinates pv) { diff --git a/src/main/java/org/orekit/utils/FieldArrayDictionary.java b/src/main/java/org/orekit/utils/FieldArrayDictionary.java index 393c0dd887..69ea6ba594 100644 --- a/src/main/java/org/orekit/utils/FieldArrayDictionary.java +++ b/src/main/java/org/orekit/utils/FieldArrayDictionary.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java b/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java index 29b3ec50da..14aa877080 100644 --- a/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java +++ b/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -30,6 +30,7 @@ *

        * @since 11.0 * @author Bryan Cazabonne + * @param type of the field elements */ public class FieldLegendrePolynomials> { diff --git a/src/main/java/org/orekit/utils/FieldPVCoordinates.java b/src/main/java/org/orekit/utils/FieldPVCoordinates.java index f9352619ad..224ce72ce6 100644 --- a/src/main/java/org/orekit/utils/FieldPVCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldPVCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,11 +23,13 @@ import org.hipparchus.analysis.differentiation.FieldDerivativeStructure; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; +import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldBlendable; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; -import org.orekit.time.TimeShiftable; +import org.orekit.time.FieldTimeShiftable; /** Simple container for Position/Velocity pairs, using {@link CalculusFieldElement}. *

        @@ -46,7 +48,7 @@ * @see PVCoordinates */ public class FieldPVCoordinates> - implements TimeShiftable> { + implements FieldTimeShiftable, T>, FieldBlendable, T> { /** The position. */ private final FieldVector3D position; @@ -624,6 +626,7 @@ public static > FieldVector3D estimateVeloc * @param dt time shift in seconds * @return a new state, shifted with respect to the instance (which is immutable) */ + @Override public FieldPVCoordinates shiftedBy(final double dt) { return new FieldPVCoordinates<>(new FieldVector3D<>(1, position, dt, velocity, 0.5 * dt * dt, acceleration), new FieldVector3D<>(1, velocity, dt, acceleration), @@ -640,16 +643,34 @@ public FieldPVCoordinates shiftedBy(final double dt) { * @param dt time shift in seconds * @return a new state, shifted with respect to the instance (which is immutable) */ + @Override public FieldPVCoordinates shiftedBy(final T dt) { final T one = dt.getField().getOne(); - return new FieldPVCoordinates<>(new FieldVector3D<>(one, position, - dt, velocity, - dt.multiply(dt).multiply(0.5), acceleration), - new FieldVector3D<>(one, velocity, - dt, acceleration), + return new FieldPVCoordinates<>(positionShiftedBy(dt), + new FieldVector3D<>(one, velocity, dt, acceleration), acceleration); } + /** + * Get a time-shifted position. Same as {@link #shiftedBy(CalculusFieldElement)} except + * that only the sifted position is returned. + *

        + * The state can be slightly shifted to close dates. This shift is based on + * a simple Taylor expansion. It is not intended as a replacement + * for proper orbit propagation (it is not even Keplerian!) but should be + * sufficient for either small time shifts or coarse accuracy. + *

        + * + * @param dt time shift in seconds + * @return a new state, shifted with respect to the instance (which is + * immutable) + * @since 11.2 + */ + public FieldVector3D positionShiftedBy(final T dt) { + final T one = dt.getField().getOne(); + return new FieldVector3D<>(one, position, dt, velocity, dt.multiply(dt).multiply(0.5), acceleration); + } + /** Gets the position. * @return the position vector (m). */ @@ -776,4 +797,15 @@ public String toString() { append(acceleration.getZ().getReal()).append(")}").toString(); } + /** {@inheritDoc} */ + @Override + public FieldPVCoordinates blendArithmeticallyWith(final FieldPVCoordinates other, + final T blendingValue) + throws MathIllegalArgumentException { + final FieldVector3D blendedPosition = position.blendArithmeticallyWith(other.getPosition(), blendingValue); + final FieldVector3D blendedVelocity = velocity.blendArithmeticallyWith(other.getVelocity(), blendingValue); + final FieldVector3D blendedAcceleration = acceleration.blendArithmeticallyWith(other.getAcceleration(), blendingValue); + + return new FieldPVCoordinates<>(blendedPosition, blendedVelocity, blendedAcceleration); + } } diff --git a/src/main/java/org/orekit/utils/FieldPVCoordinatesProvider.java b/src/main/java/org/orekit/utils/FieldPVCoordinatesProvider.java index 7457518ef1..5a02606cb0 100644 --- a/src/main/java/org/orekit/utils/FieldPVCoordinatesProvider.java +++ b/src/main/java/org/orekit/utils/FieldPVCoordinatesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +18,7 @@ package org.orekit.utils; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.orekit.frames.Frame; import org.orekit.time.FieldAbsoluteDate; @@ -32,6 +33,16 @@ */ public interface FieldPVCoordinatesProvider> { + /** Get the position of the body in the selected frame. + * @param date current date + * @param frame the frame where to define the position + * @return position of the body (m and) + * @since 12.0 + */ + default FieldVector3D getPosition(final FieldAbsoluteDate date, final Frame frame) { + return getPVCoordinates(date, frame).getPosition(); + } + /** Get the {@link FieldPVCoordinates} of the body in the selected frame. * @param date current date * @param frame the frame where to define the position diff --git a/src/main/java/org/orekit/utils/FieldTimeSpanMap.java b/src/main/java/org/orekit/utils/FieldTimeSpanMap.java index b80c6d0512..a1ce0aac8f 100644 --- a/src/main/java/org/orekit/utils/FieldTimeSpanMap.java +++ b/src/main/java/org/orekit/utils/FieldTimeSpanMap.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -31,6 +31,7 @@ /** Container for objects that apply to spans of time. * @param Type of the data. + * @param type of the field elements * @author Luc Maisonobe * @since 7.1 @@ -165,7 +166,10 @@ public SortedSet> getTransitions() { return Collections.unmodifiableSortedSet(data); } - /** Local class holding transition times. */ + /** Local class holding transition times. + * @param type of the field elements + * @param type of the data + */ public static class Transition> implements TimeStamped { /** Transition date. */ diff --git a/src/main/java/org/orekit/utils/FieldTimeStampedCache.java b/src/main/java/org/orekit/utils/FieldTimeStampedCache.java new file mode 100644 index 0000000000..d12fcbd2cf --- /dev/null +++ b/src/main/java/org/orekit/utils/FieldTimeStampedCache.java @@ -0,0 +1,83 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeStamped; + +import java.util.stream.Stream; + +/** + * Interface for a data structure that can provide concurrent access to {@link FieldTimeStamped} data surrounding a given + * date. + * + * @param the type of data + * @param type of the field element + * + * @author Luc Maisonobe + * @author Evan Ward + * @author Vincent Cucchietti + * @see ImmutableFieldTimeStampedCache + */ +public interface FieldTimeStampedCache, KK extends CalculusFieldElement> { + + /** + * Get the entries surrounding a central date. + *

        + * If the central date is well within covered range, the returned array will be balanced with half the points before + * central date and half the points after it (depending on n parity, of course). If the central date is near the + * boundary, then the returned array will be unbalanced and will contain only the n earliest (or latest) entries. A + * typical example of the later case is leap seconds cache, since the number of leap seconds cannot be arbitrarily + * increased. + *

        + * This method is safe for multiple threads to execute concurrently. + * + * @param central central date + * + * @return list of cached entries surrounding the specified date. The size of the list is guaranteed to be + * {@link #getNeighborsSize()}. + */ + Stream getNeighbors(FieldAbsoluteDate central); + + /** + * Get the fixed size of the lists returned by {@link #getNeighbors(FieldAbsoluteDate)}. + * + * @return size of the list + */ + int getNeighborsSize(); + + /** + * Get the earliest entry in this cache. + * + * @return earliest cached entry + * + * @throws IllegalStateException if this cache is empty + */ + T getEarliest() + throws IllegalStateException; + + /** + * Get the latest entry in this cache. + * + * @return latest cached entry + * + * @throws IllegalStateException if this cache is empty + */ + T getLatest() + throws IllegalStateException; +} diff --git a/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java b/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java new file mode 100644 index 0000000000..ad272fc465 --- /dev/null +++ b/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java @@ -0,0 +1,77 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; + +/** Container for azimut/elevation/range coordinates as seen from a ground point. + * @param the type of the field elements + * @see org.orekit.frames.TopocentricFrame + * @since 12.0 + */ +public class FieldTrackingCoordinates> { + + /** Azimuth. */ + private final T azimuth; + + /** Elevation. */ + private final T elevation; + + /** Range. */ + private final T range; + + /** Simple constructor. + * @param azimuth azimuth + * @param elevation elevation + * @param range range + */ + public FieldTrackingCoordinates(final T azimuth, final T elevation, final T range) { + this.azimuth = azimuth; + this.elevation = elevation; + this.range = range; + } + + /** Get the azimuth. + *

        The azimuth is the angle between the North direction at local point and + * the projection in local horizontal plane of the direction from local point + * to given point. Azimuth angles are counted clockwise, i.e positive towards the East.

        + * @return azimuth + */ + public T getAzimuth() { + return azimuth; + } + + /** Get the elevation. + *

        The elevation is the angle between the local horizontal and + * the direction from local point to given point.

        + * @return elevation + */ + public T getElevation() { + return elevation; + } + + /** Get the range. + * @return range + */ + public T getRange() { + return range; + } + +} + + + diff --git a/src/main/java/org/orekit/utils/Fieldifier.java b/src/main/java/org/orekit/utils/Fieldifier.java new file mode 100644 index 0000000000..b6c5d0f12c --- /dev/null +++ b/src/main/java/org/orekit/utils/Fieldifier.java @@ -0,0 +1,200 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.linear.FieldMatrix; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.orekit.errors.OrekitInternalError; +import org.orekit.frames.Frame; +import org.orekit.orbits.CircularOrbit; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.orbits.FieldCircularOrbit; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldStateCovariance; +import org.orekit.propagation.StateCovariance; +import org.orekit.time.FieldAbsoluteDate; + +/** + * Utility class used to convert class to their Field equivalent. + * + * @author Vincent Cucchietti + */ +public class Fieldifier { + + /** Private constructor. */ + private Fieldifier() { + // Empty constructor + } + + /** + * Fieldify given orbit with given field. + *

        + * Conserve derivatives and return orbit in same orbit type as input orbit. + * + * @param field field to fieldify with + * @param orbit orbit to fieldify + * @param type of the elements + * + * @return fielded orbit + */ + public static > FieldOrbit fieldify(final Field field, final Orbit orbit) { + + final T one = field.getOne(); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, orbit.getDate()); + final T fieldMu = one.multiply(orbit.getMu()); + final Frame frame = orbit.getFrame(); + + switch (orbit.getType()) { + case CIRCULAR: { + final CircularOrbit circOrbit = (CircularOrbit) OrbitType.CIRCULAR.convertType(orbit); + + // Get orbital elements + final T a = one.multiply(circOrbit.getA()); + final T ex = one.multiply(circOrbit.getCircularEx()); + final T ey = one.multiply(circOrbit.getCircularEy()); + final T i = one.multiply(circOrbit.getI()); + final T raan = one.multiply(circOrbit.getRightAscensionOfAscendingNode()); + final T alphaM = one.multiply(circOrbit.getAlphaM()); + + // Get derivatives + final T aDot = one.multiply(circOrbit.getADot()); + final T exDot = one.multiply(circOrbit.getCircularExDot()); + final T eyDot = one.multiply(circOrbit.getCircularEyDot()); + final T iDot = one.multiply(circOrbit.getIDot()); + final T raanDot = one.multiply(circOrbit.getRightAscensionOfAscendingNodeDot()); + final T alphaMDot = one.multiply(circOrbit.getAlphaMDot()); + + return new FieldCircularOrbit<>(a, ex, ey, i, raan, alphaM, aDot, exDot, eyDot, iDot, raanDot, alphaMDot, + PositionAngleType.MEAN, frame, fieldDate, fieldMu); + } + + case CARTESIAN: { + final FieldPVCoordinates orbitPV = new FieldPVCoordinates<>(field, orbit.getPVCoordinates()); + + return new FieldCartesianOrbit<>(orbitPV, orbit.getFrame(), fieldDate, fieldMu); + } + + case KEPLERIAN: { + final KeplerianOrbit kepOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); + + // Get orbital elements + final T a = one.multiply(kepOrbit.getA()); + final T e = one.multiply(kepOrbit.getE()); + final T i = one.multiply(kepOrbit.getI()); + final T raan = one.multiply(kepOrbit.getRightAscensionOfAscendingNode()); + final T pa = one.multiply(kepOrbit.getPerigeeArgument()); + final T meanAnomaly = one.multiply(kepOrbit.getMeanAnomaly()); + + // Get derivatives + final T aDot = one.multiply(kepOrbit.getADot()); + final T eDot = one.multiply(kepOrbit.getEDot()); + final T iDot = one.multiply(kepOrbit.getIDot()); + final T raanDot = one.multiply(kepOrbit.getRightAscensionOfAscendingNodeDot()); + final T paDot = one.multiply(kepOrbit.getPerigeeArgumentDot()); + final T meanAnomalyDot = one.multiply(kepOrbit.getMeanAnomalyDot()); + + return new FieldKeplerianOrbit<>(a, e, i, pa, raan, meanAnomaly, aDot, eDot, iDot, paDot, raanDot, + meanAnomalyDot, PositionAngleType.MEAN, frame, fieldDate, fieldMu); + } + case EQUINOCTIAL: { + final EquinoctialOrbit equiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit); + + // Get orbital elements + final T a = one.multiply(equiOrbit.getA()); + final T ex = one.multiply(equiOrbit.getEquinoctialEx()); + final T ey = one.multiply(equiOrbit.getEquinoctialEy()); + final T hx = one.multiply(equiOrbit.getHx()); + final T hy = one.multiply(equiOrbit.getHy()); + final T lm = one.multiply(equiOrbit.getLM()); + + // Get derivatives + final T aDot = one.multiply(equiOrbit.getADot()); + final T exDot = one.multiply(equiOrbit.getEquinoctialExDot()); + final T eyDot = one.multiply(equiOrbit.getEquinoctialEyDot()); + final T hxDot = one.multiply(equiOrbit.getHxDot()); + final T hyDot = one.multiply(equiOrbit.getHyDot()); + final T lmDot = one.multiply(equiOrbit.getLMDot()); + + return new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy, lm, aDot, exDot, eyDot, hxDot, hyDot, + lmDot, PositionAngleType.MEAN, frame, fieldDate, fieldMu); + } + default: + // Should never happen + throw new OrekitInternalError(null); + } + + } + + /** + * Fieldify given matrix with given field. + * + * @param field field to fieldify with + * @param matrix matrix to fieldify + * @param type of the elements + * + * @return fielded matrix + */ + public static > FieldMatrix fieldify(final Field field, + final RealMatrix matrix) { + + final int rowDim = matrix.getRowDimension(); + final int columnDim = matrix.getColumnDimension(); + + final FieldMatrix fieldMatrix = MatrixUtils.createFieldMatrix(field, rowDim, columnDim); + + for (int i = 0; i < rowDim; i++) { + for (int j = 0; j < columnDim; j++) { + fieldMatrix.setEntry(i, j, field.getOne().multiply(matrix.getEntry(i, j))); + } + } + + return fieldMatrix; + } + + /** + * Fieldify given state covariance with given field. + * + * @param field field to which the + * @param stateCovariance state covariance to fieldify + * @param type of the elements + * + * @return fielded state covariance + * + * @since 12.0 + */ + public static > FieldStateCovariance fieldify(final Field field, + final StateCovariance stateCovariance) { + final FieldMatrix fieldMatrix = fieldify(field, stateCovariance.getMatrix()); + final FieldAbsoluteDate fieldEpoch = new FieldAbsoluteDate<>(field, stateCovariance.getDate()); + if (stateCovariance.getLOF() == null) { + return new FieldStateCovariance<>(fieldMatrix, fieldEpoch, stateCovariance.getFrame(), + stateCovariance.getOrbitType(), stateCovariance.getPositionAngleType()); + } + return new FieldStateCovariance<>(fieldMatrix, fieldEpoch, stateCovariance.getLOF()); + } + +} diff --git a/src/main/java/org/orekit/utils/FrameAdapter.java b/src/main/java/org/orekit/utils/FrameAdapter.java new file mode 100644 index 0000000000..57dfb4b7c4 --- /dev/null +++ b/src/main/java/org/orekit/utils/FrameAdapter.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Adapter from {@link Frame} to {@link ExtendedPVCoordinatesProvider}. + *

        + * The moving point is the origin of the adapted frame. + *

        + *

        + * This class is roughly the inverse of {@link ExtendedPVCoordinatesProviderAdapter} + *

        + * @see ExtendedPVCoordinatesProviderAdapter + * @since 12.0 + * @author Luc Maisonobe + */ +public class FrameAdapter implements ExtendedPVCoordinatesProvider { + + /** Frame whose origin coordinates are desired. */ + private final Frame originFrame; + + /** Simple constructor. + * @param originFrame frame whose origin coordinates are desired + */ + public FrameAdapter(final Frame originFrame) { + this.originFrame = originFrame; + } + + /** {@inheritDoc} */ + @Override + public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { + return new TimeStampedPVCoordinates(date, + originFrame. + getTransformTo(frame, date). + transformPVCoordinates(PVCoordinates.ZERO)); + } + + /** {@inheritDoc} */ + @Override + public > TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, + final Frame frame) { + return new TimeStampedFieldPVCoordinates<>(date, + originFrame. + getTransformTo(frame, date). + transformPVCoordinates(PVCoordinates.ZERO)); + } + +} diff --git a/src/main/java/org/orekit/utils/GenericTimeStampedCache.java b/src/main/java/org/orekit/utils/GenericTimeStampedCache.java index 5044ad5bdf..5bf9ab29b5 100644 --- a/src/main/java/org/orekit/utils/GenericTimeStampedCache.java +++ b/src/main/java/org/orekit/utils/GenericTimeStampedCache.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -27,6 +27,7 @@ import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; @@ -63,8 +64,11 @@ public class GenericTimeStampedCache implements TimeStamp /** Generator to use for yet non-cached data. */ private final TimeStampedGenerator generator; - /** Number of entries in a neighbors array. */ - private final int neighborsSize; + /** Overriding mean step. */ + private final double overridingMeanStep; + + /** Maximum number of entries in a neighbors array. */ + private final int maxNeighborsSize; /** Independent time slots cached. */ private final List slots; @@ -82,8 +86,8 @@ public class GenericTimeStampedCache implements TimeStamp private final ReadWriteLock lock; /** Simple constructor. - * @param neighborsSize fixed size of the arrays to be returned by {@link - * #getNeighbors(AbsoluteDate)}, must be at least 2 + * @param maxNeighborsSize maximum size of the arrays to be returned by {@link + * #getNeighbors(AbsoluteDate, int)}, must be at least 2 * @param maxSlots maximum number of independent cached time slots * @param maxSpan maximum duration span in seconds of one slot * (can be set to {@code Double.POSITIVE_INFINITY} if desired) @@ -91,29 +95,58 @@ public class GenericTimeStampedCache implements TimeStamp * instead of extending an existing one * @param generator generator to use for yet non-existent data */ - public GenericTimeStampedCache(final int neighborsSize, final int maxSlots, final double maxSpan, + public GenericTimeStampedCache(final int maxNeighborsSize, final int maxSlots, final double maxSpan, final double newSlotInterval, final TimeStampedGenerator generator) { + this(maxNeighborsSize, maxSlots, maxSpan, newSlotInterval, generator, Double.NaN); + + } + + /** Simple constructor with overriding minimum step. + * @param maxNeighborsSize maximum size of the arrays to be returned by {@link + * #getNeighbors(AbsoluteDate, int)}, must be at least 2 + * @param maxSlots maximum number of independent cached time slots + * @param maxSpan maximum duration span in seconds of one slot + * (can be set to {@code Double.POSITIVE_INFINITY} if desired) + * @param newSlotInterval time interval above which a new slot is created + * instead of extending an existing one + * @param generator generator to use for yet non-existent data + * @param overridingMeanStep overriding mean step designed for non-homogeneous tabulated values. To be used for example + * when caching monthly tabulated values. Use {@code Double.NaN} otherwise. + * @throws OrekitIllegalArgumentException if : + *
          + *
        • neighbors size < 2
        • + *
        • maximum allowed number of slots < 1
        • + *
        • minimum step ≤ 0
        • + *
        + */ + public GenericTimeStampedCache(final int maxNeighborsSize, final int maxSlots, final double maxSpan, + final double newSlotInterval, final TimeStampedGenerator generator, + final double overridingMeanStep) { // safety check if (maxSlots < 1) { throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, maxSlots, 1); } - if (neighborsSize < 2) { - throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS, - neighborsSize, 2); + if (overridingMeanStep <= 0) { + throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED, + overridingMeanStep, 0); + } + if (maxNeighborsSize < 2) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS, maxNeighborsSize, 2); } - this.reference = new AtomicReference(); - this.maxSlots = maxSlots; - this.maxSpan = maxSpan; - this.newSlotQuantumGap = FastMath.round(newSlotInterval / QUANTUM_STEP); - this.generator = generator; - this.neighborsSize = neighborsSize; - this.slots = new ArrayList(maxSlots); - this.getNeighborsCalls = new AtomicInteger(0); - this.generateCalls = new AtomicInteger(0); - this.evictions = new AtomicInteger(0); - this.lock = new ReentrantReadWriteLock(); + this.reference = new AtomicReference<>(); + this.maxSlots = maxSlots; + this.maxSpan = maxSpan; + this.newSlotQuantumGap = FastMath.round(newSlotInterval / QUANTUM_STEP); + this.generator = generator; + this.overridingMeanStep = overridingMeanStep; + this.maxNeighborsSize = maxNeighborsSize; + this.slots = new ArrayList<>(maxSlots); + this.getNeighborsCalls = new AtomicInteger(0); + this.generateCalls = new AtomicInteger(0); + this.evictions = new AtomicInteger(0); + this.lock = new ReentrantReadWriteLock(); } @@ -221,11 +254,8 @@ public int getEntries() { } - /** Get the earliest cached entry. - * @return earliest cached entry - * @exception IllegalStateException if the cache has no slots at all - * @see #getSlots() - */ + /** {@inheritDoc} */ + @Override public T getEarliest() throws IllegalStateException { lock.readLock().lock(); @@ -240,11 +270,8 @@ public T getEarliest() throws IllegalStateException { } - /** Get the latest cached entry. - * @return latest cached entry - * @exception IllegalStateException if the cache has no slots at all - * @see #getSlots() - */ + /** {@inheritDoc} */ + @Override public T getLatest() throws IllegalStateException { lock.readLock().lock(); @@ -259,37 +286,25 @@ public T getLatest() throws IllegalStateException { } - /** Get the fixed size of the arrays to be returned by {@link #getNeighbors(AbsoluteDate)}. - * @return size of the array - */ - public int getNeighborsSize() { - return neighborsSize; + /** {@inheritDoc} */ + @Override + public int getMaxNeighborsSize() { + return maxNeighborsSize; } - /** Get the entries surrounding a central date. - *

        - * If the central date is well within covered range, the returned array - * will be balanced with half the points before central date and half the - * points after it (depending on n parity, of course). If the central date - * is near the generator range boundary, then the returned array will be - * unbalanced and will contain only the n earliest (or latest) generated - * (and cached) entries. A typical example of the later case is leap seconds - * cache, since the number of leap seconds cannot be arbitrarily increased. - *

        - * @param central central date - * @return array of cached entries surrounding specified date (the size - * of the array is fixed to the one specified in the {@link - * #GenericTimeStampedCache(int, int, double, double, TimeStampedGenerator)} - * @see #getEarliest() - * @see #getLatest() - */ - public Stream getNeighbors(final AbsoluteDate central) { + /** {@inheritDoc} */ + @Override + public Stream getNeighbors(final AbsoluteDate central, final int n) { + + if (n > maxNeighborsSize) { + throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, maxNeighborsSize); + } lock.readLock().lock(); try { getNeighborsCalls.incrementAndGet(); final long dateQuantum = quantum(central); - return selectSlot(central, dateQuantum).getNeighbors(central, dateQuantum); + return selectSlot(central, dateQuantum).getNeighbors(central, dateQuantum, n); } finally { lock.readLock().unlock(); } @@ -438,7 +453,7 @@ private final class Slot { Slot(final AbsoluteDate date) { // allocate cache - this.cache = new ArrayList(); + this.cache = new ArrayList<>(); // set up first entries AbsoluteDate generationDate = date; @@ -450,7 +465,7 @@ private final class Slot { earliestQuantum = new AtomicLong(cache.get(0).getQuantum()); latestQuantum = new AtomicLong(cache.get(cache.size() - 1).getQuantum()); - while (cache.size() < neighborsSize) { + while (cache.size() < maxNeighborsSize) { // we need to generate more entries final AbsoluteDate entry0 = cache.get(0).getData().getDate(); @@ -461,12 +476,12 @@ private final class Slot { if (entryN.getDate().durationFrom(date) <= date.durationFrom(entry0.getDate())) { // generate additional point at the end of the slot existingDate = entryN; - generationDate = entryN.getDate().shiftedBy(getMeanStep() * (neighborsSize - cache.size())); + generationDate = entryN.getDate().shiftedBy(getMeanStep() * (maxNeighborsSize - cache.size())); appendAtEnd(generateAndCheck(existingDate, generationDate), date); } else { // generate additional point at the start of the slot existingDate = entry0; - generationDate = entry0.getDate().shiftedBy(-getMeanStep() * (neighborsSize - cache.size())); + generationDate = entry0.getDate().shiftedBy(-getMeanStep() * (maxNeighborsSize - cache.size())); insertAtStart(generateAndCheck(existingDate, generationDate), date); } @@ -505,14 +520,16 @@ public long getLatestQuantum() { return latestQuantum.get(); } - /** Get the number of entries contained din the slot. - * @return number of entries contained din the slot + /** Get the number of entries contained in the slot. + * @return number of entries contained in the slot */ public int getEntries() { return cache.size(); } /** Get the mean step between entries. + *

        + * If an overriding mean step has been defined at construction, then it will be returned instead. * @return mean step between entries (or an arbitrary non-null value * if there are fewer than 2 entries) */ @@ -520,9 +537,13 @@ private double getMeanStep() { if (cache.size() < 2) { return 1.0; } else { - final AbsoluteDate t0 = cache.get(0).getData().getDate(); - final AbsoluteDate tn = cache.get(cache.size() - 1).getData().getDate(); - return tn.durationFrom(t0) / (cache.size() - 1); + if (!Double.isNaN(overridingMeanStep)) { + return overridingMeanStep; + } else { + final AbsoluteDate t0 = cache.get(0).getData().getDate(); + final AbsoluteDate tn = cache.get(cache.size() - 1).getData().getDate(); + return tn.durationFrom(t0) / (cache.size() - 1); + } } } @@ -546,16 +567,14 @@ public long getLastAccess() { *

        * @param central central date * @param dateQuantum global quantum of the date + * @param n number of neighbors * @return a new array containing date neighbors - * @see #getBefore(AbsoluteDate) - * @see #getAfter(AbsoluteDate) */ - public Stream getNeighbors(final AbsoluteDate central, final long dateQuantum) { - + public Stream getNeighbors(final AbsoluteDate central, final long dateQuantum, final int n) { int index = entryIndex(central, dateQuantum); - int firstNeighbor = index - (neighborsSize - 1) / 2; + int firstNeighbor = index - (n - 1) / 2; - if (firstNeighbor < 0 || firstNeighbor + neighborsSize > cache.size()) { + if (firstNeighbor < 0 || firstNeighbor + n > cache.size()) { // the cache is not balanced around the desired date, we can try to generate new data // upgrade the read lock to a write lock so we can change the list of available slots @@ -568,8 +587,8 @@ public Stream getNeighbors(final AbsoluteDate central, final long dateQuantum boolean loop = true; while (loop) { index = entryIndex(central, dateQuantum); - firstNeighbor = index - (neighborsSize - 1) / 2; - if (firstNeighbor < 0 || firstNeighbor + neighborsSize > cache.size()) { + firstNeighbor = index - (n - 1) / 2; + if (firstNeighbor < 0 || firstNeighbor + n > cache.size()) { // estimate which data we need to be generated final double step = getMeanStep(); @@ -582,7 +601,7 @@ public Stream getNeighbors(final AbsoluteDate central, final long dateQuantum simplyRebalance = existingDate.getDate().compareTo(central) <= 0; } else { existingDate = cache.get(cache.size() - 1).getData().getDate(); - generationDate = existingDate.getDate().shiftedBy(step * (firstNeighbor + neighborsSize - cache.size())); + generationDate = existingDate.getDate().shiftedBy(step * (firstNeighbor + n - cache.size())); simplyRebalance = existingDate.getDate().compareTo(central) >= 0; } generateCalls.incrementAndGet(); @@ -617,16 +636,16 @@ public Stream getNeighbors(final AbsoluteDate central, final long dateQuantum } - if (firstNeighbor + neighborsSize > cache.size()) { + if (firstNeighbor + n > cache.size()) { // we end up with a non-balanced neighborhood, // adjust the start point to fit within the cache - firstNeighbor = cache.size() - neighborsSize; + firstNeighbor = cache.size() - n; } if (firstNeighbor < 0) { firstNeighbor = 0; } final Stream.Builder builder = Stream.builder(); - for (int i = 0; i < neighborsSize; ++i) { + for (int i = 0; i < n; ++i) { builder.accept(cache.get(firstNeighbor + i).getData()); } @@ -732,7 +751,7 @@ private void insertAtStart(final List data, final AbsoluteDate requestedDate) // evict excess data at end final AbsoluteDate t0 = cache.get(0).getData().getDate(); - while (cache.size() > neighborsSize && + while (cache.size() > maxNeighborsSize && cache.get(cache.size() - 1).getData().getDate().durationFrom(t0) > maxSpan) { cache.remove(cache.size() - 1); } @@ -772,7 +791,7 @@ private void appendAtEnd(final List data, final AbsoluteDate requestedDate) { // evict excess data at start final AbsoluteDate tn = cache.get(cache.size() - 1).getData().getDate(); - while (cache.size() > neighborsSize && + while (cache.size() > maxNeighborsSize && tn.durationFrom(cache.get(0).getData().getDate()) > maxSpan) { cache.remove(0); } diff --git a/src/main/java/org/orekit/utils/IERSConventions.java b/src/main/java/org/orekit/utils/IERSConventions.java index f048fa9285..45346ce05c 100644 --- a/src/main/java/org/orekit/utils/IERSConventions.java +++ b/src/main/java/org/orekit/utils/IERSConventions.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java b/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java new file mode 100644 index 0000000000..6940224662 --- /dev/null +++ b/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java @@ -0,0 +1,283 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitIllegalStateException; +import org.orekit.errors.OrekitMessages; +import org.orekit.errors.TimeStampedCacheException; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldChronologicalComparator; +import org.orekit.time.FieldTimeStamped; +import org.orekit.time.TimeStamped; + +/** + * A cache of {@link TimeStamped} data that provides concurrency through immutability. This strategy is suitable when all the + * cached data is stored in memory. (For example, {@link org.orekit.time.UTCScale UTCScale}) This class then provides + * convenient methods for accessing the data. + * + * @param the type of data + * @param the type the field element + * + * @author Evan Ward + * @author Vincent Cucchietti + */ +public class ImmutableFieldTimeStampedCache, KK extends CalculusFieldElement> + implements FieldTimeStampedCache { + + /** + * the cached data. Be careful not to modify it after the constructor, or return a reference that allows mutating this + * list. + */ + private final List data; + + /** the size list to return from {@link #getNeighbors(FieldAbsoluteDate)}. */ + private final int neighborsSize; + + /** Earliest date. + * @since 12.0 + */ + private final FieldAbsoluteDate earliestDate; + + /** Latest date. + * @since 12.0 + */ + private final FieldAbsoluteDate latestDate; + + /** + * Create a new cache with the given neighbors size and data. + * + * @param neighborsSize the size of the list returned from {@link #getNeighbors(FieldAbsoluteDate)}. Must be less than or + * equal to {@code data.size()}. + * @param data the backing data for this cache. The list will be copied to ensure immutability. To guarantee immutability + * the entries in {@code data} must be immutable themselves. There must be more data than {@code neighborsSize}. + * + * @throws IllegalArgumentException if {@code neighborsSize > data.size()} or if {@code neighborsSize} is negative + */ + public ImmutableFieldTimeStampedCache(final int neighborsSize, + final Collection data) { + // Parameter check + if (neighborsSize > data.size()) { + throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS, + data.size(), neighborsSize); + } + if (neighborsSize < 1) { + throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, + neighborsSize, 0); + } + + // Assign instance variables + this.neighborsSize = neighborsSize; + + // Sort and copy data first + this.data = new ArrayList<>(data); + Collections.sort(this.data, new FieldChronologicalComparator<>()); + + this.earliestDate = this.data.get(0).getDate(); + this.latestDate = this.data.get(this.data.size() - 1).getDate(); + + } + + /** + * private constructor for {@link #EMPTY_CACHE}. + * @param field field to which the elements belong + */ + private ImmutableFieldTimeStampedCache(final Field field) { + this.data = null; + this.neighborsSize = 0; + this.earliestDate = FieldAbsoluteDate.getArbitraryEpoch(field); + this.latestDate = FieldAbsoluteDate.getArbitraryEpoch(field); + } + + /** + * Get an empty immutable cache, cast to the correct type. + * + * @param the type of data + * @param the type of the calculus field element + * @param field field to which the elements belong + * @return an empty {@link ImmutableTimeStampedCache}. + */ + public static , CFE extends CalculusFieldElement> + ImmutableFieldTimeStampedCache emptyCache(final Field field) { + return new EmptyFieldTimeStampedCache<>(field); + } + + /** {@inheritDoc} */ + public Stream getNeighbors(final FieldAbsoluteDate central) { + + // Find central index + final int i = findIndex(central); + + // Check index in the range of the data + if (i < 0) { + final FieldAbsoluteDate earliest = this.getEarliest().getDate(); + throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, + earliest, central, earliest.durationFrom(central).getReal()); + } + else if (i >= this.data.size()) { + final FieldAbsoluteDate latest = this.getLatest().getDate(); + throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, + latest, central, central.durationFrom(latest).getReal()); + } + + // Force unbalanced range if necessary + int start = FastMath.max(0, i - (this.neighborsSize - 1) / 2); + final int end = FastMath.min(this.data.size(), start + + this.neighborsSize); + start = end - this.neighborsSize; + + // Return list without copying + return this.data.subList(start, end).stream(); + } + + /** {@inheritDoc} */ + public int getNeighborsSize() { + return this.neighborsSize; + } + + /** {@inheritDoc} */ + public T getEarliest() { + return this.data.get(0); + } + + /** {@inheritDoc} */ + public T getLatest() { + return this.data.get(this.data.size() - 1); + } + + /** + * Get all the data in this cache. + * + * @return a sorted collection of all data passed in the + * {@link #ImmutableFieldTimeStampedCache(int, Collection) constructor}. + */ + public List getAll() { + return Collections.unmodifiableList(this.data); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "Immutable cache with " + this.data.size() + " entries"; + } + + /** + * Find the index, i, to {@link #data} such that {@code data[i] <= t} and {@code data[i+1] > t} if {@code data[i+1]} + * exists. + * + * @param t the time + * + * @return the index of the data at or just before {@code t}, {@code -1} if {@code t} is before the first entry, or + * {@code data.size()} if {@code t} is after the last entry. + */ + private int findIndex(final FieldAbsoluteDate t) { + // left bracket of search algorithm + int iInf = 0; + KK dtInf = t.durationFrom(earliestDate); + if (dtInf.getReal() < 0) { + // before first entry + return -1; + } + + // right bracket of search algorithm + int iSup = data.size() - 1; + KK dtSup = t.durationFrom(latestDate); + if (dtSup.getReal() > 0) { + // after last entry + return data.size(); + } + + // search entries, using linear interpolation + // this should take only 2 iterations for near linear entries (most frequent use case) + // regardless of the number of entries + // this is much faster than binary search for large number of entries + while (iSup - iInf > 1) { + final int iInterp = (int) FastMath.rint(dtSup.multiply(iInf).subtract(dtInf.multiply(iSup)).divide(dtSup.subtract(dtInf)).getReal()); + final int iMed = FastMath.max(iInf + 1, FastMath.min(iInterp, iSup - 1)); + final KK dtMed = t.durationFrom(data.get(iMed).getDate()); + if (dtMed.getReal() < 0) { + iSup = iMed; + dtSup = dtMed; + } else { + iInf = iMed; + dtInf = dtMed; + } + } + + return iInf; + } + + /** An empty immutable cache that always throws an exception on attempted access. */ + private static class EmptyFieldTimeStampedCache, KK extends CalculusFieldElement> + extends ImmutableFieldTimeStampedCache { + + /** Simple constructor. + * @param field field to which elements belong + */ + EmptyFieldTimeStampedCache(final Field field) { + super(field); + } + + /** {@inheritDoc} */ + @Override + public Stream getNeighbors(final FieldAbsoluteDate central) { + throw new TimeStampedCacheException(OrekitMessages.NO_CACHED_ENTRIES); + } + + /** {@inheritDoc} */ + @Override + public int getNeighborsSize() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public T getEarliest() { + throw new OrekitIllegalStateException(OrekitMessages.NO_CACHED_ENTRIES); + } + + /** {@inheritDoc} */ + @Override + public T getLatest() { + throw new OrekitIllegalStateException(OrekitMessages.NO_CACHED_ENTRIES); + } + + /** {@inheritDoc} */ + @Override + public List getAll() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "Empty immutable cache"; + } + + } + +} diff --git a/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java b/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java index f7b2b36952..28ecb516ea 100644 --- a/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java +++ b/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java @@ -24,6 +24,7 @@ import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; @@ -44,11 +45,6 @@ public class ImmutableTimeStampedCache implements TimeStampedCache { - /** - * A single chronological comparator since instances are thread safe. - */ - private static final ChronologicalComparator CMP = new ChronologicalComparator(); - /** * An empty immutable cache that always throws an exception on attempted * access. @@ -64,52 +60,73 @@ public class ImmutableTimeStampedCache private final List data; /** - * the size list to return from {@link #getNeighbors(AbsoluteDate)}. + * the maximum size list to return from {@link #getNeighbors(AbsoluteDate, int)}. + * @since 12.0 + */ + private final int maxNeighborsSize; + + /** Earliest date. + * @since 12.0 */ - private final int neighborsSize; + private final AbsoluteDate earliestDate; + + /** Latest date. + * @since 12.0 + */ + private final AbsoluteDate latestDate; /** * Create a new cache with the given neighbors size and data. * - * @param neighborsSize the size of the list returned from - * {@link #getNeighbors(AbsoluteDate)}. Must be less than or equal to + * @param maxNeighborsSize the maximum size of the list returned from + * {@link #getNeighbors(AbsoluteDate, int)}. Must be less than or equal to * {@code data.size()}. * @param data the backing data for this cache. The list will be copied to * ensure immutability. To guarantee immutability the entries in * {@code data} must be immutable themselves. There must be more data - * than {@code neighborsSize}. + * than {@code maxNeighborsSize}. * @throws IllegalArgumentException if {@code neightborsSize > data.size()} * or if {@code neighborsSize} is negative */ - public ImmutableTimeStampedCache(final int neighborsSize, + public ImmutableTimeStampedCache(final int maxNeighborsSize, final Collection data) { // parameter check - if (neighborsSize > data.size()) { + if (maxNeighborsSize > data.size()) { throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS, - data.size(), neighborsSize); + data.size(), maxNeighborsSize); } - if (neighborsSize < 1) { + if (maxNeighborsSize < 1) { throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, - neighborsSize, 0); + maxNeighborsSize, 0); } // assign instance variables - this.neighborsSize = neighborsSize; + this.maxNeighborsSize = maxNeighborsSize; // sort and copy data first - this.data = new ArrayList(data); - Collections.sort(this.data, CMP); + this.data = new ArrayList<>(data); + Collections.sort(this.data, new ChronologicalComparator()); + + this.earliestDate = this.data.get(0).getDate(); + this.latestDate = this.data.get(this.data.size() - 1).getDate(); + } /** * private constructor for {@link #EMPTY_CACHE}. */ private ImmutableTimeStampedCache() { - this.data = null; - this.neighborsSize = 0; + this.data = null; + this.maxNeighborsSize = 0; + this.earliestDate = AbsoluteDate.ARBITRARY_EPOCH; + this.latestDate = AbsoluteDate.ARBITRARY_EPOCH; } /** {@inheritDoc} */ - public Stream getNeighbors(final AbsoluteDate central) { + public Stream getNeighbors(final AbsoluteDate central, final int n) { + + if (n > maxNeighborsSize) { + throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, maxNeighborsSize); + } // find central index final int i = findIndex(central); @@ -126,10 +143,9 @@ public Stream getNeighbors(final AbsoluteDate central) { } // force unbalanced range if necessary - int start = FastMath.max(0, i - (this.neighborsSize - 1) / 2); - final int end = FastMath.min(this.data.size(), start + - this.neighborsSize); - start = end - this.neighborsSize; + int start = FastMath.max(0, i - (n - 1) / 2); + final int end = FastMath.min(this.data.size(), start + n); + start = end - n; // return list without copying return this.data.subList(start, end).stream(); @@ -145,21 +161,46 @@ public Stream getNeighbors(final AbsoluteDate central) { * {@code t} is after the last entry. */ private int findIndex(final AbsoluteDate t) { - // Guaranteed log(n) time - int i = Collections.binarySearch(this.data, t, CMP); - if (i == -this.data.size() - 1) { - // beyond last entry - i = this.data.size(); - } else if (i < 0) { - // did not find exact match, but contained in data interval - i = -i - 2; + + // left bracket of search algorithm + int iInf = 0; + double dtInf = t.durationFrom(earliestDate); + if (dtInf < 0) { + // before first entry + return -1; + } + + // right bracket of search algorithm + int iSup = data.size() - 1; + double dtSup = t.durationFrom(latestDate); + if (dtSup > 0) { + // after last entry + return data.size(); + } + + // search entries, using linear interpolation + // this should take only 2 iterations for near linear entries (most frequent use case) + // regardless of the number of entries + // this is much faster than binary search for large number of entries + while (iSup - iInf > 1) { + final int iInterp = (int) FastMath.rint((iInf * dtSup - iSup * dtInf) / (dtSup - dtInf)); + final int iMed = FastMath.max(iInf + 1, FastMath.min(iInterp, iSup - 1)); + final double dtMed = t.durationFrom(data.get(iMed).getDate()); + if (dtMed < 0) { + iSup = iMed; + dtSup = dtMed; + } else { + iInf = iMed; + dtInf = dtMed; + } } - return i; + + return iInf; } /** {@inheritDoc} */ - public int getNeighborsSize() { - return this.neighborsSize; + public int getMaxNeighborsSize() { + return maxNeighborsSize; } /** {@inheritDoc} */ @@ -202,7 +243,7 @@ public Stream getNeighbors(final AbsoluteDate central) { /** {@inheritDoc} */ @Override - public int getNeighborsSize() { + public int getMaxNeighborsSize() { return 0; } diff --git a/src/main/java/org/orekit/utils/InterpolationTableLoader.java b/src/main/java/org/orekit/utils/InterpolationTableLoader.java index 4cd7e44c3d..f79a5bf3ca 100644 --- a/src/main/java/org/orekit/utils/InterpolationTableLoader.java +++ b/src/main/java/org/orekit/utils/InterpolationTableLoader.java @@ -42,6 +42,17 @@ public class InterpolationTableLoader implements DataLoader { /** Values samples for the bi-variate interpolation function read from the file. */ private double[][] fArr; + /** Empty constructor. + *

        + * This constructor is not strictly necessary, but it prevents spurious + * javadoc warnings with JDK 18 and later. + *

        + * @since 12.0 + */ + public InterpolationTableLoader() { + // nothing to do + } + /** Returns a copy of the abscissa grid for the interpolation function. * @return the abscissa grid for the interpolation function, * or null if the file could not be read diff --git a/src/main/java/org/orekit/utils/LagrangianPoints.java b/src/main/java/org/orekit/utils/LagrangianPoints.java index a6c8731bbb..03f97572f7 100644 --- a/src/main/java/org/orekit/utils/LagrangianPoints.java +++ b/src/main/java/org/orekit/utils/LagrangianPoints.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/LegendrePolynomials.java b/src/main/java/org/orekit/utils/LegendrePolynomials.java index c1f22cd457..96cde49ce0 100644 --- a/src/main/java/org/orekit/utils/LegendrePolynomials.java +++ b/src/main/java/org/orekit/utils/LegendrePolynomials.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/LoveNumbers.java b/src/main/java/org/orekit/utils/LoveNumbers.java index 00f1046677..477ae6e745 100644 --- a/src/main/java/org/orekit/utils/LoveNumbers.java +++ b/src/main/java/org/orekit/utils/LoveNumbers.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -19,7 +19,7 @@ import java.io.Serializable; /** Container for Love numbers. - * @author luc Luc Maisonobe + * @author Luc Maisonobe * @since 6.1 */ public class LoveNumbers implements Serializable { diff --git a/src/main/java/org/orekit/utils/MultipleShooter.java b/src/main/java/org/orekit/utils/MultipleShooter.java index 4c4bbd7dad..69aaf8dcdc 100644 --- a/src/main/java/org/orekit/utils/MultipleShooter.java +++ b/src/main/java/org/orekit/utils/MultipleShooter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,7 +18,6 @@ import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.numerical.EpochDerivativesEquations; @@ -29,6 +28,7 @@ * Not suited for closed orbits. * @see "TRAJECTORY DESIGN AND ORBIT MAINTENANCE STRATEGIES IN MULTI-BODY DYNAMICAL REGIMES by Thomas A. Pavlak, Purdue University" * @author William Desprats + * @author Alberto Fossà * @since 10.2 */ public class MultipleShooter extends AbstractMultipleShooting { @@ -42,35 +42,18 @@ public class MultipleShooter extends AbstractMultipleShooting { private final List epochEquations; /** Simple Constructor. - *

        Standard constructor for multiple shooting which can be used with the CR3BP model.

        - * @param initialGuessList initial patch points to be corrected. - * @param propagatorList list of propagators associated to each patch point. - * @param additionalEquations list of additional equations linked to propagatorList. - * @param arcDuration initial guess of the duration of each arc. - * @param tolerance convergence tolerance on the constraint vector - * @deprecated as of 11.1, replaced by {@link #MultipleShooter(List, List, List, double, double, int)} - */ - @Deprecated - public MultipleShooter(final List initialGuessList, final List propagatorList, - final List additionalEquations, - final double arcDuration, final double tolerance) { - super(initialGuessList, propagatorList, additionalEquations, arcDuration, tolerance, DERIVATIVES); - epochEquations = additionalEquations.stream().map(ae -> (EpochDerivativesEquations) ae).collect(Collectors.toList()); - } - - /** Simple Constructor. - *

        Standard constructor for multiple shooting which can be used with the CR3BP model.

        - * @param initialGuessList initial patch points to be corrected. - * @param propagatorList list of propagators associated to each patch point. - * @param epochEquations list of additional derivatives providers linked to propagatorList. - * @param arcDuration initial guess of the duration of each arc. + *

        Standard constructor for multiple shooting which can be used with non-autonomous systems.

        + * @param initialGuessList initial patch points to be corrected + * @param propagatorList list of propagators associated to each patch point + * @param epochEquations list of additional derivatives providers linked to propagatorList * @param tolerance convergence tolerance on the constraint vector * @param maxIter maximum number of iterations */ - public MultipleShooter(final List initialGuessList, final List propagatorList, - final List epochEquations, final double arcDuration, + public MultipleShooter(final List initialGuessList, + final List propagatorList, + final List epochEquations, final double tolerance, final int maxIter) { - super(initialGuessList, propagatorList, arcDuration, tolerance, maxIter, DERIVATIVES); + super(initialGuessList, propagatorList, tolerance, maxIter, false, DERIVATIVES); this.epochEquations = epochEquations; } @@ -81,16 +64,13 @@ protected SpacecraftState getAugmentedInitialState(final int i) { /** {@inheritDoc} */ protected double[][] computeAdditionalJacobianMatrix(final List propagatedSP) { - final Map mapConstraints = getConstraintsMap(); - final int n = mapConstraints.size(); - final int ncolumns = getNumberOfFreeVariables() - 1; - - final double[][] M = new double[n][ncolumns]; + final Map mapConstraints = getConstraintsMap(); + final double[][] M = new double[mapConstraints.size()][getNumberOfFreeComponents()]; int k = 0; for (int index : mapConstraints.keySet()) { - M[k][index] = 1; + M[k][index] = 1.0; k++; } return M; @@ -105,12 +85,11 @@ protected double[] computeAdditionalConstraints(final List prop // [vz2i - vz2d]---- desired value) // Number of additional constraints - final int n = getConstraintsMap().size(); - final double[] fxAdditionnal = new double[n]; + final double[] fxAdditional = new double[getConstraintsMap().size()]; // Update additional constraints - updateAdditionalConstraints(0, fxAdditionnal); - return fxAdditionnal; + updateAdditionalConstraints(0, fxAdditional); + return fxAdditional; } } diff --git a/src/main/java/org/orekit/utils/MultipleShooting.java b/src/main/java/org/orekit/utils/MultipleShooting.java index d2e3d4a067..e5bfb3440a 100644 --- a/src/main/java/org/orekit/utils/MultipleShooting.java +++ b/src/main/java/org/orekit/utils/MultipleShooting.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/OccultationEngine.java b/src/main/java/org/orekit/utils/OccultationEngine.java new file mode 100644 index 0000000000..5068c1155c --- /dev/null +++ b/src/main/java/org/orekit/utils/OccultationEngine.java @@ -0,0 +1,224 @@ +/* Copyright 2002-2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; + +/** Computation engine for occultation events. + * @author Luc Maisonobe + * @since 12.0 + */ +public class OccultationEngine { + + /** Occulting body. */ + private final OneAxisEllipsoid occulting; + + /** Occulted body. */ + private final ExtendedPVCoordinatesProvider occulted; + + /** Occulted body radius (m). */ + private final double occultedRadius; + + /** Build a new occultation engine. + * @param occulted the body to be occulted + * @param occultedRadius the radius of the body to be occulted (m) + * @param occulting the occulting body + */ + public OccultationEngine(final ExtendedPVCoordinatesProvider occulted, final double occultedRadius, + final OneAxisEllipsoid occulting) { + this.occulted = occulted; + this.occultedRadius = FastMath.abs(occultedRadius); + this.occulting = occulting; + } + + /** Getter for the occulting body. + * @return the occulting body + */ + public OneAxisEllipsoid getOcculting() { + return occulting; + } + + /** Getter for the occulted body. + * @return the occulted body + */ + public ExtendedPVCoordinatesProvider getOcculted() { + return occulted; + } + + /** Getter for the occultedRadius. + * @return the occultedRadius + */ + public double getOccultedRadius() { + return occultedRadius; + } + + /** Compute the occultation angles as seen from a spacecraft. + * @param state the current state information: date, kinematics, attitude + * @return occultation angles + */ + public OccultationAngles angles(final SpacecraftState state) { + + final Vector3D psat = state.getPosition(occulting.getBodyFrame()); + final Vector3D pted = occulted.getPosition(state.getDate(), occulting.getBodyFrame()); + final Vector3D plimb = occulting.pointOnLimb(psat, pted); + final Vector3D ps = psat.subtract(pted); + final Vector3D pi = psat.subtract(plimb); + final double angle = Vector3D.angle(ps, psat); + final double rs = FastMath.asin(occultedRadius / ps.getNorm()); + final double ro = Vector3D.angle(pi, psat); + if (Double.isNaN(rs)) { + // we are inside the occulted body… + // set up dummy values consistent with full lighting (assuming occulted is the Sun) + return new OccultationAngles(FastMath.PI, 0.0, 0.0); + } else { + // regular case, we can compute limit angles as seen from spacecraft + return new OccultationAngles(angle, ro, rs); + } + + } + + /** Compute the occultation angles as seen from a spacecraft. + * @param state the current state information: date, kinematics, attitude + * @param the type of the field elements + * @return occultation angles + */ + public > FieldOccultationAngles angles(final FieldSpacecraftState state) { + + final FieldVector3D psat = state.getPosition(occulting.getBodyFrame()); + final FieldVector3D pted = occulted.getPosition(state.getDate(), occulting.getBodyFrame()); + final FieldVector3D plimb = occulting.pointOnLimb(psat, pted); + final FieldVector3D ps = psat.subtract(pted); + final FieldVector3D pi = psat.subtract(plimb); + final T angle = FieldVector3D.angle(ps, psat); + final T rs = FastMath.asin(ps.getNorm().reciprocal().multiply(occultedRadius)); + final T ro = FieldVector3D.angle(pi, psat); + if (rs.isNaN()) { + // we are inside the occulted body… + // set up dummy values consistent with full lighting (assuming occulted is the Sun) + final T zero = rs.getField().getZero(); + return new FieldOccultationAngles<>(zero.newInstance(FastMath.PI), zero, zero); + } else { + // regular case, we can compute limit angles as seen from spacecraft + return new FieldOccultationAngles<>(angle, ro, rs); + } + + } + + /** Container for occultation angles. + * @since 12.0 + */ + public static class OccultationAngles { + + /** Apparent separation between occulting and occulted directions. */ + private final double separation; + + /** Limb radius in occulting/occulted plane. */ + private final double limbRadius; + + /** Apparent radius of occulted body. */ + private final double occultedApparentRadius; + + /** Simple constructor. + * @param separation apparent separation between occulting and occulted directions (rad) + * @param limbRadius limb radius in occulting/occulted plane (rad) + * @param occultedApparentRadius apparent radius of occulted body (rad) + */ + OccultationAngles(final double separation, final double limbRadius, final double occultedApparentRadius) { + this.separation = separation; + this.limbRadius = limbRadius; + this.occultedApparentRadius = occultedApparentRadius; + } + + /** Get apparent separation between occulting and occulted directions. + * @return apparent separation between occulting and occulted directions (rad) + */ + public double getSeparation() { + return separation; + } + + /** Get limb radius in occulting/occulted plane. + * @return limb radius in occulting/occulted plane (rad) + */ + public double getLimbRadius() { + return limbRadius; + } + + /** Get apparent radius of occulted body. + * @return apparent radius of occulted body (rad) + */ + public double getOccultedApparentRadius() { + return occultedApparentRadius; + } + + } + + /** Container for occultation angles. + * @param the type of the field elements + * @since 12.0 + */ + public static class FieldOccultationAngles> { + + /** Apparent separation between occulting and occulted directions. */ + private final T separation; + + /** Limb radius in occulting/occulted plane. */ + private final T limbRadius; + + /** Apparent radius of occulted body. */ + private final T occultedApparentRadius; + + /** Simple constructor. + * @param separation apparent separation between occulting and occulted directions (rad) + * @param limbRadius limb radius in occulting/occulted plane (rad) + * @param occultedApparentRadius apparent radius of occulted body (rad) + */ + FieldOccultationAngles(final T separation, final T limbRadius, final T occultedApparentRadius) { + this.separation = separation; + this.limbRadius = limbRadius; + this.occultedApparentRadius = occultedApparentRadius; + } + + /** Get apparent separation between occulting and occulted directions. + * @return apparent separation between occulting and occulted directions (rad) + */ + public T getSeparation() { + return separation; + } + + /** Get limb radius in occulting/occulted plane. + * @return limb radius in occulting/occulted plane (rad) + */ + public T getLimbRadius() { + return limbRadius; + } + + /** Get apparent radius of occulted body. + * @return apparent radius of occulted body (rad) + */ + public T getOccultedApparentRadius() { + return occultedApparentRadius; + } + + } + +} diff --git a/src/main/java/org/orekit/utils/OrekitConfiguration.java b/src/main/java/org/orekit/utils/OrekitConfiguration.java index 2eb3aff417..d47b433a8f 100644 --- a/src/main/java/org/orekit/utils/OrekitConfiguration.java +++ b/src/main/java/org/orekit/utils/OrekitConfiguration.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/PVCoordinates.java b/src/main/java/org/orekit/utils/PVCoordinates.java index 61d3f87a3c..f4dec8489f 100644 --- a/src/main/java/org/orekit/utils/PVCoordinates.java +++ b/src/main/java/org/orekit/utils/PVCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,8 +23,10 @@ import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.UnivariateDerivative1; import org.hipparchus.analysis.differentiation.UnivariateDerivative2; +import org.hipparchus.exception.MathIllegalArgumentException; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Blendable; import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -44,7 +46,7 @@ * @author Fabien Maussion * @author Luc Maisonobe */ -public class PVCoordinates implements TimeShiftable, Serializable { +public class PVCoordinates implements TimeShiftable, Blendable, Serializable { /** Fixed position/velocity at origin (both p, v and a are zero vectors). */ public static final PVCoordinates ZERO = new PVCoordinates(Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO); @@ -631,6 +633,17 @@ private Object writeReplace() { return new DTO(this); } + /** {@inheritDoc} */ + @Override + public PVCoordinates blendArithmeticallyWith(final PVCoordinates other, final double blendingValue) + throws MathIllegalArgumentException { + final Vector3D blendedPosition = position.blendArithmeticallyWith(other.position, blendingValue); + final Vector3D blendedVelocity = velocity.blendArithmeticallyWith(other.velocity, blendingValue); + final Vector3D blendedAcceleration = acceleration.blendArithmeticallyWith(other.acceleration, blendingValue); + + return new PVCoordinates(blendedPosition, blendedVelocity, blendedAcceleration); + } + /** Internal class used only for serialization. */ private static class DTO implements Serializable { diff --git a/src/main/java/org/orekit/utils/PVCoordinatesProvider.java b/src/main/java/org/orekit/utils/PVCoordinatesProvider.java index 1ef19f0ee9..eb4961158f 100644 --- a/src/main/java/org/orekit/utils/PVCoordinatesProvider.java +++ b/src/main/java/org/orekit/utils/PVCoordinatesProvider.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,6 +17,7 @@ package org.orekit.utils; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; @@ -30,12 +31,21 @@ */ public interface PVCoordinatesProvider { + /** Get the position of the body in the selected frame. + * @param date current date + * @param frame the frame where to define the position + * @return position of the body (m and) + * @since 12.0 + */ + default Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + return getPVCoordinates(date, frame).getPosition(); + } + /** Get the {@link PVCoordinates} of the body in the selected frame. * @param date current date * @param frame the frame where to define the position * @return time-stamped position/velocity of the body (m and m/s) */ - TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, - Frame frame); + TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame); } diff --git a/src/main/java/org/orekit/utils/ParameterDriver.java b/src/main/java/org/orekit/utils/ParameterDriver.java index a5a68c6f2e..d6613348ea 100644 --- a/src/main/java/org/orekit/utils/ParameterDriver.java +++ b/src/main/java/org/orekit/utils/ParameterDriver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -26,8 +26,12 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; +import org.orekit.propagation.events.ParameterDrivenDateIntervalDetector; import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeSpanMap.Transition; /** Class allowing to drive the value of a parameter. @@ -39,11 +43,49 @@ * internal parameter in a physical model that needs to be slightly * offset. The physical model will expose to the algorithm a * set of instances of this class so the algorithm can call the - * {@link #setValue(double)} method to update the - * parameter value. Each time the value is set, the physical model + * {@link #setValue(double, AbsoluteDate)} method to update the + * parameter value at a given date. Some parameters driver only have 1 value estimated/driven + * over the all period (constructor by default). Some others have several + * values estimated/driven on several periods/intervals. For example if the time period is 3 days + * for a drag parameter estimated all days then 3 values would be estimated, one for + * each time period. In order to allow several values to be estimated, the PDriver has + * a name and a value {@link TimeSpanMap} as attribute. In order, + * to cut the time span map there are 2 options : + *

        + *
          + *
        • Passive cut calling the {@link #addSpans(AbsoluteDate, AbsoluteDate, double)} method. + * Given a start date, an end date and and a validity period (in sec) + * for the driver, the {@link #addSpans} method will cut the interval of name and value time span map + * from start date to date end in several interval of validity period duration. This method should not + * be called on orbital drivers and must be called only once at beginning of the process (for example + * beginning of orbit determination). WARNING : In order to ensure converge for orbit determination, + * the start, end date and driver periodicity must be wisely choosen . There must be enough measurements + * on each interval or convergence won't reach or singular matrixes will appear.
        • + *
        • Active cut calling the {@link #addSpanAtDate(AbsoluteDate)} method. + * Given a date, the method will cut the value and name time span name, in order to have a new span starting at + * the given date. Can be called several time to cut the time map as wished. WARNING : In order to ensure + * converge for orbit determination, if the method is called several time, the start date must be wisely choosen . + * There must be enough measurements on each interval or convergence won't reach or singular matrixes will appear.
        • + *
        + *

        + * Several ways exist in order to get a ParameterDriver value at a certain + * date for parameters having several values on several intervals. + *

        + *
          + *
        • First of all, the step estimation, that is to say, if a value wants + * to be known at a certain date, the value returned is the one of span + * beginning corresponding to the date. With this definition a value + * will be kept all along the span duration and will be the value of the span + * start.
        • + *
        • The continuous estimation, that is to say, when a value wants be to + * known at a date t, the value returned would be a linear interpolation between + * the value at the beginning of the span corresponding to date t and end this span + * (which is also the beginning of next span). NOT IMPLEMENTED FOR NOW + *
        • + *
        + * Each time the value is set, the physical model * will be notified as it will register a {@link ParameterObserver * ParameterObserver} for this purpose. - *

        *

        * This design has two major goals. First, it allows an external * algorithm to drive internal parameters almost anonymously, as it only @@ -55,13 +97,22 @@ *

        * @see ParameterObserver * @author Luc Maisonobe + * @author Melina Vanel * @since 8.0 */ public class ParameterDriver { + /** Name of the parameter.*/ + private String SPAN = "Span"; + /** Name of the parameter. */ private String name; + /** TimeSpan for period names. + * @since 12.0 + */ + private TimeSpanMap nameSpanMap; + /** Reference value. */ private double referenceValue; @@ -79,8 +130,18 @@ public class ParameterDriver { */ private AbsoluteDate referenceDate; - /** Current value. */ - private double value; + /** Flag to choose estimation method. If estimationContinuous + * is true then when a value wants to be known an interpolation + * is performed between given date span start and end (start of + * next span) otherwise the value returned is the value of span start + * @since 12.0 + */ + private boolean isEstimationContinuous; + + /** Value time span map. + * @since 12.0 + */ + private TimeSpanMap valueSpanMap; /** Selection status. *

        @@ -93,43 +154,162 @@ public class ParameterDriver { /** Observers observing this driver. */ private final List observers; + /** Create a new instance from another parameterDriver informations + * for example (useful for {@link ParameterDriversList.DelegatingDriver})) + * At construction, the parameter new is configured as not selected, + * the reference date is set to {@code null}. validityPeriod, namesSpanMap and + * valueSpanMap. + * @param name general name of the parameter + * @param namesSpanMap name time span map. WARNING, number of Span must be coherent with + * validityPeriod and valueSpanMap (same number of Span with same transitions + * dates) + * @param valuesSpanMap values time span map + * @param referenceValue reference value of the parameter + * @param scale scaling factor to convert the parameters value to + * non-dimensional (typically set to the expected standard deviation of the + * parameter), it must be non-zero + * @param minValue minimum value allowed + * @param maxValue maximum value allowed + * @since 12.0 + */ + public ParameterDriver(final String name, final TimeSpanMap namesSpanMap, + final TimeSpanMap valuesSpanMap, final double referenceValue, + final double scale, final double minValue, final double maxValue) { + if (FastMath.abs(scale) <= Precision.SAFE_MIN) { + throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER, + name, scale); + } + this.name = name; + this.nameSpanMap = namesSpanMap; + this.referenceValue = referenceValue; + this.scale = scale; + this.minValue = minValue; + this.maxValue = maxValue; + this.referenceDate = null; + this.valueSpanMap = valuesSpanMap; + this.selected = false; + this.observers = new ArrayList<>(); + this.isEstimationContinuous = false; + } + /** Simple constructor. *

        * At construction, the parameter is configured as not selected, - * the reference date is set to {@code null} and the value is set to the - * {@code referenceValue}. + * the reference date is set to {@code null}, the value is set to the + * {@code referenceValue}, the validity period is set to 0 so by default + * the parameterDriver will be estimated on only 1 interval from -INF to + * +INF. To change the validity period the + * {@link ParameterDriver#addSpans(AbsoluteDate, AbsoluteDate, double)} + * method must be called. *

        * @param name name of the parameter * @param referenceValue reference value of the parameter * @param scale scaling factor to convert the parameters value to * non-dimensional (typically set to the expected standard deviation of the * parameter), it must be non-zero - * @param minValue minimum value - * @param maxValue maximum value + * @param minValue minimum value allowed + * @param maxValue maximum value allowed */ - public ParameterDriver(final String name, final double referenceValue, - final double scale, final double minValue, - final double maxValue) { + public ParameterDriver(final String name, + final double referenceValue, final double scale, + final double minValue, final double maxValue) { if (FastMath.abs(scale) <= Precision.SAFE_MIN) { throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER, name, scale); } - this.name = name; - this.referenceValue = referenceValue; - this.scale = scale; - this.minValue = minValue; - this.maxValue = maxValue; - this.referenceDate = null; - this.value = referenceValue; - this.selected = false; - this.observers = new ArrayList<>(); + this.name = name; + this.nameSpanMap = new TimeSpanMap<>(SPAN + name + Integer.toString(0)); + this.referenceValue = referenceValue; + this.scale = scale; + this.minValue = minValue; + this.maxValue = maxValue; + this.referenceDate = null; + // at construction the parameter driver + // will be consider with only 1 estimated value over the all orbit + // determination + this.valueSpanMap = new TimeSpanMap<>(referenceValue); + this.selected = false; + this.observers = new ArrayList<>(); + this.isEstimationContinuous = false; + } + + /** Get current name span map of the parameterDriver, cut in interval + * in accordance with value span map and validity period. + * @return current name span map + * @since 12.0 + */ + public TimeSpanMap getNamesSpanMap() { + return nameSpanMap; + } + + /** Get value time span map for parameterDriver. + * @return value time span map + * @since 12.0 + */ + public TimeSpanMap getValueSpanMap() { + return valueSpanMap; + } + + /** Set current parameter value span map to match another driver. In order to keep + * consistency, the validity period and name span map are updated. + * @param driver for which the value span map wants to be copied for the + * current driver + * @since 12.0 + */ + public void setValueSpanMap(final ParameterDriver driver) { + final TimeSpanMap previousValueSpanMap = driver.getValueSpanMap(); + valueSpanMap = driver.getValueSpanMap(); + nameSpanMap = driver.getNamesSpanMap(); + for (final ParameterObserver observer : observers) { + observer.valueSpanMapChanged(previousValueSpanMap, this); + } + } + + /** Get the number of values to estimate that is to say the number. + * of Span present in valueSpanMap + * @return int the number of values to estimate + * @since 12.0 + */ + public int getNbOfValues() { + return valueSpanMap.getSpansNumber(); + } + + /** Get the dates of the transitions for the drag sensitive models {@link TimeSpanMap}. + * @return dates of the transitions for the drag sensitive models {@link TimeSpanMap} + * @since 12.0 + */ + public AbsoluteDate[] getTransitionDates() { + + // Get all transitions + final List listDates = new ArrayList<>(); + + // Extract all the transitions' dates + for (Transition transition = getValueSpanMap().getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) { + listDates.add(transition.getDate()); + } + // Return the array of transition dates + return listDates.toArray(new AbsoluteDate[0]); + } + + /** Get all values of the valueSpanMap in the chronological order. + * @return double[] containing values of the valueSpanMap in the chronological order + */ + public double[] getValues() { + final double[] chronologicalValues = new double[getNbOfValues()]; + Span currentSpan = valueSpanMap.getFirstSpan(); + for (int i = 0; i < getNbOfValues() - 1; i++) { + chronologicalValues[i] = currentSpan.getData(); + currentSpan = currentSpan.next(); + } + chronologicalValues[getNbOfValues() - 1 ] = currentSpan.getData(); + return chronologicalValues; } /** Add an observer for this driver. *

        - * The observer {@link ParameterObserver#valueChanged(double, ParameterDriver) - * valueChanged} method is called once automatically when the + * The observer {@link ParameterObserver#valueSpanMapChanged(TimeSpanMap, ParameterDriver) + * valueSpanMapChanged} method is called once automatically when the * observer is added, and then called at each value change. *

        * @param observer observer to add @@ -137,7 +317,7 @@ public ParameterDriver(final String name, final double referenceValue, */ public void addObserver(final ParameterObserver observer) { observers.add(observer); - observer.valueChanged(getValue(), this); + observer.valueSpanMapChanged(getValueSpanMap(), this); } /** Remove an observer. @@ -174,7 +354,22 @@ public List getObservers() { return Collections.unmodifiableList(observers); } - /** Change the name of this parameter driver. + /** Get parameter driver general name. + * @return name + */ + public String getName() { + return name; + } + + /** Get name of the parameter span for a specific date. + * @param date date at which the name of the span wants to be known + * @return name data of the name time span map at date + */ + public String getNameSpan(final AbsoluteDate date) { + return nameSpanMap.get(date); + } + + /** Change the general name of this parameter driver. * @param name new name */ public void setName(final String name) { @@ -183,13 +378,101 @@ public void setName(final String name) { for (final ParameterObserver observer : observers) { observer.nameChanged(previousName, this); } + // the names time span map must also be updated with the new name + if (nameSpanMap.getSpansNumber() > 1) { + Span currentNameSpan = nameSpanMap.getFirstSpan(); + nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false); + for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) { + currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd()); + nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false); + } + } else { + nameSpanMap = new TimeSpanMap<>(SPAN + name + Integer.toString(0)); + } } - /** Get name. - * @return name + /** Cut values and names time span map given orbit determination start and end and driver + * periodicity. + *

        + * For example for a drag coefficient the validity period would be + * 1 days = 86400sec. To be called after constructor to cut the temporal axis with + * the wanted parameter driver temporality for estimations on the wanted interval. + *

        + *

        + * Must be called only once at the beginning of orbit + * determination for example. If called several times, will throw exception. If parameter + * estimations intervals must be changed then a new ParameterDriver must be created or the + * function {@link #addSpanAtDate} should be used. + *

        + *

        + * This function should not be called on {@link DateDriver} and + * any of {@link ParameterDrivenDateIntervalDetector} attribute, because there is no sense to + * estimate several values for dateDriver. + *

        + *

        + * The choice of {@code orbitDeterminationStartDate}, {@code orbitDeterminationEndDate} and + * {@code validityPeriodForDriver} in a case of orbit determination must be done carefully, + * indeed, enough measurement should be available for each time interval or + * the orbit determination won't converge. + *

        + * @param orbitDeterminationStartDate start date for which the parameter driver + * starts to be estimated. + * @param orbitDeterminationEndDate end date for which the parameter driver + * stops to be estimated. + * @param validityPeriodForDriver validity period for which the parameter value + * is effective (for example 1 day for drag coefficient). Warning, validityPeriod + * should not be too short or the orbit determination won't converge. + * @since 12.0 */ - public String getName() { - return name; + public void addSpans(final AbsoluteDate orbitDeterminationStartDate, + final AbsoluteDate orbitDeterminationEndDate, + final double validityPeriodForDriver) { + + // by convention 0 is when the parameter needs to be drived only on 1 + // interval from -INF to +INF time period + if (getNbOfValues() != 1) { + // throw exception if called several time, must be called only once at the beginning of orbit + // determination, if the periods wants to be changed a new parameter must be created + throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET, name); + } else { + + int spanNumber = 1; + AbsoluteDate currentDate = orbitDeterminationStartDate.shiftedBy(validityPeriodForDriver); + //splitting the names and values span map accordingly with start and end of orbit determination + //and validity period. A security is added to avoid having to few measurements point for a span + //in order to assure orbit determination convergence + while (currentDate.isBefore(orbitDeterminationEndDate) && orbitDeterminationEndDate.durationFrom(currentDate) > validityPeriodForDriver / 3.0) { + valueSpanMap.addValidAfter(getValue(currentDate), currentDate, false); + nameSpanMap.addValidAfter(SPAN + getName() + Integer.toString(spanNumber++), currentDate, false); + currentDate = currentDate.shiftedBy(validityPeriodForDriver); + } + } + } + + /** Create a new span in values and names time span map given a start date. + * One must be aware of the importance of choosing wise dates if this function is called + * several times to create several span at wanted times. Indeed, if orbit determination is performed + * it might not converge or find singular matrix if the spans are too short and contains to few measurements. + * Must be called before any computation (for example before + * orbit determination). + * @param spanStartDate wanted start date for parameter value interval + * starts to be estimated. + * @since 12.0 + */ + public void addSpanAtDate(final AbsoluteDate spanStartDate) { + + // Split value span map with new interval having for start date spanStartDate and end + // date next span start date of +INF if no span is present after + valueSpanMap.addValidAfter(getValue(spanStartDate), spanStartDate, false); + nameSpanMap.addValidAfter(name, spanStartDate, false); + // Rename spans recursively + Span currentNameSpan = nameSpanMap.getFirstSpan(); + nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false); + + for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); spanNumber++) { + currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd()); + nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false); + } } /** Get reference parameter value. @@ -228,8 +511,10 @@ public void setMinValue(final double minValue) { for (final ParameterObserver observer : observers) { observer.minValueChanged(previousMinValue, this); } - // Check if current value is not out of min/max range - setValue(value); + // Check if all values are still not out of min/max range + for (Span span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) { + setValue(getValue(span.getStart()), span.getStart()); + } } /** Get maximum parameter value. @@ -249,8 +534,10 @@ public void setMaxValue(final double maxValue) { for (final ParameterObserver observer : observers) { observer.maxValueChanged(previousMaxValue, this); } - // Check if current value is not out of min/max range - setValue(value); + // Check if all values are still not out of min/max range + for (Span span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) { + setValue(getValue(span.getStart()), span.getStart()); + } } /** Get scale. @@ -272,7 +559,23 @@ public void setScale(final double scale) { } } - /** Get normalized value. + /** Get normalized value at specific date. + *

        + * The normalized value is a non-dimensional value + * suitable for use as part of a vector in an optimization + * process. It is computed as {@code (current - reference)/scale}. + *

        + * @param date date for which the normalized value wants to be known + * @return normalized value + */ + public double getNormalizedValue(final AbsoluteDate date) { + return (getValue(date) - getReferenceValue()) / scale; + } + + /** Get normalized value. Only useable on ParameterDriver + * which have only 1 span on their TimeSpanMap value (that is + * to say for which the setPeriod method wasn't called) otherwise + * it will throw an exception. *

        * The normalized value is a non-dimensional value * suitable for use as part of a vector in an optimization @@ -281,10 +584,26 @@ public void setScale(final double scale) { * @return normalized value */ public double getNormalizedValue() { - return (value - referenceValue) / scale; + return (getValue() - getReferenceValue()) / scale; + } + + /** Set normalized value at specific date. + *

        + * The normalized value is a non-dimensional value + * suitable for use as part of a vector in an optimization + * process. It is computed as {@code (current - reference)/scale}. + *

        + * @param date date for which the normalized value wants to be set + * @param normalized value + */ + public void setNormalizedValue(final double normalized, final AbsoluteDate date) { + setValue(getReferenceValue() + scale * normalized, date); } - /** Set normalized value. + /** Set normalized value at specific date. Only useable on ParameterDriver + * which have only 1 span on their TimeSpanMap value (that is + * to say for which the setPeriod method wasn't called) otherwise + * it will throw an exception. *

        * The normalized value is a non-dimensional value * suitable for use as part of a vector in an optimization @@ -293,7 +612,7 @@ public double getNormalizedValue() { * @param normalized value */ public void setNormalizedValue(final double normalized) { - setValue(referenceValue + scale * normalized); + setValue(getReferenceValue() + scale * normalized); } /** Get current reference date. @@ -316,38 +635,177 @@ public void setReferenceDate(final AbsoluteDate newReferenceDate) { } } - /** Get current parameter value. + /** Get current parameter value. Only usable on ParameterDriver + * which have only 1 span on their TimeSpanMap value (that is + * to say for which the setPeriod method wasn't called) * @return current parameter value */ public double getValue() { - return value; + if (getNbOfValues() > 1) { + throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "getValue(date)"); + } + // Attention voir si qlqchose est retourné si une exception est levée + return valueSpanMap.getFirstSpan().getData(); + } + + /** Get current parameter value at specific date, depending on isContinuousEstimation + * value, the value returned will be obtained by step estimation or continuous estimation. + * @param date date for which the value wants to be known. Only if + * parameter driver has 1 value estimated over the all orbit determination + * period (not validity period intervals for estimation), the date value can + * be {@code null} and then the only estimated value will be + * returned, in this case the date can also be whatever the value returned would + * be the same. Moreover in this particular case one can also call the {@link #getValue()}. + * @return current parameter value at date date, or for the all period if + * no validity period (= 1 value estimated over the all orbit determination + * period) + */ + public double getValue(final AbsoluteDate date) { + return isEstimationContinuous ? getValueContinuousEstimation(date) : getValueStepEstimation(date); } - /** Get the value as a gradient. + /** Get current parameter value at specific date with step estimation. + * @param date date for which the value wants to be known. Only if + * parameter driver has 1 value estimated over the all orbit determination + * period (not validity period intervals for estimation), the date value can + * be {@code null} and then the only estimated value will be + * returned, in this case the date can also be whatever the value returned would + * be the same. Moreover in this particular case one can also call the {@link #getValue()}. + * @return current parameter value at date date, or for the all period if + * no validity period (= 1 value estimated over the all orbit determination + * period) + */ + public double getValueStepEstimation(final AbsoluteDate date) { + return getNbOfValues() == 1 ? valueSpanMap.getFirstSpan().getData() : valueSpanMap.get(date); + } + + /** Get current parameter value at specific date with continuous estimation. + * @param date date for which the value wants to be known. Only if + * parameter driver has 1 value estimated over the all orbit determination + * period (not validity period intervals for estimation), the date value can + * be {@code null} and then the only estimated value will be + * returned, in this case the date can also be whatever the value returned would + * be the same. Moreover in this particular case one can also call the {@link #getValue()}. + * @return current parameter value at date date, or for the all period if + * no validity period (= 1 value estimated over the all orbit determination + * period) + * @since 12.0 + */ + public double getValueContinuousEstimation(final AbsoluteDate date) { + //TODO + throw new UnsupportedOperationException(); + } + + /** Get the value as a gradient at special date. * @param freeParameters total number of free parameters in the gradient * @param indices indices of the differentiation parameters in derivatives computations - * @return value with derivatives + * @return value with derivatives, will throw exception if called on a PDriver having + * several values driven * @since 10.2 */ public Gradient getValue(final int freeParameters, final Map indices) { - final Integer index = indices.get(name); - return (index == null) ? Gradient.constant(freeParameters, value) : Gradient.variable(freeParameters, index, value); + Integer index = null; + for (Span span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) { + index = indices.get(span.getData()); + if (index != null) { + break; + } + } + return (index == null) ? Gradient.constant(freeParameters, getValue()) : Gradient.variable(freeParameters, index, getValue()); } - /** Set parameter value. + /** Get the value as a gradient at special date. + * @param freeParameters total number of free parameters in the gradient + * @param indices indices of the differentiation parameters in derivatives computations, + * must be span name and not driver name + * @param date date for which the value wants to be known. Only if + * parameter driver has 1 value estimated over the all orbit determination + * period (not validity period intervals for estimation), the date value can + * be {@code null} and then the only estimated value will be + * returned + * @return value with derivatives + * @since 10.2 + */ + public Gradient getValue(final int freeParameters, final Map indices, final AbsoluteDate date) { + Integer index = null; + for (Span span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) { + index = indices.get(span.getData()); + if (index != null) { + break; + } + } + return (index == null) ? Gradient.constant(freeParameters, getValue(date)) : Gradient.variable(freeParameters, index, getValue(date)); + } + + /** Set parameter value at specific date. *

        * If {@code newValue} is below {@link #getMinValue()}, it will * be silently set to {@link #getMinValue()}. If {@code newValue} is * above {@link #getMaxValue()}, it will be silently set to {@link * #getMaxValue()}. *

        - * @param newValue new value + * @param date date for which the value wants to be set. Only if + * parameter driver has 1 value estimated over the all orbit determination + * period (not validity period intervals for estimation), the date value can + * be {@code null} + * @param newValue new value to set */ - public void setValue(final double newValue) { - final double previousValue = getValue(); - value = FastMath.max(minValue, FastMath.min(maxValue, newValue)); + public void setValue(final double newValue, final AbsoluteDate date) { + + double previousValue = Double.NaN; + AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH; + + // if valid for infinity (only 1 value estimation for the orbit determination ) + if (getNbOfValues() == 1) { + previousValue = this.getValue(referenceDateSpan); + this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue))); + // if needs to be estimated per time range / validity period + + // if several value intervals + } else { + final Span valueSpan = valueSpanMap.getSpan(date); + previousValue = valueSpan.getData(); + referenceDateSpan = valueSpan.getStart(); + // if the Span considered is from past infinity to valueSpanEndDate it is + // impossible to addValidAfter past infinity because it is creating a new span that + // is why the below trick was set up + if (referenceDateSpan.equals(AbsoluteDate.PAST_INFINITY)) { + referenceDateSpan = valueSpan.getEnd(); + this.valueSpanMap.addValidBefore(FastMath.max(minValue, FastMath.min(maxValue, newValue)), + referenceDateSpan, false); + } else { + this.valueSpanMap.addValidAfter(FastMath.max(minValue, FastMath.min(maxValue, newValue)), + referenceDateSpan, false); + } + } + for (final ParameterObserver observer : observers) { - observer.valueChanged(previousValue, this); + observer.valueChanged(previousValue, this, date); + } + } + + + /** Set parameter value. Only usable on ParameterDriver + * which have only 1 span on their TimeSpanMap value (that is + * to say for which the setPeriod method wasn't called) + *

        + * If {@code newValue} is below {@link #getMinValue()}, it will + * be silently set to {@link #getMinValue()}. If {@code newValue} is + * above {@link #getMaxValue()}, it will be silently set to {@link + * #getMaxValue()}. + *

        + * @param newValue new value to set + */ + public void setValue(final double newValue) { + if (getNbOfValues() == 1) { + final AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH; + final double previousValue = this.getValue(referenceDateSpan); + this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue))); + for (final ParameterObserver observer : observers) { + observer.valueChanged(previousValue, this, referenceDateSpan); + } + } else { + throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "setValue(date)"); } } @@ -378,11 +836,46 @@ public boolean isSelected() { return selected; } + /** Set parameter estimation to continuous, by default step estimation. + *

        Continuous estimation : when a value wants to be known at date + * t, the value returned will be an interpolation between start value + * of the span corresponding to date t and end value (which corresponds + * to the start of the next span). + *

        + *

        Step estimation : when a value wants to be + * known at date t, the value returned will be the value of the beginning + * of span corresponding to date t, step estimation. + *

        + * @param continuous if true the parameter will be estimated + * with continuous estimation, if false with step estimation. + */ + public void setContinuousEstimation(final boolean continuous) { + final boolean previousEstimation = isContinuousEstimation(); + this.isEstimationContinuous = continuous; + for (final ParameterObserver observer : observers) { + observer.estimationTypeChanged(previousEstimation, this); + } + } + + /** Check if parameter estimation is continuous, that is to say when + * a value wants to be known at date t, the value returned + * will be an interpolation between start value on span corresponding + * for date t and end value (which corresponds to the start of the next + * span), continuous estimation. Or not continuous, that is to say when a value wants to be + * known at date t, the value returned will be the value of the start + * of span corresponding to date t, step estimation. + * @return true if continuous estimation/definition, false if step estimation/definition + * @since 12.0 + */ + public boolean isContinuousEstimation() { + return isEstimationContinuous; + } + /** Get a text representation of the parameter. * @return text representation of the parameter, in the form name = value. */ public String toString() { - return name + " = " + value; + return name + " = " + valueSpanMap.get(AbsoluteDate.ARBITRARY_EPOCH); } } diff --git a/src/main/java/org/orekit/utils/ParameterDriversList.java b/src/main/java/org/orekit/utils/ParameterDriversList.java index e920610b8b..61b794a29a 100644 --- a/src/main/java/org/orekit/utils/ParameterDriversList.java +++ b/src/main/java/org/orekit/utils/ParameterDriversList.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -23,6 +23,7 @@ import java.util.List; import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeSpanMap.Span; /** Class managing several {@link ParameterDriver parameter drivers}, @@ -43,6 +44,7 @@ * with each other. *

        * @author Luc Maisonobe + * @author Mélina Vanel * @since 8.0 */ public class ParameterDriversList { @@ -64,6 +66,13 @@ public ParameterDriversList() { * being set to the value of the last driver added (i.e. * each addition overrides the parameter value). *

        + *

        + * Warning if a driver is added and a driver with the same name + * was already added before, they should have the same validity + * Period to avoid surprises. Whatever, all driver having + * same name will have their valueSpanMap, nameSpanMap and validity period + * overwritten with the last driver added attributes. + *

        * @param driver driver to add */ public void add(final ParameterDriver driver) { @@ -134,6 +143,23 @@ public DelegatingDriver findByName(final String name) { return null; } + /** Find a {@link DelegatingDriver delegating driver} by name. + * @param name name to check + * @return a {@link DelegatingDriver delegating driver} managing this parameter name + * @since 9.1 + */ + public String findDelegatingSpanNameBySpanName(final String name) { + for (final DelegatingDriver d : delegating) { + for (Span span = d.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + if (span.getData().equals(name)) { + return span.getData(); + } + } + } + return null; + } + + /** Sort the parameters lexicographically. */ public void sort() { @@ -167,6 +193,17 @@ public int getNbParams() { return delegating.size(); } + /** Get the number of values to estimate for parameters with different names. + * @return number of values to estimate for parameters with different names + */ + public int getNbValuesToEstimate() { + int nbValuesToEstimate = 0; + for (DelegatingDriver driver : delegating) { + nbValuesToEstimate += driver.getNbOfValues(); + } + return nbValuesToEstimate; + } + /** Get delegating drivers for all parameters. *

        * The delegating drivers are not the same as @@ -197,13 +234,14 @@ public static class DelegatingDriver extends ParameterDriver { * @param driver first driver in the series */ DelegatingDriver(final ParameterDriversList owner, final ParameterDriver driver) { - super(driver.getName(), driver.getReferenceValue(), + super(driver.getName(), driver.getNamesSpanMap(), + driver.getValueSpanMap(), driver.getReferenceValue(), driver.getScale(), driver.getMinValue(), driver.getMaxValue()); owners = new ArrayList<>(); addOwner(owner); - setValue(driver.getValue()); + setValueSpanMap(driver); setReferenceDate(driver.getReferenceDate()); setSelected(driver.isSelected()); @@ -233,12 +271,24 @@ private void removeOwner(final ParameterDriversList owner) { } } - /** Add a driver. + /** Add a driver. Warning, by doing this operation + * all the delegated drivers present in the parameterDriverList + * will be overwritten with the attributes of the driver given + * in argument. + *

        + *

        + * Warning if a driver is added and a driver with the same name + * was already added before, they should have the same validity + * Period (that is to say that the {@link ParameterDriver#setPeriods} + * method should have been called with same arguments for all drivers + * having the same name) to avoid surprises. Whatever, all driver having + * same name will have their valueSpanMap, nameSpanMap and validity period + * overwritten with the last driver added attributes. * @param driver driver to add */ private void add(final ParameterDriver driver) { - setValue(driver.getValue()); + setValueSpanMap(driver); setReferenceDate(driver.getReferenceDate()); // if any of the drivers is selected, all must be selected @@ -259,6 +309,15 @@ private void add(final ParameterDriver driver) { * only as a child of the current instance. Changes are therefore * still forwarded to it, but it is itself not responsible anymore * for forwarding change. + *

        + *

        + * Warning if a driver is added and a driver with the same name + * was already added before, they should have the same validity + * Period (that is to say that the {@link ParameterDriver#setPeriods} + * method should have been called with same arguments for all drivers + * having the same name) to avoid surprises. Whatever, all driver having + * same name will have their valueSpanMap, nameSpanMap and validity period + * overwritten with the last driver added attributes. *

        * @param other instance to merge */ @@ -271,7 +330,8 @@ private void merge(final DelegatingDriver other) { } // synchronize parameter - setValue(other.getValue()); + setValueSpanMap(other); + //setValue(other.getValue()); setReferenceDate(other.getReferenceDate()); if (isSelected()) { other.setSelected(true); @@ -364,8 +424,14 @@ public List getDrivers() { /** {@inheritDoc} */ @Override - public void valueChanged(final double previousValue, final ParameterDriver driver) { - updateAll(driver, d -> d.setValue(driver.getValue())); + public void valueSpanMapChanged(final TimeSpanMap previousValueSpanMap, final ParameterDriver driver) { + updateAll(driver, d -> d.setValueSpanMap(driver)); + } + + /** {@inheritDoc} */ + @Override + public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { + updateAll(driver, d -> d.setValue(driver.getValue(date), date)); } /** {@inheritDoc} */ @@ -386,6 +452,12 @@ public void selectionChanged(final boolean previousSelection, final ParameterDri updateAll(driver, d -> d.setSelected(driver.isSelected())); } + /** {@inheritDoc} */ + @Override + public void estimationTypeChanged(final boolean previousSelection, final ParameterDriver driver) { + updateAll(driver, d -> d.setContinuousEstimation(driver.isContinuousEstimation())); + } + /** {@inheritDoc} */ @Override public void referenceValueChanged(final double previousReferenceValue, final ParameterDriver driver) { diff --git a/src/main/java/org/orekit/utils/ParameterDriversProvider.java b/src/main/java/org/orekit/utils/ParameterDriversProvider.java new file mode 100644 index 0000000000..3f0168ed24 --- /dev/null +++ b/src/main/java/org/orekit/utils/ParameterDriversProvider.java @@ -0,0 +1,197 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.MathArrays; +import org.orekit.errors.UnsupportedParameterException; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.TimeSpanMap.Span; + +/** Provider for {@link ParameterDriver parameters drivers}. + * @author Luc Maisonobe + * @author Melina Vanel + * @author Maxime Journot + * @since 11.2 + */ +public interface ParameterDriversProvider { + + /** Get the drivers for parameters. + * @return drivers for parameters + */ + List getParametersDrivers(); + + /** Get total number of spans for all the parameters driver. + * @return total number of span to be estimated + * @since 12.0 + */ + default int getNbParametersDriversValue() { + int totalSpan = 0; + final List allParameters = getParametersDrivers(); + for (ParameterDriver driver : allParameters) { + totalSpan += driver.getNbOfValues(); + } + return totalSpan; + } + + /** Get model parameters. + * @return model parameters, will throw an + * exception if one PDriver has several values driven. If + * it's the case (if at least 1 PDriver of the model has several values + * driven) the method {@link #getParameters(AbsoluteDate)} must be used. + * @since 12.0 + */ + default double[] getParameters() { + + final List drivers = getParametersDrivers(); + final double[] parameters = new double[drivers.size()]; + for (int i = 0; i < drivers.size(); ++i) { + parameters[i] = drivers.get(i).getValue(); + } + return parameters; + } + + /** Get model parameters. + * @param date date at which the parameters want to be known, can + * be new AbsoluteDate() if all the parameters have no validity period + * that is to say that they have only 1 estimated value over the all + * interval + * @return model parameters + * @since 12.0 + */ + default double[] getParameters(AbsoluteDate date) { + + final List drivers = getParametersDrivers(); + final double[] parameters = new double[drivers.size()]; + for (int i = 0; i < drivers.size(); ++i) { + parameters[i] = drivers.get(i).getValue(date); + } + return parameters; + } + + /** Get model parameters, return a list a all span values + * of all parameters. + * @return model parameters + * @since 12.0 + */ + default double[] getParametersAllValues() { + + final List drivers = getParametersDrivers(); + final int nbParametersValues = getNbParametersDriversValue(); + final double[] parameters = new double[nbParametersValues]; + int paramIndex = 0; + for (int i = 0; i < drivers.size(); ++i) { + for (Span span = drivers.get(i).getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + parameters[paramIndex++] = span.getData(); + } + + } + return parameters; + } + + /** Get model parameters. + * @param field field to which the elements belong + * @param type of the elements + * @return model parameters + * @since 9.0 + */ + default > T[] getParametersAllValues(final Field field) { + final List drivers = getParametersDrivers(); + final int nbParametersValues = getNbParametersDriversValue(); + final T[] parameters = MathArrays.buildArray(field, nbParametersValues); + int paramIndex = 0; + for (int i = 0; i < drivers.size(); ++i) { + for (Span span = drivers.get(i).getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { + parameters[paramIndex++] = field.getZero().add(span.getData()); + } + } + return parameters; + } + + /** Get model parameters. + * @param field field to which the elements belong + * @param type of the elements + * @return model parameters, will throw an + * exception if one PDriver of the has several values driven. If + * it's the case (if at least 1 PDriver of the model has several values + * driven) the method {@link #getParameters(Field, FieldAbsoluteDate)} must be used. + * @since 9.0 + */ + default > T[] getParameters(final Field field) { + final List drivers = getParametersDrivers(); + final T[] parameters = MathArrays.buildArray(field, drivers.size()); + for (int i = 0; i < drivers.size(); ++i) { + parameters[i] = field.getZero().add(drivers.get(i).getValue()); + } + return parameters; + } + + /** Get model parameters. + * @param field field to which the elements belong + * @param type of the elements + * @param date field date at which the parameters want to be known, can + * be new AbsoluteDate() if all the parameters have no validity period. + * @return model parameters + * @since 9.0 + */ + default > T[] getParameters(final Field field, final FieldAbsoluteDate date) { + final List drivers = getParametersDrivers(); + final T[] parameters = MathArrays.buildArray(field, drivers.size()); + for (int i = 0; i < drivers.size(); ++i) { + parameters[i] = field.getZero().add(drivers.get(i).getValue(date.toAbsoluteDate())); + } + return parameters; + } + + /** Get parameter value from its name. + * @param name parameter name + * @return parameter value + * @since 8.0 + */ + default ParameterDriver getParameterDriver(final String name) { + + for (final ParameterDriver driver : getParametersDrivers()) { + if (name.equals(driver.getName())) { + // we have found a parameter with that name + return driver; + } + } + throw new UnsupportedParameterException(name, getParametersDrivers()); + } + + /** Check if a parameter is supported. + *

        Supported parameters are those listed by {@link #getParametersDrivers()}.

        + * @param name parameter name to check + * @return true if the parameter is supported + * @see #getParametersDrivers() + * @since 8.0 + */ + default boolean isSupported(String name) { + for (final ParameterDriver driver : getParametersDrivers()) { + if (name.equals(driver.getName())) { + // we have found a parameter with that name + return true; + } + } + // the parameter is not supported + return false; + } +} diff --git a/src/main/java/org/orekit/utils/ParameterFunction.java b/src/main/java/org/orekit/utils/ParameterFunction.java index 22559fd103..0076bcbf62 100644 --- a/src/main/java/org/orekit/utils/ParameterFunction.java +++ b/src/main/java/org/orekit/utils/ParameterFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,6 +16,8 @@ */ package org.orekit.utils; +import org.orekit.time.AbsoluteDate; + /** Interface representing a scalar function depending on a {@link ParameterDriver}. * @see Differentiation#differentiate(ParameterFunction, int, double) * @author Luc Maisonobe @@ -23,10 +25,12 @@ */ public interface ParameterFunction { + /** Evaluate the function. * @param parameterDriver driver for the parameter. + * @param date date at which the function wants to be known * @return scalar value of the function */ - double value(ParameterDriver parameterDriver); + double value(ParameterDriver parameterDriver, AbsoluteDate date); } diff --git a/src/main/java/org/orekit/utils/ParameterObserver.java b/src/main/java/org/orekit/utils/ParameterObserver.java index 0e0847fedf..d4f6747d02 100644 --- a/src/main/java/org/orekit/utils/ParameterObserver.java +++ b/src/main/java/org/orekit/utils/ParameterObserver.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -29,8 +29,15 @@ public interface ParameterObserver { /** Notify that a parameter value has been changed. * @param previousValue previous value * @param driver parameter driver that has been changed + * @param date date for which the parameter value have been updated */ - void valueChanged(double previousValue, ParameterDriver driver); + void valueChanged(double previousValue, ParameterDriver driver, AbsoluteDate date); + + /** Notify that a parameter value span map has been changed. + * @param previousValueSpanMap previous value + * @param driver parameter driver that has been changed + */ + void valueSpanMapChanged(TimeSpanMap previousValueSpanMap, ParameterDriver driver); /** Notify that a parameter reference date has been changed. *

        @@ -69,6 +76,19 @@ default void selectionChanged(final boolean previousSelection, final ParameterDr // nothing by default } + /** Notify that a parameter estimation type (continuous or step) has been changed. + *

        + * The default implementation does nothing + *

        + * @param previousIsContinuous previous estimation type, continuous estimation if true, + * step estimation if not. + * @param driver parameter driver that has been changed + * @since 9.0 + */ + default void estimationTypeChanged(final boolean previousIsContinuous, final ParameterDriver driver) { + // nothing by default + } + /** Notify that a parameter reference value has been changed. *

        * The default implementation does nothing diff --git a/src/main/java/org/orekit/utils/SecularAndHarmonic.java b/src/main/java/org/orekit/utils/SecularAndHarmonic.java index eba5f5fd49..aff467a387 100644 --- a/src/main/java/org/orekit/utils/SecularAndHarmonic.java +++ b/src/main/java/org/orekit/utils/SecularAndHarmonic.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -109,11 +109,25 @@ public void setMaxIter(final int maxIter) { } /** Add a fitting point. + *

        + * The point weight is set to 1.0 + *

        * @param date date of the point * @param osculatingValue osculating value + * @see #addWeightedPoint(AbsoluteDate, double, double) */ public void addPoint(final AbsoluteDate date, final double osculatingValue) { - observedPoints.add(new WeightedObservedPoint(1.0, date.durationFrom(reference), osculatingValue)); + addWeightedPoint(date, osculatingValue, 1.0); + } + + /** Add a weighted fitting point. + * @param date date of the point + * @param osculatingValue osculating value + * @param weight weight of the points + * @since 12.0 + */ + public void addWeightedPoint(final AbsoluteDate date, final double osculatingValue, final double weight) { + observedPoints.add(new WeightedObservedPoint(weight, date.durationFrom(reference), osculatingValue)); } /** Get the reference date. @@ -124,6 +138,22 @@ public AbsoluteDate getReferenceDate() { return reference; } + /** Get degree of polynomial secular part. + * @return degree of polynomial secular part + * @since 12.0 + */ + public int getSecularDegree() { + return secularDegree; + } + + /** Get the pulsations of harmonic part. + * @return pulsations of harmonic part + * @since 12.0 + */ + public double[] getPulsations() { + return pulsations.clone(); + } + /** Get an upper bound of the fitted harmonic amplitude. * @return upper bound of the fitted harmonic amplitude */ diff --git a/src/main/java/org/orekit/utils/StateFunction.java b/src/main/java/org/orekit/utils/StateFunction.java index ee0429f9e4..da5367bfca 100644 --- a/src/main/java/org/orekit/utils/StateFunction.java +++ b/src/main/java/org/orekit/utils/StateFunction.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ /** Interface representing a vector function depending on {@link SpacecraftState}. * @see Differentiation#differentiate(StateFunction, int, org.orekit.attitudes.AttitudeProvider, - * org.orekit.orbits.OrbitType, org.orekit.orbits.PositionAngle, double, int) + * org.orekit.orbits.OrbitType, org.orekit.orbits.PositionAngleType, double, int) * @see StateJacobian * @author Luc Maisonobe * @since 8.0 diff --git a/src/main/java/org/orekit/utils/StateJacobian.java b/src/main/java/org/orekit/utils/StateJacobian.java index f7b92f32d9..913a74d532 100644 --- a/src/main/java/org/orekit/utils/StateJacobian.java +++ b/src/main/java/org/orekit/utils/StateJacobian.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -20,7 +20,7 @@ /** Interface representing the Jacobian of a vector function depending on {@link SpacecraftState}. * @see Differentiation#differentiate(StateFunction, int, org.orekit.attitudes.AttitudeProvider, - * org.orekit.orbits.OrbitType, org.orekit.orbits.PositionAngle, double, int) + * org.orekit.orbits.OrbitType, org.orekit.orbits.PositionAngleType, double, int) * @see StateFunction * @author Luc Maisonobe * @since 8.0 @@ -29,7 +29,7 @@ public interface StateJacobian { /** Evaluate the Jacobian of the function. * @param state spacecraft state as the sole free parameter of the function. - * @return Jacobian matric + * @return Jacobian matrix */ double[][] value(SpacecraftState state); diff --git a/src/main/java/org/orekit/utils/TimeSpanMap.java b/src/main/java/org/orekit/utils/TimeSpanMap.java index 07f2c92189..8ae6c2259b 100644 --- a/src/main/java/org/orekit/utils/TimeSpanMap.java +++ b/src/main/java/org/orekit/utils/TimeSpanMap.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,12 +16,9 @@ */ package org.orekit.utils; -import java.util.NavigableSet; -import java.util.TreeSet; import java.util.function.Consumer; import org.orekit.time.AbsoluteDate; -import org.orekit.time.ChronologicalComparator; import org.orekit.time.TimeStamped; /** Container for objects that apply to spans of time. @@ -99,20 +96,6 @@ public synchronized int getSpansNumber() { return nbSpans; } - /** Add an entry valid before a limit date. - *

        - * Calling this method is equivalent to call {@link #addValidAfter(Object, - * AbsoluteDate, boolean) addValidAfter(entry, latestValidityDate, false)}. - *

        - * @param entry entry to add - * @param latestValidityDate date before which the entry is valid - * @deprecated as of 11.1, replaced by {@link #addValidBefore(Object, AbsoluteDate, boolean)} - */ - @Deprecated - public void addValidBefore(final T entry, final AbsoluteDate latestValidityDate) { - addValidBefore(entry, latestValidityDate, false); - } - /** Add an entry valid before a limit date. *

        * As an entry is valid, it truncates or overrides the validity of the neighboring @@ -190,43 +173,29 @@ public synchronized Span addValidBefore(final T entry, final AbsoluteDate lat } - /** Add an entry valid after a limit date. - *

        - * Calling this method is equivalent to call {@link #addValidAfter(Object, - * AbsoluteDate, boolean) addValidAfter(entry, earliestValidityDate, false)}. - *

        - * @param entry entry to add - * @param earliestValidityDate date after which the entry is valid - * @deprecated as of 11.1, replaced by {@link #addValidAfter(Object, AbsoluteDate, boolean)} - */ - @Deprecated - public void addValidAfter(final T entry, final AbsoluteDate earliestValidityDate) { - addValidAfter(entry, earliestValidityDate, false); - } - /** Add an entry valid after a limit date. *

        * As an entry is valid, it truncates or overrides the validity of the neighboring * entries already present in the map. *

        *

        - * If the map already contains transitions that occur earlier than {@code earliestValidityDate}, - * the {@code erasesEarlier} parameter controls what to do with them. Lets consider + * If the map already contains transitions that occur later than {@code earliestValidityDate}, + * the {@code erasesLater} parameter controls what to do with them. Lets consider * the time span [tₖ ; tₖ₊₁[ associated with entry eₖ that would have been valid at time * {@code earliestValidityDate} prior to the call to the method (i.e. tₖ < * {@code earliestValidityDate} < tₖ₊₁). *

        *
          - *
        • if {@code erasesEarlier} is {@code true}, then all earlier transitions - * up to and including tₖ are erased, and the {@code entry} will be valid from past infinity - * to {@code earliestValidityDate}
        • - *
        • if {@code erasesEarlier} is {@code false}, then all earlier transitions - * are preserved, and the {@code entry} will be valid from tₖ - * to {@code earliestValidityDate}
        • + *
        • if {@code erasesLater} is {@code true}, then all later transitions + * from and including tₖ₊₁ are erased, and the {@code entry} will be valid from + * {@code earliestValidityDate} to future infinity
        • + *
        • if {@code erasesLater} is {@code false}, then all later transitions + * are preserved, and the {@code entry} will be valid from {@code earliestValidityDate} + * to tₖ₊₁
        • *
        *

        * In both cases, the existing entry eₖ time span will be truncated and will be valid - * only from {@code earliestValidityDate} to tₖ₊₁. + * only from tₖ to {@code earliestValidityDate}. *

        * @param entry entry to add * @param earliestValidityDate date after which the entry is valid @@ -491,20 +460,6 @@ public synchronized TimeSpanMap extractRange(final AbsoluteDate start, final } - /** Get copy of the sorted transitions. - * @return copy of the sorted transitions - * @deprecated as of 11.1, replaced by {@link #getFirstSpan()}, {@link #getLastSpan()}, - * {@link #getFirstTransition()}, {@link #getLastTransition()}, and {@link #getSpansNumber()} - */ - @Deprecated - public synchronized NavigableSet> getTransitions() { - final NavigableSet> set = new TreeSet<>(new ChronologicalComparator()); - for (Transition transition = getFirstTransition(); transition != null; transition = transition.next()) { - set.add(transition); - } - return set; - } - /** * Performs an action for each non-null element of map. *

        diff --git a/src/main/java/org/orekit/utils/TimeStampedAngularCoordinates.java b/src/main/java/org/orekit/utils/TimeStampedAngularCoordinates.java index f66c231288..38b286ec89 100644 --- a/src/main/java/org/orekit/utils/TimeStampedAngularCoordinates.java +++ b/src/main/java/org/orekit/utils/TimeStampedAngularCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,19 +16,11 @@ */ package org.orekit.utils; -import java.util.Collection; - import org.hipparchus.analysis.differentiation.Derivative; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitInternalError; -import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; @@ -210,150 +202,4 @@ public TimeStampedAngularCoordinates subtractOffset(final AngularCoordinates off return addOffset(offset.revert()); } - /** Interpolate angular coordinates. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation. - *

        - *

        - * This method is based on Sergei Tanygin's paper Attitude - * Interpolation, changing the norm of the vector to match the modified Rodrigues - * vector as described in Malcolm D. Shuster's paper A - * Survey of Attitude Representations. This change avoids the singularity at π. - * There is still a singularity at 2π, which is handled by slightly offsetting all rotations - * when this singularity is detected. Another change is that the mean linear motion - * is first removed before interpolation and added back after interpolation. This allows - * to use interpolation even when the sample covers much more than one turn and even - * when sample points are separated by more than one turn. - *

        - *

        - * Note that even if first and second time derivatives (rotation rates and acceleration) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the rotations. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @return a new position-velocity, interpolated at specified date - */ - public static TimeStampedAngularCoordinates interpolate(final AbsoluteDate date, - final AngularDerivativesFilter filter, - final Collection sample) { - - // set up safety elements for 2π singularity avoidance - final double epsilon = 2 * FastMath.PI / sample.size(); - final double threshold = FastMath.min(-(1.0 - 1.0e-4), -FastMath.cos(epsilon / 4)); - - // set up a linear model canceling mean rotation rate - final Vector3D meanRate; - if (filter != AngularDerivativesFilter.USE_R) { - Vector3D sum = Vector3D.ZERO; - for (final TimeStampedAngularCoordinates datedAC : sample) { - sum = sum.add(datedAC.getRotationRate()); - } - meanRate = new Vector3D(1.0 / sample.size(), sum); - } else { - if (sample.size() < 2) { - throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION, - sample.size()); - } - Vector3D sum = Vector3D.ZERO; - TimeStampedAngularCoordinates previous = null; - for (final TimeStampedAngularCoordinates datedAC : sample) { - if (previous != null) { - sum = sum.add(estimateRate(previous.getRotation(), datedAC.getRotation(), - datedAC.date.durationFrom(previous.date))); - } - previous = datedAC; - } - meanRate = new Vector3D(1.0 / (sample.size() - 1), sum); - } - TimeStampedAngularCoordinates offset = - new TimeStampedAngularCoordinates(date, Rotation.IDENTITY, meanRate, Vector3D.ZERO); - - boolean restart = true; - for (int i = 0; restart && i < sample.size() + 2; ++i) { - - // offset adaptation parameters - restart = false; - - // set up an interpolator taking derivatives into account - final HermiteInterpolator interpolator = new HermiteInterpolator(); - - // add sample points - double sign = +1.0; - Rotation previous = Rotation.IDENTITY; - - for (final TimeStampedAngularCoordinates ac : sample) { - - // remove linear offset from the current coordinates - final double dt = ac.date.durationFrom(date); - final TimeStampedAngularCoordinates fixed = ac.subtractOffset(offset.shiftedBy(dt)); - - // make sure all interpolated points will be on the same branch - final double dot = MathArrays.linearCombination(fixed.getRotation().getQ0(), previous.getQ0(), - fixed.getRotation().getQ1(), previous.getQ1(), - fixed.getRotation().getQ2(), previous.getQ2(), - fixed.getRotation().getQ3(), previous.getQ3()); - sign = FastMath.copySign(1.0, dot * sign); - previous = fixed.getRotation(); - - // check modified Rodrigues vector singularity - if (fixed.getRotation().getQ0() * sign < threshold) { - // the sample point is close to a modified Rodrigues vector singularity - // we need to change the linear offset model to avoid this - restart = true; - break; - } - - final double[][] rodrigues = fixed.getModifiedRodrigues(sign); - switch (filter) { - case USE_RRA: - // populate sample with rotation, rotation rate and acceleration data - interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1], rodrigues[2]); - break; - case USE_RR: - // populate sample with rotation and rotation rate data - interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1]); - break; - case USE_R: - // populate sample with rotation data only - interpolator.addSamplePoint(dt, rodrigues[0]); - break; - default : - // this should never happen - throw new OrekitInternalError(null); - } - } - - if (restart) { - // interpolation failed, some intermediate rotation was too close to 2π - // we need to offset all rotations to avoid the singularity - offset = offset.addOffset(new AngularCoordinates(new Rotation(Vector3D.PLUS_I, - epsilon, - RotationConvention.VECTOR_OPERATOR), - Vector3D.ZERO, Vector3D.ZERO)); - } else { - // interpolation succeeded with the current offset - final double[][] p = interpolator.derivatives(0.0, 2); - final AngularCoordinates ac = createFromModifiedRodrigues(p); - return new TimeStampedAngularCoordinates(offset.getDate(), - ac.getRotation(), - ac.getRotationRate(), - ac.getRotationAcceleration()).addOffset(offset); - } - - } - - // this should never happen - throw new OrekitInternalError(null); - - } - } diff --git a/src/main/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolator.java new file mode 100644 index 0000000000..26db82573c --- /dev/null +++ b/src/main/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolator.java @@ -0,0 +1,260 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.orekit.errors.OrekitInternalError; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; + +import java.util.List; + +/** + * Class using Hermite interpolator to interpolate time stamped angular coordinates. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @author Vincent Cucchietti + * @author Luc Maisonobe + * @see HermiteInterpolator + * @see TimeStampedAngularCoordinates + */ +public class TimeStampedAngularCoordinatesHermiteInterpolator + extends AbstractTimeInterpolator { + + /** Filter for derivatives from the sample to use in interpolation. */ + private final AngularDerivativesFilter filter; + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + */ + public TimeStampedAngularCoordinatesHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * /** Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedAngularCoordinatesHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, AngularDerivativesFilter.USE_RR); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedAngularCoordinatesHermiteInterpolator(final int interpolationPoints, + final AngularDerivativesFilter filter) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, filter); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedAngularCoordinatesHermiteInterpolator(final int interpolationPoints, + final double extrapolationThreshold, + final AngularDerivativesFilter filter) { + super(interpolationPoints, extrapolationThreshold); + this.filter = filter; + } + + /** Get filter for derivatives from the sample to use in interpolation. + * @return filter for derivatives from the sample to use in interpolation + */ + public AngularDerivativesFilter getFilter() { + return filter; + } + + /** + * {@inheritDoc} + *

        + * The interpolated instance is created by polynomial Hermite interpolation on Rodrigues vector ensuring rotation rate + * remains the exact derivative of rotation. + *

        + * This method is based on Sergei Tanygin's paper Attitude Interpolation, changing the norm + * of the vector to match the modified Rodrigues vector as described in Malcolm D. Shuster's paper A + * Survey of Attitude Representations. This change avoids the singularity at π. There is still a singularity at 2π, + * which is handled by slightly offsetting all rotations when this singularity is detected. Another change is that the + * mean linear motion is first removed before interpolation and added back after interpolation. This allows to use + * interpolation even when the sample covers much more than one turn and even when sample points are separated by more + * than one turn. + *

        + *

        + * Note that even if first and second time derivatives (rotation rates and acceleration) from sample can be ignored, the + * interpolated instance always includes interpolated derivatives. This feature can be used explicitly to compute these + * derivatives when it would be too complex to compute them from an analytical formula: just compute a few sample points + * from the explicit formula and set the derivatives to zero in these sample points, then use interpolation to add + * derivatives consistent with the rotations. + */ + @Override + protected TimeStampedAngularCoordinates interpolate(final InterpolationData interpolationData) { + + // Get date + final AbsoluteDate date = interpolationData.getInterpolationDate(); + + // Get sample + final List sample = interpolationData.getNeighborList(); + + // set up safety elements for 2π singularity avoidance + final double epsilon = 2 * FastMath.PI / sample.size(); + final double threshold = FastMath.min(-(1.0 - 1.0e-4), -FastMath.cos(epsilon / 4)); + + // set up a linear model canceling mean rotation rate + final Vector3D meanRate; + Vector3D sum = Vector3D.ZERO; + if (filter != AngularDerivativesFilter.USE_R) { + for (final TimeStampedAngularCoordinates datedAC : sample) { + sum = sum.add(datedAC.getRotationRate()); + } + meanRate = new Vector3D(1.0 / sample.size(), sum); + } + else { + TimeStampedAngularCoordinates previous = null; + for (final TimeStampedAngularCoordinates datedAC : sample) { + if (previous != null) { + sum = sum.add(TimeStampedAngularCoordinates.estimateRate(previous.getRotation(), datedAC.getRotation(), + datedAC.getDate() + .durationFrom(previous.getDate()))); + } + previous = datedAC; + } + meanRate = new Vector3D(1.0 / (sample.size() - 1), sum); + } + TimeStampedAngularCoordinates offset = + new TimeStampedAngularCoordinates(date, Rotation.IDENTITY, meanRate, Vector3D.ZERO); + + boolean restart = true; + for (int i = 0; restart && i < sample.size() + 2; ++i) { + + // offset adaptation parameters + restart = false; + + // set up an interpolator taking derivatives into account + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // add sample points + double sign = 1.0; + Rotation previous = Rotation.IDENTITY; + + for (final TimeStampedAngularCoordinates ac : sample) { + + // remove linear offset from the current coordinates + final double dt = ac.getDate().durationFrom(date); + final TimeStampedAngularCoordinates fixed = ac.subtractOffset(offset.shiftedBy(dt)); + + // make sure all interpolated points will be on the same branch + final double dot = MathArrays.linearCombination(fixed.getRotation().getQ0(), previous.getQ0(), + fixed.getRotation().getQ1(), previous.getQ1(), + fixed.getRotation().getQ2(), previous.getQ2(), + fixed.getRotation().getQ3(), previous.getQ3()); + sign = FastMath.copySign(1.0, dot * sign); + previous = fixed.getRotation(); + + // check modified Rodrigues vector singularity + if (fixed.getRotation().getQ0() * sign < threshold) { + // the sample point is close to a modified Rodrigues vector singularity + // we need to change the linear offset model to avoid this + restart = true; + break; + } + + final double[][] rodrigues = fixed.getModifiedRodrigues(sign); + switch (filter) { + case USE_RRA: + // populate sample with rotation, rotation rate and acceleration data + interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1], rodrigues[2]); + break; + case USE_RR: + // populate sample with rotation and rotation rate data + interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1]); + break; + case USE_R: + // populate sample with rotation data only + interpolator.addSamplePoint(dt, rodrigues[0]); + break; + default: + // this should never happen + throw new OrekitInternalError(null); + } + } + + if (restart) { + // interpolation failed, some intermediate rotation was too close to 2π + // we need to offset all rotations to avoid the singularity + offset = offset.addOffset(new AngularCoordinates(new Rotation(Vector3D.PLUS_I, + epsilon, + RotationConvention.VECTOR_OPERATOR), + Vector3D.ZERO, Vector3D.ZERO)); + } + else { + // interpolation succeeded with the current offset + final double[][] p = interpolator.derivatives(0.0, 2); + final AngularCoordinates ac = AngularCoordinates.createFromModifiedRodrigues(p); + return new TimeStampedAngularCoordinates(offset.getDate(), + ac.getRotation(), + ac.getRotationRate(), + ac.getRotationAcceleration()).addOffset(offset); + } + + } + + // this should never happen + throw new OrekitInternalError(null); + } +} diff --git a/src/main/java/org/orekit/utils/TimeStampedCache.java b/src/main/java/org/orekit/utils/TimeStampedCache.java index 892d1491eb..4b64a683ac 100644 --- a/src/main/java/org/orekit/utils/TimeStampedCache.java +++ b/src/main/java/org/orekit/utils/TimeStampedCache.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -48,17 +48,40 @@ public interface TimeStampedCache { * * @param central central date * @return list of cached entries surrounding the specified date. The size - * of the list is guaranteed to be {@link #getNeighborsSize()}. + * of the list is guaranteed to be {@link #getMaxNeighborsSize()}. + * @see #getNeighbors(AbsoluteDate, int) */ - Stream getNeighbors(AbsoluteDate central); + default Stream getNeighbors(AbsoluteDate central) { + return getNeighbors(central, getMaxNeighborsSize()); + } /** - * Get the fixed size of the lists returned by - * {@link #getNeighbors(AbsoluteDate)}. + * Get the entries surrounding a central date. + *

        + * If the central date is well within covered range, the returned array will + * be balanced with half the points before central date and half the points + * after it (depending on n parity, of course). If the central date is near + * the boundary, then the returned array will be unbalanced and will contain + * only the n earliest (or latest) entries. A typical example of the later + * case is leap seconds cache, since the number of leap seconds cannot be + * arbitrarily increased. + *

        + * This method is safe for multiple threads to execute concurrently. + * + * @param central central date + * @param n number of neighbors (cannot exceed {@link #getMaxNeighborsSize()}) + * @return stream of cached entries surrounding the specified date. + * @since 12.0 + */ + Stream getNeighbors(AbsoluteDate central, int n); + + /** + * Get the maximum size of the lists returned by + * {@link #getNeighbors(AbsoluteDate, int)}. * * @return size of the list */ - int getNeighborsSize(); + int getMaxNeighborsSize(); /** * Get the earliest entry in this cache. diff --git a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java index eea59eb558..99c2f466e6 100644 --- a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java +++ b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,20 +16,14 @@ */ package org.orekit.utils; -import java.util.Collection; import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.FieldDerivative; import org.hipparchus.analysis.differentiation.FieldDerivativeStructure; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.RotationConvention; -import org.hipparchus.util.FastMath; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitInternalError; -import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeStamped; @@ -268,192 +262,4 @@ public TimeStampedFieldAngularCoordinates subtractOffset(final FieldAngularCo return addOffset(offset.revert()); } - /** Interpolate angular coordinates. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation. - *

        - *

        - * This method is based on Sergei Tanygin's paper Attitude - * Interpolation, changing the norm of the vector to match the modified Rodrigues - * vector as described in Malcolm D. Shuster's paper A - * Survey of Attitude Representations. This change avoids the singularity at π. - * There is still a singularity at 2π, which is handled by slightly offsetting all rotations - * when this singularity is detected. - *

        - *

        - * Note that even if first time derivatives (rotation rates) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the rotations. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @param the type of the field elements - * @return a new position-velocity, interpolated at specified date - */ - public static > - TimeStampedFieldAngularCoordinates interpolate(final AbsoluteDate date, - final AngularDerivativesFilter filter, - final Collection> sample) { - return interpolate(new FieldAbsoluteDate<>(sample.iterator().next().getRotation().getQ0().getField(), date), - filter, sample); - } - - /** Interpolate angular coordinates. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * on Rodrigues vector ensuring rotation rate remains the exact derivative of rotation. - *

        - *

        - * This method is based on Sergei Tanygin's paper Attitude - * Interpolation, changing the norm of the vector to match the modified Rodrigues - * vector as described in Malcolm D. Shuster's paper A - * Survey of Attitude Representations. This change avoids the singularity at π. - * There is still a singularity at 2π, which is handled by slightly offsetting all rotations - * when this singularity is detected. - *

        - *

        - * Note that even if first time derivatives (rotation rates) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the rotations. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @param the type of the field elements - * @return a new position-velocity, interpolated at specified date - */ - public static > - TimeStampedFieldAngularCoordinates interpolate(final FieldAbsoluteDate date, - final AngularDerivativesFilter filter, - final Collection> sample) { - - // get field properties - final Field field = sample.iterator().next().getRotation().getQ0().getField(); - - // set up safety elements for 2π singularity avoidance - final double epsilon = 2 * FastMath.PI / sample.size(); - final double threshold = FastMath.min(-(1.0 - 1.0e-4), -FastMath.cos(epsilon / 4)); - - // set up a linear model canceling mean rotation rate - final FieldVector3D meanRate; - if (filter != AngularDerivativesFilter.USE_R) { - FieldVector3D sum = FieldVector3D.getZero(field); - for (final TimeStampedFieldAngularCoordinates datedAC : sample) { - sum = sum.add(datedAC.getRotationRate()); - } - meanRate = new FieldVector3D<>(1.0 / sample.size(), sum); - } else { - if (sample.size() < 2) { - throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION, - sample.size()); - } - FieldVector3D sum = FieldVector3D.getZero(field); - TimeStampedFieldAngularCoordinates previous = null; - for (final TimeStampedFieldAngularCoordinates datedAC : sample) { - if (previous != null) { - sum = sum.add(estimateRate(previous.getRotation(), datedAC.getRotation(), - datedAC.date.durationFrom(previous.getDate()))); - } - previous = datedAC; - } - meanRate = new FieldVector3D<>(1.0 / (sample.size() - 1), sum); - } - TimeStampedFieldAngularCoordinates offset = - new TimeStampedFieldAngularCoordinates<>(date, FieldRotation.getIdentity(field), - meanRate, FieldVector3D.getZero(field)); - - boolean restart = true; - for (int i = 0; restart && i < sample.size() + 2; ++i) { - - // offset adaptation parameters - restart = false; - - // set up an interpolator taking derivatives into account - final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); - - // add sample points - double sign = +1.0; - FieldRotation previous = FieldRotation.getIdentity(field); - - for (final TimeStampedFieldAngularCoordinates ac : sample) { - - // remove linear offset from the current coordinates - final T dt = ac.date.durationFrom(date); - final TimeStampedFieldAngularCoordinates fixed = ac.subtractOffset(offset.shiftedBy(dt)); - - // make sure all interpolated points will be on the same branch - final T dot = dt.linearCombination(fixed.getRotation().getQ0(), previous.getQ0(), - fixed.getRotation().getQ1(), previous.getQ1(), - fixed.getRotation().getQ2(), previous.getQ2(), - fixed.getRotation().getQ3(), previous.getQ3()); - sign = FastMath.copySign(1.0, dot.getReal() * sign); - previous = fixed.getRotation(); - - // check modified Rodrigues vector singularity - if (fixed.getRotation().getQ0().getReal() * sign < threshold) { - // the sample point is close to a modified Rodrigues vector singularity - // we need to change the linear offset model to avoid this - restart = true; - break; - } - - final T[][] rodrigues = fixed.getModifiedRodrigues(sign); - switch (filter) { - case USE_RRA: - // populate sample with rotation, rotation rate and acceleration data - interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1], rodrigues[2]); - break; - case USE_RR: - // populate sample with rotation and rotation rate data - interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1]); - break; - case USE_R: - // populate sample with rotation data only - interpolator.addSamplePoint(dt, rodrigues[0]); - break; - default : - // this should never happen - throw new OrekitInternalError(null); - } - } - - if (restart) { - // interpolation failed, some intermediate rotation was too close to 2π - // we need to offset all rotations to avoid the singularity - offset = offset.addOffset(new FieldAngularCoordinates<>(new FieldRotation<>(FieldVector3D.getPlusI(field), - field.getZero().add(epsilon), - RotationConvention.VECTOR_OPERATOR), - FieldVector3D.getZero(field), - FieldVector3D.getZero(field))); - } else { - // interpolation succeeded with the current offset - final T[][] p = interpolator.derivatives(field.getZero(), 2); - final FieldAngularCoordinates ac = createFromModifiedRodrigues(p); - return new TimeStampedFieldAngularCoordinates<>(offset.getDate(), - ac.getRotation(), - ac.getRotationRate(), - ac.getRotationAcceleration()).addOffset(offset); - } - - } - - // this should never happen - throw new OrekitInternalError(null); - - } - } diff --git a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java new file mode 100644 index 0000000000..5f726f077d --- /dev/null +++ b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java @@ -0,0 +1,268 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitInternalError; +import org.orekit.time.AbstractFieldTimeInterpolator; +import org.orekit.time.FieldAbsoluteDate; + +import java.util.List; + +/** + * Class using Hermite interpolator to interpolate time stamped angular coordinates. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @param type of the field element + * + * @author Vincent Cucchietti + * @author Luc Maisonobe + * @see FieldHermiteInterpolator + * @see TimeStampedFieldAngularCoordinates + */ +public class TimeStampedFieldAngularCoordinatesHermiteInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** Filter for derivatives from the sample to use in interpolation. */ + private final AngularDerivativesFilter filter; + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + */ + public TimeStampedFieldAngularCoordinatesHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * /** Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedFieldAngularCoordinatesHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, AngularDerivativesFilter.USE_RR); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedFieldAngularCoordinatesHermiteInterpolator(final int interpolationPoints, + final AngularDerivativesFilter filter) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, filter); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedFieldAngularCoordinatesHermiteInterpolator(final int interpolationPoints, + final double extrapolationThreshold, + final AngularDerivativesFilter filter) { + super(interpolationPoints, extrapolationThreshold); + this.filter = filter; + } + + /** Get filter for derivatives from the sample to use in interpolation. + * @return filter for derivatives from the sample to use in interpolation + */ + public AngularDerivativesFilter getFilter() { + return filter; + } + + /** + * {@inheritDoc} + *

        + * The interpolated instance is created by polynomial Hermite interpolation on Rodrigues vector ensuring rotation rate + * remains the exact derivative of rotation. + *

        + * This method is based on Sergei Tanygin's paper Attitude Interpolation, changing the norm + * of the vector to match the modified Rodrigues vector as described in Malcolm D. Shuster's paper A + * Survey of Attitude Representations. This change avoids the singularity at π. There is still a singularity at 2π, + * which is handled by slightly offsetting all rotations when this singularity is detected. Another change is that the + * mean linear motion is first removed before interpolation and added back after interpolation. This allows to use + * interpolation even when the sample covers much more than one turn and even when sample points are separated by more + * than one turn. + *

        + *

        + * Note that even if first and second time derivatives (rotation rates and acceleration) from sample can be ignored, the + * interpolated instance always includes interpolated derivatives. This feature can be used explicitly to compute these + * derivatives when it would be too complex to compute them from an analytical formula: just compute a few sample points + * from the explicit formula and set the derivatives to zero in these sample points, then use interpolation to add + * derivatives consistent with the rotations. + */ + @Override + protected TimeStampedFieldAngularCoordinates interpolate(final InterpolationData interpolationData) { + + // Get interpolation date + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + + // Get sample + final List> sample = interpolationData.getNeighborList(); + + // set up safety elements for 2π singularity avoidance + final double epsilon = 2 * FastMath.PI / sample.size(); + final double threshold = FastMath.min(-(1.0 - 1.0e-4), -FastMath.cos(epsilon / 4)); + + // set up a linear model canceling mean rotation rate + final Field field = interpolationData.getField(); + final FieldVector3D meanRate; + FieldVector3D sum = FieldVector3D.getZero(field); + if (filter != AngularDerivativesFilter.USE_R) { + for (final TimeStampedFieldAngularCoordinates datedAC : sample) { + sum = sum.add(datedAC.getRotationRate()); + } + meanRate = new FieldVector3D<>(1.0 / sample.size(), sum); + } + else { + TimeStampedFieldAngularCoordinates previous = null; + for (final TimeStampedFieldAngularCoordinates datedAC : sample) { + if (previous != null) { + sum = sum.add(TimeStampedFieldAngularCoordinates.estimateRate(previous.getRotation(), + datedAC.getRotation(), + datedAC.getDate() + .durationFrom(previous.getDate()))); + } + previous = datedAC; + } + meanRate = new FieldVector3D<>(1.0 / (sample.size() - 1), sum); + } + TimeStampedFieldAngularCoordinates offset = + new TimeStampedFieldAngularCoordinates<>(interpolationDate, FieldRotation.getIdentity(field), + meanRate, FieldVector3D.getZero(field)); + + boolean restart = true; + for (int i = 0; restart && i < sample.size() + 2; ++i) { + + // offset adaptation parameters + restart = false; + + // set up an interpolator taking derivatives into account + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // add sample points + final KK one = interpolationData.getOne(); + double sign = 1.0; + FieldRotation previous = FieldRotation.getIdentity(field); + + for (final TimeStampedFieldAngularCoordinates ac : sample) { + + // remove linear offset from the current coordinates + final KK dt = ac.getDate().durationFrom(interpolationDate); + final TimeStampedFieldAngularCoordinates fixed = ac.subtractOffset(offset.shiftedBy(dt)); + + // make sure all interpolated points will be on the same branch + final double dot = one.linearCombination(fixed.getRotation().getQ0(), previous.getQ0(), + fixed.getRotation().getQ1(), previous.getQ1(), + fixed.getRotation().getQ2(), previous.getQ2(), + fixed.getRotation().getQ3(), previous.getQ3()).getReal(); + sign = FastMath.copySign(1.0, dot * sign); + previous = fixed.getRotation(); + + // check modified Rodrigues vector singularity + if (fixed.getRotation().getQ0().getReal() * sign < threshold) { + // the sample point is close to a modified Rodrigues vector singularity + // we need to change the linear offset model to avoid this + restart = true; + break; + } + + final KK[][] rodrigues = fixed.getModifiedRodrigues(sign); + switch (filter) { + case USE_RRA: + // populate sample with rotation, rotation rate and acceleration data + interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1], rodrigues[2]); + break; + case USE_RR: + // populate sample with rotation and rotation rate data + interpolator.addSamplePoint(dt, rodrigues[0], rodrigues[1]); + break; + case USE_R: + // populate sample with rotation data only + interpolator.addSamplePoint(dt, rodrigues[0]); + break; + default: + // this should never happen + throw new OrekitInternalError(null); + } + } + + if (restart) { + // interpolation failed, some intermediate rotation was too close to 2π + // we need to offset all rotations to avoid the singularity + offset = offset.addOffset( + new FieldAngularCoordinates<>(new FieldRotation<>(FieldVector3D.getPlusI(field), + one.multiply(epsilon), + RotationConvention.VECTOR_OPERATOR), + FieldVector3D.getZero(field), FieldVector3D.getZero(field))); + } else { + // interpolation succeeded with the current offset + final KK zero = interpolationData.getZero(); + final KK[][] p = interpolator.derivatives(zero, 2); + final FieldAngularCoordinates ac = FieldAngularCoordinates.createFromModifiedRodrigues(p); + return new TimeStampedFieldAngularCoordinates<>(offset.getDate(), + ac.getRotation(), + ac.getRotationRate(), + ac.getRotationAcceleration()).addOffset(offset); + } + + } + + // this should never happen + throw new OrekitInternalError(null); + } +} diff --git a/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinates.java b/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinates.java index 23f32ea4d0..4474aa37a4 100644 --- a/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinates.java +++ b/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,17 +16,13 @@ */ package org.orekit.utils; -import java.util.Collection; -import java.util.stream.Stream; - import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldDerivative; import org.hipparchus.analysis.differentiation.FieldDerivativeStructure; -import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitInternalError; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeStamped; @@ -96,6 +92,15 @@ public TimeStampedFieldPVCoordinates(final FieldAbsoluteDate date, final Fiel this.date = date; } + /** Constructor from Field and TimeStampedPVCoordinates. + *

        Build a TimeStampedFieldPVCoordinates from non-Field one.

        + * @param field CalculusField to base object on + * @param pv non-field, time-stamped Position-Velocity coordinates + */ + public TimeStampedFieldPVCoordinates(final Field field, final TimeStampedPVCoordinates pv) { + this(pv.getDate(), new FieldPVCoordinates(field, pv)); + } + /** Multiplicative constructor *

        Build a PVCoordinates from another one and a scale factor.

        *

        The PVCoordinates built will be a * pv

        @@ -626,107 +631,6 @@ public TimeStampedFieldPVCoordinates shiftedBy(final T dt) { spv.getPosition(), spv.getVelocity(), spv.getAcceleration()); } - /** Interpolate position-velocity. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * Note that even if first time derivatives (velocities) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the positions. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @param the type of the field elements - * @return a new position-velocity, interpolated at specified date - */ - public static > - TimeStampedFieldPVCoordinates interpolate(final FieldAbsoluteDate date, - final CartesianDerivativesFilter filter, - final Collection> sample) { - return interpolate(date, filter, sample.stream()); - } - - /** Interpolate position-velocity. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * Note that even if first time derivatives (velocities) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the positions. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @param the type of the field elements - * @return a new position-velocity, interpolated at specified date - */ - public static > - TimeStampedFieldPVCoordinates interpolate(final FieldAbsoluteDate date, - final CartesianDerivativesFilter filter, - final Stream> sample) { - - // set up an interpolator taking derivatives into account - final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); - - // add sample points - switch (filter) { - case USE_P : - // populate sample with position data, ignoring velocity - sample.forEach(pv -> { - final FieldVector3D position = pv.getPosition(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray()); - }); - break; - case USE_PV : - // populate sample with position and velocity data - sample.forEach(pv -> { - final FieldVector3D position = pv.getPosition(); - final FieldVector3D velocity = pv.getVelocity(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray()); - }); - break; - case USE_PVA : - // populate sample with position, velocity and acceleration data - sample.forEach(pv -> { - final FieldVector3D position = pv.getPosition(); - final FieldVector3D velocity = pv.getVelocity(); - final FieldVector3D acceleration = pv.getAcceleration(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray(), acceleration.toArray()); - }); - break; - default : - // this should never happen - throw new OrekitInternalError(null); - } - - // interpolate - final T[][] p = interpolator.derivatives(date.getField().getZero(), 2); - - // build a new interpolated instance - - return new TimeStampedFieldPVCoordinates<>(date, - new FieldVector3D<>(p[0]), - new FieldVector3D<>(p[1]), - new FieldVector3D<>(p[2])); - - } - /** Convert to a constant position-velocity. * @return a constant position-velocity * @since 9.0 diff --git a/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolator.java new file mode 100644 index 0000000000..b3cafcc7f8 --- /dev/null +++ b/src/main/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolator.java @@ -0,0 +1,187 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.orekit.errors.OrekitInternalError; +import org.orekit.time.AbstractFieldTimeInterpolator; +import org.orekit.time.FieldAbsoluteDate; + +import java.util.stream.Stream; + +/** + * Class using a Hermite interpolator to interpolate time stamped position-velocity-acceleration coordinates. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @param type of the field element + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see FieldHermiteInterpolator + * @see TimeStampedFieldPVCoordinates + */ +public class TimeStampedFieldPVCoordinatesHermiteInterpolator> + extends AbstractFieldTimeInterpolator, KK> { + + /** Filter for derivatives from the sample to use in interpolation. */ + private final CartesianDerivativesFilter filter; + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of angular and first time derivative for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + */ + public TimeStampedFieldPVCoordinatesHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and both time derivatives for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedFieldPVCoordinatesHermiteInterpolator(final int interpolationPoints) { + + this(interpolationPoints, CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedFieldPVCoordinatesHermiteInterpolator(final int interpolationPoints, + final CartesianDerivativesFilter filter) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, filter); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedFieldPVCoordinatesHermiteInterpolator(final int interpolationPoints, + final double extrapolationThreshold, + final CartesianDerivativesFilter filter) { + super(interpolationPoints, extrapolationThreshold); + this.filter = filter; + } + + /** filter for derivatives from the sample to use in interpolation. + * @return filter for derivatives from the sample to use in interpolation + */ + public CartesianDerivativesFilter getFilter() { + return filter; + } + + /** + * {@inheritDoc} + *

        + * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact + * derivative of position. + *

        + * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always + * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be + * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and + * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the + * positions. + */ + @Override + protected TimeStampedFieldPVCoordinates interpolate(final InterpolationData interpolationData) { + + // Get interpolation date + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + + // Convert sample to stream + final Stream> sample = interpolationData.getNeighborList().stream(); + + // Set up an interpolator taking derivatives into account + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // Add sample points + switch (filter) { + case USE_P: + // populate sample with position data, ignoring velocity + sample.forEach(pv -> { + final FieldVector3D position = pv.getPosition(); + interpolator.addSamplePoint(pv.getDate().durationFrom(interpolationDate), + position.toArray()); + }); + break; + case USE_PV: + // populate sample with position and velocity data + sample.forEach(pv -> { + final FieldVector3D position = pv.getPosition(); + final FieldVector3D velocity = pv.getVelocity(); + interpolator.addSamplePoint(pv.getDate().durationFrom(interpolationDate), + position.toArray(), velocity.toArray()); + }); + break; + case USE_PVA: + // populate sample with position, velocity and acceleration data + sample.forEach(pv -> { + final FieldVector3D position = pv.getPosition(); + final FieldVector3D velocity = pv.getVelocity(); + final FieldVector3D acceleration = pv.getAcceleration(); + interpolator.addSamplePoint(pv.getDate().durationFrom(interpolationDate), + position.toArray(), velocity.toArray(), acceleration.toArray()); + }); + break; + default: + // this should never happen + throw new OrekitInternalError(null); + } + + // Interpolate + final KK[][] pva = interpolator.derivatives(interpolationDate.getField().getZero(), 2); + + // Build a new interpolated instance + return new TimeStampedFieldPVCoordinates<>(interpolationDate, new FieldVector3D<>(pva[0]), new FieldVector3D<>(pva[1]), + new FieldVector3D<>(pva[2])); + } +} diff --git a/src/main/java/org/orekit/utils/TimeStampedGenerator.java b/src/main/java/org/orekit/utils/TimeStampedGenerator.java index abe89d700f..b6da7ef93c 100644 --- a/src/main/java/org/orekit/utils/TimeStampedGenerator.java +++ b/src/main/java/org/orekit/utils/TimeStampedGenerator.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/TimeStampedPVCoordinates.java b/src/main/java/org/orekit/utils/TimeStampedPVCoordinates.java index 1124da5300..673eb6aba0 100644 --- a/src/main/java/org/orekit/utils/TimeStampedPVCoordinates.java +++ b/src/main/java/org/orekit/utils/TimeStampedPVCoordinates.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -17,18 +17,15 @@ package org.orekit.utils; import java.io.Serializable; -import java.util.Collection; -import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.Derivative; -import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitInternalError; import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; @@ -221,6 +218,12 @@ public TimeStampedPVCoordinates shiftedBy(final double dt) { */ public PVCoordinatesProvider toTaylorProvider(final Frame instanceFrame) { return new PVCoordinatesProvider() { + /** {@inheritDoc} */ + public Vector3D getPosition(final AbsoluteDate d, final Frame f) { + final TimeStampedPVCoordinates shifted = shiftedBy(d.durationFrom(getDate())); + final StaticTransform transform = instanceFrame.getStaticTransformTo(f, d); + return transform.transformPosition(shifted.getPosition()); + } /** {@inheritDoc} */ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate d, final Frame f) { final TimeStampedPVCoordinates shifted = shiftedBy(d.durationFrom(date)); @@ -230,100 +233,6 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate d, final Fr }; } - /** Interpolate position-velocity. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * Note that even if first time derivatives (velocities) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the positions. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @return a new position-velocity, interpolated at specified date - */ - public static TimeStampedPVCoordinates interpolate(final AbsoluteDate date, - final CartesianDerivativesFilter filter, - final Collection sample) { - return interpolate(date, filter, sample.stream()); - } - - /** Interpolate position-velocity. - *

        - * The interpolated instance is created by polynomial Hermite interpolation - * ensuring velocity remains the exact derivative of position. - *

        - *

        - * Note that even if first time derivatives (velocities) - * from sample can be ignored, the interpolated instance always includes - * interpolated derivatives. This feature can be used explicitly to - * compute these derivatives when it would be too complex to compute them - * from an analytical formula: just compute a few sample points from the - * explicit formula and set the derivatives to zero in these sample points, - * then use interpolation to add derivatives consistent with the positions. - *

        - * @param date interpolation date - * @param filter filter for derivatives from the sample to use in interpolation - * @param sample sample points on which interpolation should be done - * @return a new position-velocity, interpolated at specified date - * @since 9.0 - */ - public static TimeStampedPVCoordinates interpolate(final AbsoluteDate date, - final CartesianDerivativesFilter filter, - final Stream sample) { - - // set up an interpolator taking derivatives into account - final HermiteInterpolator interpolator = new HermiteInterpolator(); - - // add sample points - switch (filter) { - case USE_P : - // populate sample with position data, ignoring velocity - sample.forEach(pv -> { - final Vector3D position = pv.getPosition(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray()); - }); - break; - case USE_PV : - // populate sample with position and velocity data - sample.forEach(pv -> { - final Vector3D position = pv.getPosition(); - final Vector3D velocity = pv.getVelocity(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray()); - }); - break; - case USE_PVA : - // populate sample with position, velocity and acceleration data - sample.forEach(pv -> { - final Vector3D position = pv.getPosition(); - final Vector3D velocity = pv.getVelocity(); - final Vector3D acceleration = pv.getAcceleration(); - interpolator.addSamplePoint(pv.getDate().durationFrom(date), - position.toArray(), velocity.toArray(), acceleration.toArray()); - }); - break; - default : - // this should never happen - throw new OrekitInternalError(null); - } - - // interpolate - final double[][] p = interpolator.derivatives(0.0, 2); - - // build a new interpolated instance - return new TimeStampedPVCoordinates(date, new Vector3D(p[0]), new Vector3D(p[1]), new Vector3D(p[2])); - - } - /** Return a string representation of this date, position, velocity, and acceleration. * *

        This method uses the {@link DataContext#getDefault() default data context}. diff --git a/src/main/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolator.java new file mode 100644 index 0000000000..218ec22fbc --- /dev/null +++ b/src/main/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolator.java @@ -0,0 +1,182 @@ +/* Copyright 2002-2023 CS GROUP + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitInternalError; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; + +import java.util.stream.Stream; + +/** + * Class using a Hermite interpolator to interpolate time stamped position-velocity-acceleration coordinates. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points + * (about 10-20 points) in order to avoid Runge's phenomenon + * and numerical problems (including NaN appearing). + * + * @author Luc Maisonobe + * @author Vincent Cucchietti + * @see HermiteInterpolator + * @see TimeStampedPVCoordinates + */ +public class TimeStampedPVCoordinatesHermiteInterpolator extends AbstractTimeInterpolator { + + /** Filter for derivatives from the sample to use in interpolation. */ + private final CartesianDerivativesFilter filter; + + /** + * Constructor with : + *

          + *
        • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
        • + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and both time derivatives for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + */ + public TimeStampedPVCoordinatesHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        • Use of position and both time derivatives for attitude interpolation
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedPVCoordinatesHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, CartesianDerivativesFilter.USE_PVA); + } + + /** + * Constructor with : + *
          + *
        • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
        • + *
        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedPVCoordinatesHermiteInterpolator(final int interpolationPoints, + final CartesianDerivativesFilter filter) { + + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, filter); + } + + /** + * Constructor. + *

        + * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation + * points (about 10-20 points) in order to avoid Runge's + * phenomenon and numerical problems (including NaN appearing). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param filter filter for derivatives from the sample to use in interpolation + */ + public TimeStampedPVCoordinatesHermiteInterpolator(final int interpolationPoints, + final double extrapolationThreshold, + final CartesianDerivativesFilter filter) { + super(interpolationPoints, extrapolationThreshold); + this.filter = filter; + } + + /** Get filter for derivatives from the sample to use in interpolation. + * @return filter for derivatives from the sample to use in interpolation + */ + public CartesianDerivativesFilter getFilter() { + return filter; + } + + /** + * {@inheritDoc} + *

        + * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact + * derivative of position. + *

        + * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always + * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be + * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and + * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the + * positions. + */ + @Override + protected TimeStampedPVCoordinates interpolate(final InterpolationData interpolationData) { + + // Get date + final AbsoluteDate date = interpolationData.getInterpolationDate(); + + // Convert sample to stream + final Stream sample = interpolationData.getNeighborList().stream(); + + // Set up an interpolator taking derivatives into account + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Add sample points + switch (filter) { + case USE_P: + // populate sample with position data, ignoring velocity + sample.forEach(pv -> { + final Vector3D position = pv.getPosition(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray()); + }); + break; + case USE_PV: + // populate sample with position and velocity data + sample.forEach(pv -> { + final Vector3D position = pv.getPosition(); + final Vector3D velocity = pv.getVelocity(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray(), velocity.toArray()); + }); + break; + case USE_PVA: + // populate sample with position, velocity and acceleration data + sample.forEach(pv -> { + final Vector3D position = pv.getPosition(); + final Vector3D velocity = pv.getVelocity(); + final Vector3D acceleration = pv.getAcceleration(); + interpolator.addSamplePoint(pv.getDate().durationFrom(date), + position.toArray(), velocity.toArray(), acceleration.toArray()); + }); + break; + default: + // this should never happen + throw new OrekitInternalError(null); + } + + // Interpolate + final double[][] pva = interpolator.derivatives(0.0, 2); + + // Build a new interpolated instance + return new TimeStampedPVCoordinates(date, new Vector3D(pva[0]), new Vector3D(pva[1]), new Vector3D(pva[2])); + } +} diff --git a/src/main/java/org/orekit/utils/TrackingCoordinates.java b/src/main/java/org/orekit/utils/TrackingCoordinates.java new file mode 100644 index 0000000000..08550ec2c2 --- /dev/null +++ b/src/main/java/org/orekit/utils/TrackingCoordinates.java @@ -0,0 +1,74 @@ +/* Copyright 2023 Luc Maisonobe + * Licensed to CS GROUP (CS) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * CS licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.orekit.utils; + +/** Container for azimut/elevation/range coordinates as seen from a ground point. + * @see org.orekit.frames.TopocentricFrame + * @since 12.0 + */ +public class TrackingCoordinates { + + /** Azimuth. */ + private final double azimuth; + + /** Elevation. */ + private final double elevation; + + /** Range. */ + private final double range; + + /** Simple constructor. + * @param azimuth azimuth + * @param elevation elevation + * @param range range + */ + public TrackingCoordinates(final double azimuth, final double elevation, final double range) { + this.azimuth = azimuth; + this.elevation = elevation; + this.range = range; + } + + /** Get the azimuth. + *

        The azimuth is the angle between the North direction at local point and + * the projection in local horizontal plane of the direction from local point + * to given point. Azimuth angles are counted clockwise, i.e positive towards the East.

        + * @return azimuth + */ + public double getAzimuth() { + return azimuth; + } + + /** Get the elevation. + *

        The elevation is the angle between the local horizontal and + * the direction from local point to given point.

        + * @return elevation + */ + public double getElevation() { + return elevation; + } + + /** Get the range. + * @return range + */ + public double getRange() { + return range; + } + +} + + + diff --git a/src/main/java/org/orekit/utils/WaypointPVBuilder.java b/src/main/java/org/orekit/utils/WaypointPVBuilder.java index ff5ed7cb56..9cf5410826 100644 --- a/src/main/java/org/orekit/utils/WaypointPVBuilder.java +++ b/src/main/java/org/orekit/utils/WaypointPVBuilder.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 Joseph Reed +/* Copyright 2002-2023 Joseph Reed * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -58,21 +58,24 @@ public class WaypointPVBuilder { private final OneAxisEllipsoid body; /** Set of waypoints, indexed by time. */ - private final TreeMap waypoints = new TreeMap<>(); + private final TreeMap waypoints; /** Whether the resulting provider should be invalid or constant prior to the first waypoint. */ - private boolean invalidBefore = true; + private boolean invalidBefore; /** Whether the resulting provider should be invalid or constant after to the last waypoint. */ - private boolean invalidAfter = true; + private boolean invalidAfter; /** Create a new instance. * @param factory The factory used to create the intermediate coordinate providers between waypoints. * @param body The central body, on which the way points are defined. */ public WaypointPVBuilder(final InterpolationFactory factory, final OneAxisEllipsoid body) { - this.factory = factory; - this.body = body; + this.factory = factory; + this.body = body; + this.waypoints = new TreeMap<>(); + this.invalidBefore = true; + this.invalidAfter = true; } /** Construct a waypoint builder interpolating points using a linear cartesian interpolation. @@ -297,6 +300,20 @@ static class GreatCircleWaypointPVProv implements PVCoordinatesProvider { initialAltitude = point1.getAltitude(); } + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + final double d = date.durationFrom(t0); + final double fraction = d / duration; + final double phase = fraction * phaseLength; + + final S2Point sp = new S2Point(circle.getPointAt(phase0 + phase)); + final GeodeticPoint point = toGeodetic(sp, initialAltitude + d * altitudeSlope); + final Vector3D p = body.transform(point); + + return body.getBodyFrame().getStaticTransformTo(frame, date).transformPosition(p); + + } + @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { final double d = date.durationFrom(t0); @@ -317,10 +334,19 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final return body.getBodyFrame().getTransformTo(frame, date).transformPVCoordinates(tpv); } + /** Converts the given geodetic point to a point on the 2-sphere. + * @param point input geodetic point + * @return a point on the 2-sphere + */ static S2Point toSpherical(final GeodeticPoint point) { return new S2Point(point.getLongitude(), 0.5 * FastMath.PI - point.getLatitude()); } + /** Converts a 2-sphere point to a geodetic point. + * @param point point on the 2-sphere + * @param alt point altitude + * @return a geodetic point + */ static GeodeticPoint toGeodetic(final S2Point point, final double alt) { return new GeodeticPoint(0.5 * FastMath.PI - point.getPhi(), point.getTheta(), alt); } @@ -357,6 +383,15 @@ static class LoxodromeWaypointPVProv implements PVCoordinatesProvider { this.velocity = arc.getDistance() / duration; } + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + final double fraction = date.durationFrom(t0) / duration; + final GeodeticPoint point = arc.calculatePointAlongArc(fraction); + final Vector3D p = arc.getBody().transform(point); + + return arc.getBody().getBodyFrame().getStaticTransformTo(frame, date).transformPosition(p); + } + @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { final double fraction = date.durationFrom(t0) / duration; @@ -403,6 +438,13 @@ static class CartesianWaypointPVProv implements PVCoordinatesProvider { this.sourceFrame = body.getBodyFrame(); } + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + final double d = date.durationFrom(t0); + final Vector3D p = p0.add(vel.scalarMultiply(d)); + return sourceFrame.getStaticTransformTo(frame, date).transformPosition(p); + } + @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { final double d = date.durationFrom(t0); @@ -410,5 +452,6 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final final TimeStampedPVCoordinates pv = new TimeStampedPVCoordinates(date, p, vel); return sourceFrame.getTransformTo(frame, date).transformPVCoordinates(pv); } + } } diff --git a/src/main/java/org/orekit/utils/package-info.java b/src/main/java/org/orekit/utils/package-info.java index 174c286420..acb4a0c287 100644 --- a/src/main/java/org/orekit/utils/package-info.java +++ b/src/main/java/org/orekit/utils/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/Lexer.java b/src/main/java/org/orekit/utils/units/Lexer.java index 9f6626b55e..629cd3b668 100644 --- a/src/main/java/org/orekit/utils/units/Lexer.java +++ b/src/main/java/org/orekit/utils/units/Lexer.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -160,6 +160,15 @@ public Token next() { return emit(start + 3, TokenType.FRACTION, 1, 2); } + // look for special case "1.5" (used by CCSDS for power 3/2) + if (start < end - 2 && + unitSpecification.charAt(start) == '1' && + unitSpecification.charAt(start + 1) == '.' && + unitSpecification.charAt(start + 2) == '5') { + // 3/2 written as decimal number + return emit(start + 3, TokenType.FRACTION, 3, 2); + } + // look for unicode fractions if (unitSpecification.charAt(start) == '¼') { return emit(start + 1, TokenType.FRACTION, 1, 4); diff --git a/src/main/java/org/orekit/utils/units/Parser.java b/src/main/java/org/orekit/utils/units/Parser.java index f7a4137687..84d35ff8fd 100644 --- a/src/main/java/org/orekit/utils/units/Parser.java +++ b/src/main/java/org/orekit/utils/units/Parser.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/PowerTerm.java b/src/main/java/org/orekit/utils/units/PowerTerm.java index fef55904fc..bccf6525ee 100644 --- a/src/main/java/org/orekit/utils/units/PowerTerm.java +++ b/src/main/java/org/orekit/utils/units/PowerTerm.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/Prefix.java b/src/main/java/org/orekit/utils/units/Prefix.java index 8f7eeaa283..1faf7cf3b6 100644 --- a/src/main/java/org/orekit/utils/units/Prefix.java +++ b/src/main/java/org/orekit/utils/units/Prefix.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/PrefixedUnit.java b/src/main/java/org/orekit/utils/units/PrefixedUnit.java index 0fb0bc8133..349d893724 100644 --- a/src/main/java/org/orekit/utils/units/PrefixedUnit.java +++ b/src/main/java/org/orekit/utils/units/PrefixedUnit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/Token.java b/src/main/java/org/orekit/utils/units/Token.java index 4515e31e4c..32f59e3cc5 100644 --- a/src/main/java/org/orekit/utils/units/Token.java +++ b/src/main/java/org/orekit/utils/units/Token.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/TokenType.java b/src/main/java/org/orekit/utils/units/TokenType.java index 4209fe2ec7..16025838ee 100644 --- a/src/main/java/org/orekit/utils/units/TokenType.java +++ b/src/main/java/org/orekit/utils/units/TokenType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/Unit.java b/src/main/java/org/orekit/utils/units/Unit.java index 30267dfd98..77f827cf88 100644 --- a/src/main/java/org/orekit/utils/units/Unit.java +++ b/src/main/java/org/orekit/utils/units/Unit.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -128,7 +128,7 @@ public class Unit implements Serializable { public static final Unit TESLA = VOLT.multiply(null, SECOND).divide("T", METRE.power(null, Fraction.TWO)); /** Solar Flux Unit. */ - public static final Unit SOLAR_FLUX_UNIT = WATT.divide(null, METRE.power(null, Fraction.TWO).multiply(null, HERTZ)).scale("sfu", 1.0e-22); + public static final Unit SOLAR_FLUX_UNIT = WATT.divide(null, METRE.power(null, Fraction.TWO).multiply(null, HERTZ)).scale("SFU", 1.0e-22); /** Total Electron Content Unit. */ public static final Unit TOTAL_ELECTRON_CONTENT_UNIT = METRE.power(null, new Fraction(-2)).scale("TECU", 1.0e+16); @@ -257,6 +257,35 @@ public Unit sameDimensionSI() { return new Unit(builder.toString(), 1.0, mass, length, time, current, angle); } + /** Ensure some units are compatible with reference units. + * @param description description of the units list (for error message generation) + * @param reference reference units + * @param units units to check + * @param allowScaleDifferences if true, unit with same dimension but different + * scale (like {@link #KILOMETRE} versus {@link #METRE}) are allowed, otherwise they will trigger an exception + * @exception OrekitException if units are not compatible (number of elements, dimensions or scaling) + */ + public static void ensureCompatible(final String description, final List reference, + final boolean allowScaleDifferences, final List units) { + if (units.size() != reference.size()) { + throw new OrekitException(OrekitMessages.WRONG_NB_COMPONENTS, + description, reference.size(), units.size()); + } + for (int i = 0; i < reference.size(); ++i) { + if (!reference.get(i).sameDimension(units.get(i))) { + throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS, + reference.get(i).getName(), + units.get(i).getName()); + } + if (!(allowScaleDifferences || + Precision.equals(reference.get(i).getScale(), units.get(i).getScale(), 1))) { + throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS, + reference.get(i).getName(), + units.get(i).getName()); + } + } + } + /** Append a dimension contribution to a unit name. * @param builder builder for unit name * @param dim name of the dimension diff --git a/src/main/java/org/orekit/utils/units/UnitsCache.java b/src/main/java/org/orekit/utils/units/UnitsCache.java index cc00f039cc..3bc3188d0a 100644 --- a/src/main/java/org/orekit/utils/units/UnitsCache.java +++ b/src/main/java/org/orekit/utils/units/UnitsCache.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/java/org/orekit/utils/units/UnitsConverter.java b/src/main/java/org/orekit/utils/units/UnitsConverter.java index 6ba11ab5a9..db04f84caf 100644 --- a/src/main/java/org/orekit/utils/units/UnitsConverter.java +++ b/src/main/java/org/orekit/utils/units/UnitsConverter.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -48,6 +48,10 @@ public class UnitsConverter { public static final UnitsConverter MILLI_SECONDS_TO_SECONDS = new UnitsConverter(Unit.parse("ms"), Unit.SECOND); + /** Nano Teslas to Tesla converter. */ + public static final UnitsConverter NANO_TESLAS_TO_TESLAS = + new UnitsConverter(Unit.parse("nT"), Unit.TESLA); + /** Days to seconds converter. */ public static final UnitsConverter DAYS_TO_SECONDS = new UnitsConverter(Unit.DAY, Unit.SECOND); diff --git a/src/main/java/org/orekit/utils/units/package-info.java b/src/main/java/org/orekit/utils/units/package-info.java index 1ca15d40c3..6d06360bde 100644 --- a/src/main/java/org/orekit/utils/units/package-info.java +++ b/src/main/java/org/orekit/utils/units/package-info.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 index 7fe8e686c5..f4073f88c3 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = uventet data i linje {0} i filen {1} # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = ikke-kronologiske datoer i filen {0}, linje {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = ingen IERS UTC-TAI historikdata er indlæst @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = kunne ikke analysere elementet {0} på linje { # unable to find file {0} UNABLE_TO_FIND_FILE = kunne ikke finde filen {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = rumfartøjets masse bliver negativ (m: {0}) - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = positiv strøm (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = ingen binære JPL efemeridefiler fundet # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = dato udenfor gyldighedsperioden for {0} efemeriderne: {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = dato udenfor gyldighedsperioden for efemeriderne: {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = dato udenfor gyldighedsperioden: {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = to uventede højdeværdier: {0 # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = ikke understøttet parameternavn {0}, understøttede navne: {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = skaleringsfaktor for parameter {0} er for lav: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = ingen IERS-konventioner er angivet før indlæsninge # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = ramme {0} er ikke gyldig i denne CCSDS fils kontekst +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = Ikke-konsistente tidssystemer: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = Tidssystem skulle have været sat før linje {0} af fil {1} +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = navnet "{0}" er allerede brugt til en anden tilstand @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = ikke-konsistent skyggeberegning: indgang = {0} og # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = Den aktuelle bane har en excentricitet ({0} > 0.5). DSST kræver en ikke implementeret tidsafhængig numerisk metode til at beregne de gennemsnitlige hastigheder -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = ikke-understøttet version {0} af sp3 fil +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = ikke-understøttet version "{0}" af sp3 fil + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = fandt {0} epoker i filen {1}, forventede {2} -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = uventet ende på fil i sp3 fil (efter linje {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = uventet ende på fil i STK fil (efter linje {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = ikke-understøttet urfilversion {0} -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = ikke-understøttet navigationsbeskedfilversion {0} +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = ikke-eksisterende geomagnetisk model {0} for år {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = geomagnetisk model {0} med epoke {1} understøtter # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = tidstransformationen af den geomagnetiske model {0} med epoke {1} er udenfor sit gyldighedsområde: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = ikke nok data til interpolation (intervalstørrelse = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = ikke nok data (intervalstørrelse = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = for få mellemlagrede naboer: {0} (skal mindst være {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = kun {0} GNSS omløb er angivet og {1} påkræves for a # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = brugen af tidssystemet {0} i CCSDS filer kræver en ekstra ICD, og er ikke implementeret i Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = forkert antal enheder til manøvre {0} # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = manglende tidsfelt for manøvre {0} +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Oprettelsen af en aggregeret propagator kræver mindst en propagator-bestanddel, men ingen blev angivet. @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = ukendt satellitsystem {0} # unknown time system {0} UNKNOWN_TIME_SYSTEM = ukendt tidssystem {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = ukendt ur datatype {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = ukendt RINEX-frekvens {0} i filen {1}, linje {2} # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = uafstemte frekvenser i filen {0}, linje {1} (forventede {2}, fandt {3}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = forkert kolonneantal i filen {0}, linje {1} (forventede {2} kolonner, fandt {3} kolonner) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = ugyldigt satellitsystem {0} -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = uventet indholdstype {0} # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 index 29cd61c920..5559b29dac 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = nicht chronologische Daten, abgelegt unter Datei {0}, Zeile {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = keine IERS UTC-TAI historische Daten geladen @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = # unable to find file {0} UNABLE_TO_FIND_FILE = Datei {0} kann nicht gefunden werden -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = Satellitenmasse wird negativ (m: {0}) - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = positive Durchflussrate (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = keine JPL-Ephemerides Binärdateien gefu # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = Datum nicht im zulässigen Bereich für {0} ephemerides: {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = Datum nicht im zulässigen Bereich für ephemerides: {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = Datum nicht im zulässigen Bereich: {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = unerwartet zwei Höhenwerte: { # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = nicht unterstützter Parametername {0}, unterstützte Namen {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = Skalierungsfaktor für Parameter {0} ist zu klein: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = es wurden keine IERS Richtlinien eingestellt vor dem # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = Koordinatennetz {0} ist nicht gültig im Kontext dieser CCSDS Datei +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = inkonsistente Zeitreferenz: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = Name "{0}" wird bereits für einen anderen Zustand verwendet @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = inkonsistente Schattenberechnung: Anfang = {0} wo # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = Der aktuelle Orbit hat eine Exzentrizität ({0} > 0.5). DSST benötigt eine nicht implementierte, zeitabhängige numerische Methode um die gemittelten Raten zu berechnen -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = nicht unterstützte SP3 Dateiversion {0} +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = nicht unterstützte SP3 Dateiversion "{0}" + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = Es wurden {0} von {2} erwarteten Epochen in der Datei {1} gefunden -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = unerwartetes Ende in der SP3 Datei (nach Zeile {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = unerwartetes Ende in der STK Datei (nach Zeile {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = nicht unterstützte Clock-Dateiversion {0} -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = nicht existierendes geo-magnetisches Modell {0} für das Jahr {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = geo-magnetisches Feldmodell {0} mit Epoche {1} unte # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = Zeittransformation des geo-magnetischen Feldmodells {0} mit Epoche {1} liegt ausserhalb des gültigen Bereichs: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = nicht genügend Daten verfügbar zum Interpolieren (Datenmenge = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = nicht genügend Daten verfügbar (Datenmenge = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = nicht genug Nachbareinträge im Cache verfügbar: {0} (minimum {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = Es wurden keine {0} GNSS Orbits angegeben, während {1 # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = Die Benutzung des Zeitsystems {0} in CCSDS Dateien benötigt zusätzliche ICD und ist nicht von Orekit implementiert -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Das erstellen von AggregatePropagator benötigt zumindest einen Bestandteil des Propagator. Es wurden keine zur Verfügung gestellt @@ -523,15 +565,18 @@ STATION_NOT_FOUND = Station {0} nicht gefunden! Bekannte Stationen: {1} # unknown satellite system {0} UNKNOWN_SATELLITE_SYSTEM = Unbekanntes Satellitensystem {0} -# unknown satellite antenna code {0} -UNKNOWN_SATELLITE_ANTENNA_CODE = Unbekannter Satellitenantennencode {0} - # unknown time system {0} UNKNOWN_TIME_SYSTEM = Unbekannter Zeitsystem {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = Unbekannter Uhrendatentyp {0} +# unknown satellite antenna code {0} +UNKNOWN_SATELLITE_ANTENNA_CODE = Unbekannter Satellitenantennencode {0} + # frequency {0} is not supported by antenna {1} UNSUPPORTED_FREQUENCY_FOR_ANTENNA = Die Frequenz {0} ist von der Antenne {1} nicht unterstützt @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = Unbekannte RINEX Frequenz {0} in Datei {1} bei Zeile { # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = Keine Übereinstimmung der Frequenzen in Datei {0}, Zeile {1} (erwartet {2}, erhalten {3}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = Falsche Anzahl an Spalten in Datei {0}, Zeile {1} (erwartet {2}, erhalten {3}) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = Unregelmäßige oder unvollständige "grid" in Da # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = Ungültiges Satellitensystem {0} -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = IONEX Datei {0} beinhaltet keine TEC Daten für Datum {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = IONEX Datei {0} beinhaltet keine TEC Daten für Datum {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = Anzahl an Karten {0} ist nicht konsistent mit der Kopfzeilen-Spezifikation: {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 index 7084bf7bde..5caea1b28d 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = μη χρονολογικές ημερομηνίες στο αρχείο {0}, γραμμή {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = κανένα φορτωμένο IERS UTC-TAI ιστορικό δεδομένων @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = # unable to find file {0} UNABLE_TO_FIND_FILE = αδύνατο να βρεθεί το αρχείο {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = η μάζα του διαστημικού οχήματος γίνεται αρνητική: {0} kg - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = θετικός ρυθμός ροής (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = κανένα αρχείο JPL δυαδι # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = ημερομηνία εκτός εύρους για {0} εφημερίδες: {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = ημερομηνία εκτός εύρους για εφημερίδες: {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = ημερομηνία εκτός εύρους: {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = απροσδόκητες υψ # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = μη έγκυρο όνομα παραμέτρου {0}, έγκυρα ονόματα: {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = δεν υπάρχουν IERs συμβάσεις πο # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = το πλαίσιο {0} δεν είναι έγκυρο στο πλαίσιο αρχείου CCSDS +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems in the ephemeris blocks: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = μη έγκυρα συστήματα του χρόνου: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = το όνομα "{0}" χρησιμοποιείται ήδη για μια επιπλέον κατάσταση @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = μη έγκυρος υπολογισμός σκι # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = Η τρέχουσα τροχιά έχει εκκεντρότητα ({0}> 0.5). Ο DSST χρειάζεται για τον υπολογισμό των κατά μέσο όρο ποσοστών, μία αριθμητική μέθοδο εξαρτώμενη από το χρόνο που δεν έχει ακόμη υλοποιηθεί -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = μη υποστηριζόμενη έκδοση του αρχείου sp3 {0} +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = μη υποστηριζόμενη έκδοση του αρχείου sp3 "{0}" + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = μη αναμενόμενο τέλος αρχείου στο αρχείο sp3 (μετά από τη γραμμή {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = μη αναμενόμενο τέλος αρχείου στο αρχείο STK (μετά από τη γραμμή {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = μη υποστηριζόμενη έκδοση αρχείου ρολογιού {0} -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = ανύπαρκτο γεωμαγνητικό μοντέλο {0} για το έτος {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = το γεωμαγνητικό μοντέλο {0} μ # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = o χρόνος μετατροπής τoυ γεωμαγνητικού μοντέλου {0} με την εποχή {1} είναι εκτός της περιοχής ισχύος: {2}! = [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = δεν υπάρχουν επαρκή δεδομένα για την παρεμβολή (μέγεθος δείγματος = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = δεν υπάρχουν επαρκή δεδομένα (μέγεθος δείγματος = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = πολύ μικρός αριθμός των προσωρινά αποθηκευμένων γειτόνων {0} (πρέπει να είναι τουλάχιστον {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = # unknown time system {0} UNKNOWN_TIME_SYSTEM = άγνωστο σύστημα χρόνου {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = άγνωστος τύπος δεδομένων ρολογιού {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 index 358d001f26..50760d653e 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = unexpected data at line {0} in file {1} # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = non-chronological dates in file {0}, line {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = inconsistent sampling date: expected {0} but got {1} + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = no IERS UTC-TAI history data loaded @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = unable to parse element {0} at line {1}, file # unable to find file {0} UNABLE_TO_FIND_FILE = unable to find file {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = spacecraft mass becomes negative: {0} kg - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = positive flow rate (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = no JPL ephemerides binary files found # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = out of range date for {0} ephemerides: {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = out of range date for ephemerides: {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = out of range date: {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = unexpected two elevation value # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = unsupported parameter name {0}, supported names: {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = scale factor for parameter {0} is too small: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = no IERS conventions have been set before parsing # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = frame {0} is not valid in this CCSDS file context +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = inconsistent time systems: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = no Range Units converter configured fo # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = Time system should have already been set before line {0} of file {1} +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = cannot estimate precession without proper derivatives + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = name "{0}" is already used for an additional state @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = inconsistent shadow computation: entry = {0} wher # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = unsupported sp3 file version {0} +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = unsupported sp3 file version "{0}" + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = invalid header entry {0} "{1}" in file {2} (format version {3}) + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = version "{0}" supports only up to {1} satellites, found {2} in file {3} # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = found {0} epochs in file {1}, expected {2} -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = unexpected end of sp3 file (after line {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = cannot splice sp3 files with incompatible metadata + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = cannot splice sp3 files with incompatible satellite metadata for satellite {0} + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = STK coordinate system "{0}" is invalid or not yet supported + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = STK coordinate system "{0}" has not been mapped to an Orekit frame + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = unexpected end of STK file (after line {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = unsupported clock file version {0} -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = unsupported navigation messages file version {0} +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = version {0} from file {1} is not supported, supported version: {2} # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = non-existent geomagnetic model {0} for year {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = geomagnetic model {0} with epoch {1} does not suppo # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = not enough data for interpolation (sample size = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = not enough data (sample size = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = too small number of cached neighbors: {0} (must be at least {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = only {0} GNSS orbits are provided while {1} are needed # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = inconsistent time systems in the attitude blocks: {0} ≠ {1} +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = unknown attitude type {0} -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = attitude type {0} in CCSDS AEM files is not implemented in Orekit +# incomplete data +CCSDS_INCOMPLETE_DATA = incomplete data # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = invalid rotation sequence {0} at line {1} of file {2} @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = element set type {0} ({1}) is not supported # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = retrograde factor not supported in element set {0} -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = element set type {0} ({1}) expects {2} elements - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = wrong number of units for maneuver {0} # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = missing time field for maneuver {0} +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = attitude type {0} and rate type {1} calls for {2} states, got {3} + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = incompatible keys {0} and {1} should not both be used + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = sensor index {0} is already used + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = missing sensor index {0} + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = inconsistent number of elements: expected {0}, got {1} + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Creating an aggregate propagator requires at least one constituent propagator, but none were provided. @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = unknown satellite system {0} # unknown time system {0} UNKNOWN_TIME_SYSTEM = unknown time system {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = unknown UTC Id {0} + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = unknown clock data type {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = unknown RINEX frequency {0} in file {1}, line {2} # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = wrong parsing type for file {0} + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = irregular or incomplete grid in file {0} # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = invalid satellite system {0} -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = IONEX file {0} does not contain TEC data for date {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = IONEX files {0} does not contain TEC data for date {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = number of maps {0} is inconsistent with header specification: {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = unexpected content type {0} # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = cannot parse GNSS data from {0} +# invalid GNSS data: {0} +INVALID_GNSS_DATA = invalid GNSS data: {0} + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = GNSS parity error on word {0} + # unknown host {0} UNKNOWN_HOST = unknown host {0} @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = attempt to generate file {0} with a formatt # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = backward propagation not allowed here - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = missing station data for epoch {0} # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = inconsistent parameters selection between pairs {0}/{1} and {2}/{3} @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = transform from {0} to {1} is not implemented # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = orbital parameters type: {0} is different from expected orbital type : {1} +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = {0} expects {1} elements, got {2} + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = cannot change covariance type if defined in a local orbital frame # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = cannot change covariance type if defined in a non pseudo-inertial reference frame + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = primary collision object time of closest approach is different from the secondary collision object's one + +# first date {0} does not match second date {1} +DATES_MISMATCH = first date {0} does not match second date {1} + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = first orbit mu {0} does not match second orbit mu {1} + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = state date {0} does not match its covariance date {1} + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = wrong interpolator defined for this spacecraft state type (orbit or absolute PV) + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = multiple interpolators are used so they may use different numbers of interpolation points + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = header for file {0} has not been written yet + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = header for file {0} has already been written + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = Cannot start the propagation from an infinitely far date + +# invalid satellite id {0} +INVALID_SATELLITE_ID = invalid satellite id {0} + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = EOP interpolation degree must be of the form 4k-1, got {0} diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 index e6ec8e0440..7d14d8b07a 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = datos imprevistos en la línea {0} del fichero # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = fechas no cronológicas en el fichero {0} en la línea {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = fechas de las muestras incoherentes: se esperaba {0} , pero se ha dado {1} + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = los datos históricos IERSD UTC-TAI no están cargados @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = imposible de analizar el elemento {0} de la l # unable to find file {0} UNABLE_TO_FIND_FILE = imposible de encontrar el fichero {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = la masa del vehículo adquiere un valor negativo : {0} kg - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = flujo de masa positiva (q : {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = no se ha encontrado ningún fichero bina # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = fecha fuera del rango de validez de las efemérides « {0} » : {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = fecha fuera del rango de validez de las efemérides : {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = fecha fuera del rango de validez : {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = fecha fuera del rango de validez de las efemérides: {0} es {3,number,0.0##############E0} segundos anterior a [{1}, {2}] @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = datos inesperados, dos valores # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = nombre de parámetro no conocido {0}, parámetro conocido {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = {0} parámetro contiene varios intervalos en su valor TimeSpanMap, es necessario utilizar {1} + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = la función setPeriod ya ha sido utilizada para el parámetro {0}, se debe crear un nuevo parámetro + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = el factor de escala para el parámetro {0} es demasiado pequeño: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = no se ha inicializado ninguna convención IERS antes # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = el sistema de referencia {0} no es válido en este contexto de fichero CCSDS +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = este punto de referencia local LVLH utiliza una definición diferente, utilice el punto de referencia LVLH_CCSDS en su lugar + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = sistemas temporales inconsistentes: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = no se ha encontrado ningún convertido # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = el sistema temporal tendría que haber sido inicializado antes de la línea {0} del fichero {1} +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = imposible estimar la precisión sin las derivadas correctas + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = el nombre "{0}" ya se está utilizando para un estado adicional @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = cálculo de sombra incoherente : entrada = {0} mi # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = la órbita tiene una excentricidad ({0} > 0.5). DSST necesita para calcular las derivadas medias un método numérico dependiente del tiempo que no está implementado -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = versión de formato sp3 {0} no reconocida +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = versión de formato sp3 "{0}" no reconocida + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = se han encontrado {0} épocas en el fichero {1} en vez de {2} -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = final inesperado de un fichero sp3 (después de la línea {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = imposible de adjuntar los ficheros sp3 teniendo metadata diferente + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = imposible de adjuntar los ficheros sp3 teniendo metadata del satéllite {0} diferente + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = el sistema de coordenadas "{0}" de STK es inválido o no aún implementado + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = el sistema de coordenadas "{0}" de STK aún no ha sido mapeado a una estructura Orekit + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = final inesperado de un fichero STK (después de la línea {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = versión de archivo de reloj {0} no reconocida -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = versión de formato de los mensajes de navegación {0} no reconocida +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = verion {0} del fichero {1} no se ha tenido en cuenta, versiones tenidas en cuenta : {2} # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = no existe el fichero de modelo geomagnético {0} para el año {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = el modelo geomagnético {0} en la época {1} no tie # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = la transformación temporal del modelo geomagnético {0} en la época {1} está fuera del dominio de validez: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = no hay datos suficientes como para interpolar (tamaño de la muestra = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = no hay datos suficientes como (tamaño de la muestra = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = número de vecinos almacenados demasiado pequeño : {0} (debe ser por lo menos {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = sólo se han especificado {0} órbitas GNSS y se neces # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = para utilizar el sistema temporal {0} en los ficheros CCSDS se requiere un ICD adicional y no está disponible en Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = sistemas temporales incoherentes en los bloques de actitud : {0} ≠ {1} +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = tipo de orientación {0} desconocida -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = el modo de actitud {0} de los ficheros CCSDS AEM no está todavía implementado en Orekit +# incomplete data +CCSDS_INCOMPLETE_DATA = datos incompletos # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = secuencia de rotación {0} inválida en la línea {1} del fichero {2} @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = todavía no hay soporte para el tipo de ele # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = factor retrógrado sin soporte en el tipo de elementos {0} -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = el tipo de elementos {0} ({1}) necesita {2} elementos - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = número incorrecto de unidades para la maniobra {0} # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = falta campo temporal para la maniobra {0} +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = el tipo de orientación {0} y el tipo de velocidad {1} necesitan {2} estados, pero {3} se han dado + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = las claves incompatibles {0} y {1} no deberían utilizarse juntas + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = índice de captor {0} ya utilizado + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = falta el indice de captor {0} + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = número de elementos incoherentes: esperados {0}, dados {1} + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = para crear un propagador combinado se necesita al menos un propagador, pero no se ha especificado ninguno @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = sistema de satélites desconocido {0} # unknown time system {0} UNKNOWN_TIME_SYSTEM = sistema de tiempo desconocido {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = identificador UTC {0} desconocido + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = tipo de datos de reloj desconocido {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = frecuencia RINEX desconocida en el fichero {1}, línea # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = frecuencias incoherentes en el fichero {0}, línea {1} (se esperaba {2} y se encontró {3}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = tipo de análisis incorrecto para el fichero {0} + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = número de columnas erróneo en el fichero {0}, línea {1} (se esperaban {2} columnas y se encontraron {3}) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = cuadrícula irregular o incompleta en el fichero # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = el sistema de satélites {0} no es válido -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = el fichero IONEX {0} no contiene datos TEC para la fecha {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = el fichero IONEX {0} no contiene datos TEC para la fecha {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = el número de mapas {0} no es coherente con la especificación del encabezado {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = tipo de contenido {0} inesperado # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = no se pueden analizar los datos GNSS de {0} +# invalid GNSS data: {0} +INVALID_GNSS_DATA = datos GNSS inválido: {0} + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = error de paridad GNSS en la palabra {0} + # unknown host {0} UNKNOWN_HOST = anfitrión {0} desconocido @@ -747,29 +801,68 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = tentativa de generar el fichero {0} con un # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = {0} ha fallado buscando una solución entre {1} (g={2,number,0.0##############E0}) y {3} (g={4,number,0.0##############E0})\nÚltima iteración en {5} (g={6,number,0.0##############E0}) -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = aquí no se permite la propagación hacia atrás - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = ningún valor de excentricidad de la estación para la época dada {0}, el intervalo de validez está entre {1} y {2} +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = Faltan datos de la estación para la época {0} # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = selección de parámetros inconsistente entre los pares {0}/{1} y {2}/{3} # no unscented transform configured -NO_UNSCENTED_TRANSFORM_CONFIGURED = +NO_UNSCENTED_TRANSFORM_CONFIGURED = ninguna transformación unscented configurada # value is not strictly positive: {0} -NOT_STRICTLY_POSITIVE = +NOT_STRICTLY_POSITIVE = el valor {0} no es estrictamente positivo # transform from {0} to {1} is not implemented -UNSUPPORTED_TRANSFORM = +UNSUPPORTED_TRANSFORM = la transformación de {0} a {1} no se ha implementado # orbital parameters type: {0} is different from expected orbital type : {1} -WRONG_ORBIT_PARAMETERS_TYPE = +WRONG_ORBIT_PARAMETERS_TYPE = parámetro orbital de tipo: {0} es diferente del parámetro orbital esperado de tipo: {1} + +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = {0} necesita {1} elementos, tiene {2} # cannot change covariance type if defined in a local orbital frame -CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = +CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = no puede cambiar el tipo de covarianza si se define en el el marco de referencia de la orbita local # cannot change covariance type if defined in a non pseudo-inertial reference frame -CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = +CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = no puede cambiar el tipo de covarianza si se define en un marco de referencia no pseudo-inercial + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = el tiempo del mayor acercamiento del primer objecto de colision es diferente del segundo objecto de colision + +# first date {0} does not match second date {1} +DATES_MISMATCH = la primera fecha {0} no coincide con la segunda fecha {1} + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = la mu {0} de la primera orbita no coincide con la mu de la segunda orbita {1} + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = un estado está definido utilizando una orbita mientras que el otro estado está definido utilizando una posición-velocidad-acceleración´absolutas + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = la fecha {0} del estado no coincide la fecha {1} de su covarianza + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = crear un interpolador para el vehiculo espacial requiere al menos un in interpolador de orbita o un interpolador absoluto de posición-velocidad-acceleración + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = interpolador mal definido por este tipo de definición del vehiculo espacial (orbita or PV absolutos) + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = mala interpolación definida por este tipo de definición del vehículo espacial (orbita ou PV) + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = la cabezera del fichero {0} aún no se ha escrito + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = la cabezera del fichero {0} ya se ha escrito + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 index 875538a198..13e6244952 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = données inattendues à la ligne {0} du fichie # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = dates non chronologique dans le fichier {0} à la ligne {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = dates d''échantillonnage incohérentes : {0} était attendu, mais {1} a été fourni + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = aucune donnée d''historique UTC-TAI n''a été chargée @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = impossible d''analyser l''élément {0} de la # unable to find file {0} UNABLE_TO_FIND_FILE = impossible de trouver le fichier {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = masse de véhicule devenant négative (m : {0}) - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = débit massique positif (q : {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = aucun fichier d''éphémérides du JPL n # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = date hors du domaine de validité des éphémérides « {0} » : {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = date hors du domaine de validité des éphémérides : {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = date hors du domaine de validité : {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = date hors du domaine de validité des éphémérides : {0} est {3,number,0.0##############E0} s avant [{1}, {2}] @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = données inattendues, deux val # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = paramètre {0} inconnu, paramètres connus : {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = le paramètre {0} contient plusieurs intervals dans sa TimeSpanMap de valeurs, il faut utiliser la méthode {1} + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = la fonction setPeriod a déjà été utilisée pour le paramètre {0}, il faut créer un nouveau paramètre pour changer à nouveau les intervals d''estimation + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = le facteur d''échelle pour le paramètre {0} est trop petit : {1} @@ -335,7 +341,7 @@ CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION = le mot-clef {0} n''est pas autorisé dans CCSDS_UNEXPECTED_KEYWORD = mot-clef inattendu à la ligne {0} du fichier CCSDS {1} :\n{2} # the central body gravitational coefficient cannot be retrieved from the ODM -CCSDS_UNKNOWN_GM = le coefficient d''atraction gravifique ne peut être récupéré dans l''ODM +CCSDS_UNKNOWN_GM = le coefficient d''attraction gravifique ne peut être récupéré dans l''ODM # there is no spacecraft mass associated with this ODM file CCSDS_UNKNOWN_SPACECRAFT_MASS = il n''y a pas de masse pour le véhicule spatial dans ce fichier ODM @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = les conventions IERS n''ont pas été initialisées # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = le repère {0} est invalide dans ce contexte de fichier CCSDS +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = ce repère orbital local LVLH utilise une définition différente, veuillez utiliser le repère LVLH_CCSDS à la place + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = systèmes temporels incohérents : {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = aucun convertisseur d''unitées de pse # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = le système temporel aurait déjà dû être initialisé avant la ligne {0} du fichier {1} +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = impossible d''estimer la précession sans dérivées correctes + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = le nom "{0}" est déjà utilisé pour un état additionnel @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = calcul d''ombre incohérent : entrée = {0} et so # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = l''orbite courante a une excentricité {0} > 0.5 qui nécessite une méthode de moyennation non mise en œuvre -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = version de fichier sp3 {0} non prise en compte +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = version de fichier sp3 "{0}" non prise en compte + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = entrée d''en-tête {0} "{1}" invalide dans le fichier {2} (version {3} du format) + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = la version "{0}" du format ne prend en compte que {1} satellites au plus, {2} trouvés dans le fichier {3} # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = {0} époques trouvées dans le fichier {1} au lieu de {2} attendues -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = fin inattendue d''un fichier sp3 (après la ligne {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = impossible de joindre des fichiers sp3 ayant des métadonnées différentes + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = impossible de joindre des fichiers sp3 ayant des métadonnées différentes pour le satellite {0} + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = fin inattendue d''un fichier STK (après la ligne {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = version de fichier d''horloge {0} non prise en charge -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = version de fichier de messages de navigation {0} non prise en charge +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = version {0} du fichier {1} non prise en charge, versions prises en charge : {2} # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = fichier de modèle géomagnétique {0} absent pour l''année {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = le modèle géomagnétique {0} à l''époque {1} ne # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = la transformation temporelle du modèle géomagnétique {0} à l''époque {1} est hors du domaine de validité : {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = données insuffisnates pour réaliser une interpolation (taille de l''échantillon : {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = données insuffisantes (taille de l''échantillon : {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = nombre de voisins en cache trop petit : {0} (il faut au moins {1} voisins) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = seulement {0} orbite(s) GNSS fournie(s) alors qu''il e # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = l''utilisation du système temporel {0} nécessite un ICD additionel et n''est pas disponible dans Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = systèmes temporels incohérents dans des blocs d''attitude : {0} ≠ {1} +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = type d''attitude {0} inconnu -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = le mode d''attitude {0} des fichiers CCSDS AEM n''est pas encore implémenté dans Orekit +# incomplete data +CCSDS_INCOMPLETE_DATA = données incomplètes # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = la séquence de rotation {0} à la ligne {1} du fichier {2} est invalide @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = le type d''éléments {0} ({1}) n''est pas # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = facteur rétrograde non supporté pour le type d''éléments {0} -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = le type d''éléments {0} ({1}) nécessite {2} éléments - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = nombre d''unités incorrect pour la manœuvre {0} # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = champ temporel manquant pour la manœuvre {0} +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = le type d''attitude {0} et le type de vitesse {1} nécessitent {2} états, mais {3} ont été fournis + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = les clefs incompatibles {0} et {1} ne devraient pas être utilisées simultanément + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = index de capteur {0} déjà utilisé + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = index de capteur {0} manquant + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = nombre d''éléments incohérent, {0} attendus pour {1} fournis + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = la création d''un propagateur combiné nécessite au moins un propagateur, mais aucun n''a été fourni @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = système satellitaire {0} inconnu # unknown time system {0} UNKNOWN_TIME_SYSTEM = système de temps {0} inconnu +# unknown UTC Id {0} +UNKNOWN_UTC_ID = Identifiant UTC {0} inconnu + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = type de donnée d''horloge {0} inconnu @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = fréquence RINEX {0} inconnue à la ligne {2} du fichi # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = fréquence incohérente à la ligne {1} du fichier {0} (lu {3} alors que {2} était attendu) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = type analysé incorrect pour le fichier {0} + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = nombre de colonnes erroné à la ligne {1} du fichier {0} ({3} colonnes vues pour {2} attendues) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = grille incomplète ou irrégulière dans le fichi # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = le système satellite {0} est non valide -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = le fichier IONEX {0} ne contient pas de données de TEC pour la date {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = les fichiers IONEX {0} ne contiennent pas de données de TEC pour la date {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = le nombre de cartes {0} est incompatible avec les specifications de l''en-tête: {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = type de contenu {0} imprévu # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = les données GNSS de {0} ne peuvent pas être analysées +# invalid GNSS data: {0} +INVALID_GNSS_DATA = données GNSS invalides : {0} + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = erreur de parité GNSS sur le mot {0} + # unknown host {0} UNKNOWN_HOST = hôte {0} inconnu @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = tentative de génération du fichier {0} av # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = {0} n''a pas réussi à trouver une solution entre {1} (g={2,number,0.0##############E0}) et {3} (g={4,number,0.0##############E0})\nLa dernière itération était à {5} (g={6,number,0.0##############E0}) -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = la propagation à rebours n''a pas été autorisée ici - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = pas d''excentricité station pour la date {0}, l''intervalle de validité est entre {1} et {2} +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = donnée station manquante pour la date {0} # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = sélection de paramètres incohérents entre les paires {0}/{1} et {2}/{3} @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = la transformation de {0} vers {1} n'est pas implémenté # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = Le type de paramètres orbitaux: {0} est différent du type de paramètres orbitaux attendus: {1} +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = {0} nécessite {1} éléments, mais {2} ont été fournis + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = impossible de changer le type de la covariance si celle-ci est définie dans un repère orbital local # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = impossible de changer le type de la covariance si celle-ci est définie dans un référentiel non pseudo-inertiel + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = la date de plus proche conjunction du primaire et différente de celle du secondaire + +# first date {0} does not match second date {1} +DATES_MISMATCH = La première date {0} ne correspond pas à la seconde date {1} + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = Le mu {0} de la première orbite ne correspond pas au mu {1} de la seconde orbite + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = un état est défini en utilisant une orbite tandis que l'autre état est défini en utilisant une position-vitesse-accelération absolue + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = la date {0} de l état est différente de la date {1} de sa covariance + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = créer un interpolateur de véhicule spatial nécessite au moins un interpolateur d'orbite ou un interpolateur de position-vitesse-accelération absolue + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = mauvais interpolateur défini pour ce type de définition du véhicule spatial (orbite ou PV) + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = plusieurs interpolateurs sont utilisés donc le nombre de points d'interpolation défini pour chacun pourrait différer + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = l''en-tête du fichier {0} n''a pas encore été écrit + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = l''en-tête du fichier {0} a déjà été écrit + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = Impossible de lancer une propagation à partir d'une date infiniment lointaine + +# invalid satellite id {0} +INVALID_SATELLITE_ID = identifiant satellite {0} invalide + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = le degré d''interpolation pour les EOP devrait être de la forme 4k-1, mais {0} a été fourni diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 index 565a501e2f..d9922665fc 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = datas non cronolóxicas no ficheiro {0} na liña {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = os datos históricos IERS UTC-AIT non estan cargados @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = # unable to find file {0} UNABLE_TO_FIND_FILE = imposible de atopa-lo ficheiro {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = masa do vehículo facéndose negativa (m : {0}) - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = fluxo de masa positiva (q : {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = non existe ningún ficheiro binario de e # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = data fora do rango de validez das efemérides « {0} » : {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = data fora do rango de validez das efemérides : {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = data fora do rango de validez : {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = datos inesperados, dous valore # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = nome de parámetro non coñecido {0}, parámetro coñecido {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = non se inicializou ningunha convención IERS antes d # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = o sistema de referencia {0} non é válido neste contexto de ficheiro CCSDS +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = sistemas temporais inconsistentes: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = o nome {0} xa está empregado por un estado adicional @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = cálculo de sombra incoherente : entrada = {0} e # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = a órbita corrente ten unha excentricidade ({0} > 0.5) que necesita un método de medianización non implementada -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = versión de formato sp3 {0} non incluída +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = versión de formato sp3 "{0}" non incluída + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = final inesperado dun ficheiro sp3 (despois da liña {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = final inesperado dun ficheiro STK (despois da liña {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = versión de ficheiro de reloxo {0} non incluída -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = ficheiro de modelo xeomagnético {0} ausente do ano {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = o modelo xeomagnético {0} na época {1} non ten en # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = a transformación temporal do modelo xeomagnético {0} na época {1} está fora do dominio de validez: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = número de vecinos almacenados demasiado pequeno: {0} (debe ser polo menos {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = # unknown time system {0} UNKNOWN_TIME_SYSTEM = sistema de tempo descoñecido {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = tipo de datos de reloxo descoñecido {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 index 638327220e..0041216a32 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = il file {1} contiene un dato inatteso alla lin # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = date non cronologiche nel file {0} alla linea {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = nessun dato storico IERS UTC-TAI caricato @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = impossibile analizzare l'' elemento {0} alla l # unable to find file {0} UNABLE_TO_FIND_FILE = impossibile trovare il file {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = la massa del veicolo sta diventando negativa: {0} kg - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = portata del flusso positiva (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = nessun file binario di effemeridi del JP # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = data fuori dell''intervallo di validità per le effemeridi {0}: {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = data fuori dell''intervallo di validità per le effemeridi: {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = data fuori dell''intervallo di validità: {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = data fuori dall''intervallo di validità delle effemeridi: {0} è {3,number,0.0##############E0} s prima del [{1}, {2}] @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = due valori di elevazione inatt # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = nome del parametro {0} sconosciuto, parametri conosciuti: {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = il fattore di scala per il parametro {0} è troppo piccolo: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = le convenzioni IERS non sono state inizializzate pri # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = il sistema di riferimento {0} non è valido in questo contesto del file CCSDS +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = sistemi temporali incoerenti: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = non è stato configurato un convertito # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = Nel file {1}, il sistema temporale deve essere impostato prima della linea {0} +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = il nome "{0}" è già utilizzato per uno stato aggiuntivo @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = calcolo dell''ombra incoerente: ingresso = {0} me # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = l''orbita attuale ha un''eccentricità {0} > 0.5; il DSST ha bisogno di un metodo di calcolo della media dipendente dal tempo che non è implementato. -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = versione del file sp3 {0} non supportata +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = versione del file sp3 "{0}" non supportata + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = {0} epoche trovate nel file {1} invece di {2} attese -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = fine inattesa di un file sp3 (dopo la linea {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = fine inattesa di un file STK (dopo la linea {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = versione del file orologio {0} non supportata -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = la versione {0} del file contentente i messaggi di navigazione non é supportata +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = modello geomagnetico {0} inesistente per l''anno {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = il modello geomagnetico {0} all''epoca {1} non supp # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = la trasformazione temporale del modello geomagnetico {0} all''epoca {1} è al di fuori del suo intervallo di validità: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = dati insufficienti per realizzare un''interpolazione (passo di campionamento = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = dati insufficienti (passo di campionamento = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = numero di vicini in cache troppo piccolo: {0} (devono essere almeno {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = solo {0} orbite GNSS fornite, mentre ne servono {1} pe # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = l''utilizzo del sistema temporale {0} nei file CCSDS richiede un ICD aggiuntivo e non è implementato in Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = sistema temporale inconsistente nei blocchi di assetto: {0} ≠ {1} +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = il tipo di assetto {0} nei file CCSDS AEM non è ancora implementato in Orekit +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = la sequenza di rotazione {0} presente alla linea {1} del file {2} è inconsistente @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = il tipo di settaggio {0} ({1}) non è ancor # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = fattore retrogrado non supportato per il tipo di elementi {0} -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = il tipo di settaggio {0} ({1}) presuppone {2} elementi - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = errato numero di unità per la manovra {0} # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = campo temporale manacante per la manovra {0} +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = la creazione di un propagatore combinato richiede almeno un propagatore, ma non ne è stato fornito alcuno @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = sistema satellitare {0} sconosciuto # unknown time system {0} UNKNOWN_TIME_SYSTEM = sistema orario sconosciuto {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = tipo di dati dell''orologio sconosciuto {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = frequenza RINEX {0} sconociuta alla linea {2} del file # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = frequenza incoerente alla linea {1} del file {0} (letta {3}, attesa {2}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = numero di colonne errato alla linea {1} del file {0} ({3} colonne invece di {2} attese) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = griglia irregolare o incompleta nel file {0} # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = sistema satellitare invalido {0} -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = il file IONEX {0} non contiene dati TEC per la data {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = il file IONEX {0} non contiene dati TEC per la data {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = il numero di mappe {0} è incoerente con le specifiche dell''intestazione: {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = tipo di contenuto {0} inatteso # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = impossibile ricavare dati GNSS da {0} +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = host {0} sconosciuto @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = tentativo di generare il file {0} con un er # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = {0} non ha trovato una soluzione tra {1} (g={2,number,0.0##############E0}) e {3} (g={4,number,0.0##############E0})\nUltima iterazione a {5} (g={6,number,0.0##############E0}) -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = la propagazione all'indietro non è consentita qui - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = nessun valore di eccentricità stazione per la data inserita {0}, intervallo di validità tra {1} e {2} +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 index 225bd30fe5..f5b7b6164c 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = ikkekronologiske datoer i filen {0}, linje {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = ingen IERS UTC-TAI historikk data er lastet @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = # unable to find file {0} UNABLE_TO_FIND_FILE = umulig å finne filen {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = romfartøyets masse blir negativ (m: {0}) - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = positiv strøm (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = ingen binære JPL efemeridefil funnet # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = dato utenfor gyldighetsperioden til {0} efemeridene: {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = dato utenfor gyldighetsperioden til efemeridene: {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = dato utenfor gyldighetsperioden: {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = uventete to høydeverdier: {0} # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = ustøttet parameter navn {0}, støttete navn: {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = skala-faktoren for parameteren {0} er for liten: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = ingen IERS-konvensjoner er blitt satt før lesingen # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = ramme {0} er ikke gyldig i denne CCSDS filens sammenheng +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = ukonsekvente tidssystemer: {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = navnet "{0}" er allerede brukt for en annen tilstand @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = ukonsekvent skyggeberegning: inngang = {0} og utg # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = Den aktuelle banen har en eksentrisitet ({0} > 0.5). DSST trenger en implementert tid avhengig numerisk metode til å beregne de gjennomsnittlige hastighetene -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = ustøttet version for sp3 filen {0} +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = ustøttet version for sp3 filen "{0}" + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = fant {0} epoker i filen {1}, ventet {2} -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = uventet slutt på fil for sp3 fil (etter linje {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = uventet slutt på fil for STK fil (etter linje {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = ikke-støttet klokkefilversjon {0} -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = ikke-eksisterende geomagnetisk model {0} for år {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = geomagnetisk model {0} med epoke {1} støtter ikke # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = tidstransformasjonen av den geomagnetiske modelen {0} med epoke {1} er utenfor sitt gyldighetsområde: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = ikke nok data for interpolasjon (intervall størrelse = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = ikke nok data (intervall størrelse = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = for få bufrete naboer: {0} (må i allefall være {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = Bare {0} GNSS baner har blitt presistert mens {1} bane # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = Bruk av tidssystemet {0} i CCSDS-filer krever en ytterligere ICD og er ikke støttet av Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = En aggregat-propagatør krever minst en bestanddel propagatør, men ingen var spesifisert. @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = ukjent satellittsystem {0} # unknown time system {0} UNKNOWN_TIME_SYSTEM = ukjent tidssystem {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = ukjent datatype for klokke {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = ukjent RINEX-frekvens {0} i filen {1}, linje {2} # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = upassende frekvenser i filen {0}, linje {1} (forventet {2}, fant {3}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = galt kolonnenummer i filen {0}, linje {1} (forventet {2} kolonner, men fant {3} kolonner) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = irregulært eller ufullstendig nett i filen {0} # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = ugyldig satellittsystem {0} -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = IONEX-filen {0} inneholder ikke TEC-data for datoen {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = IONEX-filen {0} inneholder ikke TEC-data for datoen {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = antall kart {0} er inkonsistent i forhold til startspesifiseringen: {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 index 4ecb496483..f0bfc97bf9 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 @@ -91,6 +91,9 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = date neașteptate la linia {0} din fișierul { # non-chronological dates in file {0}, line {1} NON_CHRONOLOGICAL_DATES_IN_FILE = date necronologice în fișierul {0}, linia {1} +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = + # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = datele istorice UTC-TAI nu sunt încărcate @@ -115,9 +118,6 @@ UNABLE_TO_PARSE_ELEMENT_IN_FILE = imposibil de analizat elementul {0} de la lini # unable to find file {0} UNABLE_TO_FIND_FILE = imposibil de găsit fișierul {0} -# spacecraft mass becomes negative: {0} kg -SPACECRAFT_MASS_BECOMES_NEGATIVE = masa navei spațiale devine negativă: {0} kg - # positive flow rate (q: {0}) POSITIVE_FLOW_RATE = rata fluxului pozitivă (q: {0}) @@ -268,8 +268,8 @@ NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = niciun fișier de efemeride JPL găsit # out of range date for {0} ephemerides: {1} OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = dată în afara domeniului de valabilitate al efemeridelor « {0} » : {1} -# out of range date for ephemerides: {0}, [{1}, {2}] -OUT_OF_RANGE_EPHEMERIDES_DATE = dată în afara domeniului de valabilitate al efemeridelor : {0}, [{1}, {2}] +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = dată în afara domeniului de valabilitate : {0}, [{1}, {2}] # out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = dată în afara domeniului de valabilitate al efemeridelor : {0} este cu {3,number,0.0##############E0} s înainte de [{1}, {2}] @@ -283,6 +283,12 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = date neașteptate, două valor # unsupported parameter name {0}, supported names: {1} UNSUPPORTED_PARAMETER_NAME = parametrul {0} necunoscut, parametrii cunoscuți: {1} +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = + # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = factorul de scalare pentru parametrul {0} este prea mic: {1} @@ -346,6 +352,9 @@ CCSDS_UNKNOWN_CONVENTIONS = convențiile IERS nu au fost inițializate înainte # frame {0} is not valid in this CCSDS file context CCSDS_INVALID_FRAME = sistemul de referință {0} nu este valid în contextul acestui fișier CCSDS +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = + # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = sisteme de timp incoerente : {0} ≠ {1} @@ -358,6 +367,9 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = niciun convertor de unitate pseudo-dis # Time system should have already been set before line {0} of file {1} CCSDS_TIME_SYSTEM_NOT_READ_YET = Sistemul de timp ar fi trebuit deja configurat înaintea liniei {0} din fișierul {1} +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = + # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = numele "{0}" este deja folosit pentru o stare adițională @@ -376,20 +388,38 @@ DSST_SPR_SHADOW_INCONSISTENT = inconsistență în calculul umbrei: intrare = {0 # The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = Orbita curentă are o eccentricitate {0} > 0.5 care necesită pentru calculul ratelor medii o metodă numerică cu dependență de timp neimplementată încă -# unsupported sp3 file version {0} -SP3_UNSUPPORTED_VERSION = versiune neacceptată a fișierului sp3 {0} +# unsupported sp3 file version "{0}" +SP3_UNSUPPORTED_VERSION = versiune neacceptată a fișierului sp3 "{0}" + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = au fost găsite {0} date de referință în fișierul {1}, așteptate {2} -# unexpected end of file in sp3 file (after line {0}) -SP3_UNEXPECTED_END_OF_FILE = sfârșit neașteptat al fișierului sp3 (după linia {0}) +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = sfârșit neașteptat al fișierului STK (după linia {0}) # unsupported clock file version {0} CLOCK_FILE_UNSUPPORTED_VERSION = versiune neacceptată a fișierului de ceas {0} -# unsupported navigation messages file version {0} -NAVIGATION_FILE_UNSUPPORTED_VERSION = versiune neacceptată a fișierului cu mesaje de navigație {0} +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = fișierul de model geomagnetic {0} inexistent pentru anul {1} @@ -400,8 +430,8 @@ UNSUPPORTED_TIME_TRANSFORM = modelul geomagnetic {0} cu moment de timp de referi # time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] OUT_OF_RANGE_TIME_TRANSFORM = tranformarea temporală a modelului geomagnetic {0} cu moment de timp de referință {1} este în afara intervalului de validitate: {2} != [{3}, {4}] -# not enough data for interpolation (sample size = {0}) -NOT_ENOUGH_DATA_FOR_INTERPOLATION = date insuficiente pentru interpolare (dimensiunea eșantionului = {0}) +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = date insuficiente (dimensiunea eșantionului = {0}) # too small number of cached neighbors: {0} (must be at least {1}) NOT_ENOUGH_CACHED_NEIGHBORS = numărul de vecini din cache este prea mic: {0} (trebuie cel puțin {1}) @@ -469,11 +499,11 @@ NOT_ENOUGH_GNSS_FOR_DOP = numai {0} orbite GNSS sunt oferite în timp ce {1} sun # use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = utilizarea sistemului de timp {0} în fișierele CCSDS necesită un ICD adițional și nu este disponibil în Orekit -# inconsistent time systems in the attitude blocks: {0} ≠ {1} -CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS = sisteme de timp inconsistente în blocurile de atitudine: {0} ≠ {1} +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = -# attitude type {0} in CCSDS AEM files is not implemented in Orekit -CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED = tipul de atitudine {0} din fișierele CCSDS AEM nu este implementat în Orekit +# incomplete data +CCSDS_INCOMPLETE_DATA = # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = secvență de rotație {0} invalidă la linia {1} din fișierul {2} @@ -484,15 +514,27 @@ CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = tipul de set de elemente {0} ({1}) nu este # retrograde factor not supported in element set {0} CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = factorul retrograd nu este permis în setul de elemente {0} -# element set type {0} ({1}) expects {2} elements -CCSDS_ELEMENT_SET_WRONG_NB_COMPONENTS = tipul de set de elemente {0} ({1}) așteaptă {2} elemente - # wrong number of units for maneuver {0} CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = număr greșit al unităților pentru manevra {0} # missing time field for maneuver {0} CCSDS_MANEUVER_MISSING_TIME = lipsește câmpul de timp pentru manevra {0} +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = + # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Crearea unui propagator combinator necesită cel puțin un propagator, dar nici unul nu a fost definit. @@ -526,6 +568,9 @@ UNKNOWN_SATELLITE_SYSTEM = sistemul de sateliți {0} este necunoscut # unknown time system {0} UNKNOWN_TIME_SYSTEM = sistem de timp necunoscut {0} +# unknown UTC Id {0} +UNKNOWN_UTC_ID = + # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = tip de date de ceas necunoscut {0} @@ -544,6 +589,9 @@ UNKNOWN_RINEX_FREQUENCY = frecventa RINEX {0} din fișierul {1}, linia {2} este # mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) MISMATCHED_FREQUENCIES = frecvențe nepotrivite în fișierul {0}, linia {1} (așteptate {2}, găsite {3}) +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = + # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = număr incorect de coloane în fișierul {0}, linia {1} (așteptate {2} coloane, găsite {3} coloane) @@ -589,8 +637,8 @@ IRREGULAR_OR_INCOMPLETE_GRID = grilă neregulată sau incompletă în fișierul # invalid satellite system {0} INVALID_SATELLITE_SYSTEM = sistemul satelit {0} este nevalid -# IONEX file {0} does not contain TEC data for date {1} -NO_TEC_DATA_IN_FILE_FOR_DATE = fișierul IONEX {0} nu conține informații TEC pentru data {1} +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = fișierul IONEX {0} nu conține informații TEC pentru data {1} # number of maps {0} is inconsistent with header specification: {1} INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = numărul de hărți {0} este incompatibil cu specificațiile din antet: {1} @@ -711,6 +759,12 @@ UNEXPECTED_CONTENT_TYPE = tip de conținut {0} neașteptat # cannot parse GNSS data from {0} CANNOT_PARSE_GNSS_DATA = imposibil de analizat datele GNSS de la {0} +# invalid GNSS data: {0} +INVALID_GNSS_DATA = + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = + # unknown host {0} UNKNOWN_HOST = mașina gazdă {0} este necunoscută @@ -747,11 +801,8 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = încercare de generare a fișierului {0} ce # {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) FIND_ROOT = {0} nu a găsit rădăcina între {1} (g={2,number,0.0##############E0}) și {3} (g={4,number,0.0##############E0})\nUltima iterație la {5} (g={6,number,0.0##############E0}) -# backward propagation not allowed here -BACKWARD_PROPAGATION_NOT_ALLOWED = - -# no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2} -NO_STATION_ECCENTRICITY_FOR_EPOCH = +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} INCONSISTENT_SELECTION = @@ -768,8 +819,50 @@ UNSUPPORTED_TRANSFORM = # orbital parameters type: {0} is different from expected orbital type : {1} WRONG_ORBIT_PARAMETERS_TYPE = +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = + # cannot change covariance type if defined in a local orbital frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = # cannot change covariance type if defined in a non pseudo-inertial reference frame CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = + +# first date {0} does not match second date {1} +DATES_MISMATCH = + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = diff --git a/src/site/markdown/architecture/attitudes.md b/src/site/markdown/architecture/attitudes.md index ed71268f04..938c0d4f56 100644 --- a/src/site/markdown/architecture/attitudes.md +++ b/src/site/markdown/architecture/attitudes.md @@ -1,4 +1,4 @@ - + + + +EOP 20 C04 +2023-09-27 +2022-now + + + + +Year +Month +Day +MJD + + + +X +sigma_X +Y +sigma_Y +x_rate +sigma_x_rate +y_rate +sigma_y_rate +UT1-UTC +sigma_UT1-UTC +LOD +sigma_LOD +dX +sigma_dX +dY +sigma_dY + + + + +arcsec +arcsec +arcsec +arcsec +arcsec +arcsec +arcsec +arcsec +sec +sec +sec +sec +arcsec +arcsec +arcsec +arcsec + + + + + + + + +0.054651 +0.000076 +0.276982 +0.000055 +-0.001480 +0.000084 +0.000936 +0.000094 + + +-0.1105073 +0.0000134 +-0.0000314 +0.0000313 + + +0.000066 +0.000053 +-0.000221 +0.000056 + + + + + + + +0.053421 +0.000078 +0.277921 +0.000054 +-0.000845 +0.000088 +0.000992 +0.000093 + + +-0.1103808 +0.0000129 +-0.0002175 +0.0000330 + + +0.000067 +0.000051 +-0.000208 +0.000054 + + + + + + + +0.052959 +0.000077 +0.279062 +0.000055 +0.000061 +0.000091 +0.001375 +0.000091 + + +-0.1101259 +0.0000113 +-0.0002629 +0.0000329 + + +0.000082 +0.000048 +-0.000200 +0.000052 + + + + + + + +0.053255 +0.000075 +0.280692 +0.000056 +0.000345 +0.000092 +0.002005 +0.000087 + + +-0.1099060 +0.0000099 +-0.0001505 +0.0000320 + + +0.000106 +0.000047 +-0.000194 +0.000051 + + + + + + + +0.053444 +0.000073 +0.282876 +0.000057 +-0.000194 +0.000094 +0.002204 +0.000083 + + +-0.1098521 +0.0000100 +0.0000528 +0.0000322 + + +0.000132 +0.000047 +-0.000189 +0.000051 + + + + + + + +0.052911 +0.000072 +0.285027 +0.000057 +-0.000859 +0.000096 +0.002011 +0.000080 + + +-0.1100113 +0.0000111 +0.0002599 +0.0000324 + + +0.000160 +0.000047 +-0.000183 +0.000051 + + + + + + + +0.051783 +0.000070 +0.286925 +0.000056 +-0.001402 +0.000094 +0.001814 +0.000079 + + +-0.1103513 +0.0000127 +0.0004075 +0.0000320 + + +0.000188 +0.000048 +-0.000173 +0.000051 + + + + + + + +0.050229 +0.000068 +0.288649 +0.000055 +-0.001618 +0.000090 +0.001638 +0.000078 + + +-0.1107928 +0.0000156 +0.0004538 +0.0000322 + + +0.000236 +0.000048 +-0.000135 +0.000051 + + + + + + + +0.048613 +0.000067 +0.290181 +0.000054 +-0.001514 +0.000087 +0.001394 +0.000078 + + +-0.1112319 +0.0000170 +0.0004076 +0.0000320 + + +0.000282 +0.000048 +-0.000087 +0.000052 + + + + + + + +0.047137 +0.000067 +0.291459 +0.000052 +-0.001546 +0.000084 +0.001183 +0.000077 + + +-0.1115940 +0.0000136 +0.0003191 +0.0000299 + + +0.000300 +0.000049 +-0.000053 +0.000052 + + + + + + + +0.045606 +0.000066 +0.292549 +0.000052 +-0.001480 +0.000083 +0.000960 +0.000077 + + +-0.1118477 +0.0000104 +0.0001747 +0.0000265 + + +0.000275 +0.000050 +-0.000046 +0.000053 + + + + + + + +0.044270 +0.000066 +0.293457 +0.000052 +-0.001124 +0.000082 +0.000863 +0.000077 + + +-0.1119359 +0.0000087 +-0.0000014 +0.0000238 + + +0.000085 +0.000052 +-0.000143 +0.000055 + + + + + + + +0.043407 +0.000066 +0.294418 +0.000052 +-0.000467 +0.000081 +0.001193 +0.000079 + + +-0.1118463 +0.0000080 +-0.0001679 +0.0000223 + + +-0.000029 +0.000055 +-0.000206 +0.000057 + + + + + + + +0.043166 +0.000065 +0.295863 +0.000052 +-0.000083 +0.000082 +0.001807 +0.000080 + + +-0.1115942 +0.0000083 +-0.0003399 +0.0000218 + + +0.000117 +0.000050 +-0.000133 +0.000052 + + + + + + + +0.043044 +0.000066 +0.297850 +0.000052 +-0.000365 +0.000082 +0.002040 +0.000082 + + +-0.1111728 +0.0000098 +-0.0005049 +0.0000225 + + +0.000248 +0.000048 +-0.000060 +0.000051 + + + + + + + +0.042450 +0.000066 +0.299831 +0.000054 +-0.000802 +0.000082 +0.001787 +0.000084 + + +-0.1106106 +0.0000110 +-0.0005977 +0.0000236 + + +0.000257 +0.000050 +-0.000043 +0.000053 + + + + + + + +0.041424 +0.000064 +0.301470 +0.000055 +-0.001366 +0.000080 +0.001564 +0.000088 + + +-0.1099935 +0.0000102 +-0.0006299 +0.0000244 + + +0.000211 +0.000052 +-0.000061 +0.000055 + + + + + + + +0.039917 +0.000062 +0.302907 +0.000055 +-0.001547 +0.000078 +0.001194 +0.000094 + + +-0.1093731 +0.0000084 +-0.0006103 +0.0000239 + + +0.000151 +0.000053 +-0.000103 +0.000058 + + + + + + + +0.038526 +0.000059 +0.303982 +0.000055 +-0.000974 +0.000078 +0.001073 +0.000096 + + +-0.1088143 +0.0000068 +-0.0004822 +0.0000226 + + +0.000119 +0.000054 +-0.000160 +0.000059 + + + + + + + +0.037836 +0.000059 +0.305066 +0.000054 +-0.000465 +0.000078 +0.001124 +0.000094 + + +-0.1084331 +0.0000062 +-0.0002610 +0.0000220 + + +0.000170 +0.000058 +-0.000226 +0.000063 + + + + + + + +0.037447 +0.000060 +0.306204 +0.000053 +-0.000405 +0.000079 +0.001087 +0.000091 + + +-0.1082893 +0.0000068 +-0.0000229 +0.0000225 + + +0.000200 +0.000067 +-0.000218 +0.000073 + + + + + + + +0.036918 +0.000064 +0.307320 +0.000053 +-0.000755 +0.000082 +0.001242 +0.000089 + + +-0.1083574 +0.0000087 +0.0001250 +0.0000245 + + +0.000206 +0.000096 +-0.000249 +0.000108 + + + + + + + +0.035925 +0.000066 +0.308652 +0.000053 +-0.001292 +0.000083 +0.001411 +0.000088 + + +-0.1085297 +0.0000112 +0.0002054 +0.0000272 + + +0.000195 +0.000153 +-0.000324 +0.000174 + + + + + + + +0.034433 +0.000065 +0.310094 +0.000054 +-0.001672 +0.000084 +0.001440 +0.000089 + + +-0.1087616 +0.0000133 +0.0002618 +0.0000289 + + +0.000164 +0.000219 +-0.000335 +0.000251 + + + + + + + +0.032744 +0.000065 +0.311503 +0.000055 +-0.001541 +0.000083 +0.001323 +0.000090 + + +-0.1090219 +0.0000154 +0.0002418 +0.0000291 + + +0.000113 +0.000267 +-0.000188 +0.000308 + + + + + + + +0.031347 +0.000064 +0.312796 +0.000055 +-0.001216 +0.000082 +0.001301 +0.000091 + + +-0.1092136 +0.0000187 +0.0001214 +0.0000289 + + +0.000034 +0.000385 +0.000262 +0.000446 + + + + + + + +0.030234 +0.000064 +0.314139 +0.000054 +-0.001118 +0.000081 +0.001437 +0.000092 + + +-0.1092537 +0.0000175 +-0.0000281 +0.0000281 + + +0.000046 +0.000344 +0.000291 +0.000398 + + + + + + + +0.029170 +0.000062 +0.315635 +0.000051 +-0.000920 +0.000082 +0.001485 +0.000092 + + +-0.1091351 +0.0000138 +-0.0002147 +0.0000274 + + +0.000141 +0.000227 +-0.000099 +0.000262 + + + + + + + +0.028335 +0.000060 +0.317179 +0.000049 +-0.000787 +0.000084 +0.001690 +0.000094 + + +-0.1088260 +0.0000119 +-0.0003994 +0.0000280 + + +0.000181 +0.000181 +-0.000309 +0.000207 + + + + + + + +0.027549 +0.000060 +0.318983 +0.000048 +-0.000904 +0.000087 +0.001888 +0.000097 + + +-0.1083629 +0.0000106 +-0.0005163 +0.0000288 + + +0.000174 +0.000144 +-0.000338 +0.000164 + + + + + + + +0.026669 +0.000059 +0.320951 +0.000048 +-0.000704 +0.000089 +0.002028 +0.000098 + + +-0.1078401 +0.0000083 +-0.0005151 +0.0000286 + + +0.000158 +0.000102 +-0.000303 +0.000115 + + + + + + + +0.026121 +0.000058 +0.323071 +0.000047 +-0.000383 +0.000088 +0.002299 +0.000098 + + +-0.1073883 +0.0000062 +-0.0003559 +0.0000278 + + +0.000151 +0.000072 +-0.000234 +0.000079 + + + + + + + +0.025846 +0.000057 +0.325440 +0.000047 +-0.000259 +0.000087 +0.002328 +0.000099 + + +-0.1071441 +0.0000052 +-0.0001267 +0.0000274 + + +0.000220 +0.000063 +-0.000153 +0.000068 + + + + + + + +0.025668 +0.000057 +0.327732 +0.000047 +0.000041 +0.000085 +0.002263 +0.000099 + + +-0.1071182 +0.0000048 +0.0000529 +0.0000277 + + +0.000260 +0.000057 +-0.000108 +0.000061 + + + + + + + +0.025787 +0.000058 +0.329961 +0.000047 +0.000048 +0.000083 +0.002231 +0.000097 + + +-0.1072325 +0.0000049 +0.0001668 +0.0000294 + + +0.000197 +0.000051 +-0.000149 +0.000055 + + + + + + + +0.025780 +0.000059 +0.332108 +0.000048 +-0.000072 +0.000081 +0.001936 +0.000095 + + +-0.1074224 +0.0000060 +0.0002035 +0.0000330 + + +0.000139 +0.000049 +-0.000167 +0.000053 + + + + + + + +0.025695 +0.000061 +0.333914 +0.000050 +0.000058 +0.000079 +0.001748 +0.000092 + + +-0.1076025 +0.0000078 +0.0001376 +0.0000350 + + +0.000129 +0.000047 +-0.000126 +0.000051 + + + + + + + +0.025687 +0.000061 +0.335620 +0.000052 +-0.000294 +0.000078 +0.001740 +0.000089 + + +-0.1076739 +0.0000089 +-0.0000047 +0.0000341 + + +0.000145 +0.000045 +-0.000062 +0.000048 + + + + + + + +0.025131 +0.000061 +0.337270 +0.000053 +-0.000840 +0.000078 +0.001399 +0.000085 + + +-0.1075794 +0.0000088 +-0.0001873 +0.0000319 + + +0.000174 +0.000043 +0.000002 +0.000046 + + + + + + + +0.024101 +0.000060 +0.338495 +0.000053 +-0.001102 +0.000079 +0.001081 +0.000082 + + +-0.1072911 +0.0000080 +-0.0003925 +0.0000298 + + +0.000180 +0.000042 +-0.000006 +0.000044 + + + + + + + +0.022877 +0.000060 +0.339526 +0.000051 +-0.001423 +0.000080 +0.001064 +0.000079 + + +-0.1067966 +0.0000070 +-0.0005945 +0.0000282 + + +0.000181 +0.000039 +-0.000042 +0.000041 + + + + + + + +0.021310 +0.000061 +0.340644 +0.000050 +-0.001731 +0.000081 +0.001232 +0.000078 + + +-0.1061135 +0.0000064 +-0.0007723 +0.0000268 + + +0.000184 +0.000037 +-0.000084 +0.000039 + + + + + + + +0.019564 +0.000064 +0.341916 +0.000050 +-0.001677 +0.000081 +0.001224 +0.000077 + + +-0.1052855 +0.0000071 +-0.0008653 +0.0000260 + + +0.000196 +0.000038 +-0.000109 +0.000040 + + + + + + + +0.018085 +0.000067 +0.343121 +0.000050 +-0.001101 +0.000081 +0.001166 +0.000080 + + +-0.1044205 +0.0000083 +-0.0008345 +0.0000259 + + +0.000211 +0.000040 +-0.000123 +0.000042 + + + + + + + +0.017263 +0.000068 +0.344344 +0.000050 +-0.000594 +0.000079 +0.001382 +0.000082 + + +-0.1036358 +0.0000074 +-0.0007222 +0.0000258 + + +0.000225 +0.000042 +-0.000134 +0.000045 + + + + + + + +0.016802 +0.000065 +0.345874 +0.000050 +-0.000438 +0.000077 +0.001623 +0.000083 + + +-0.1029889 +0.0000059 +-0.0005724 +0.0000254 + + +0.000235 +0.000044 +-0.000144 +0.000046 + + + + + + + +0.016413 +0.000063 +0.347675 +0.000050 +-0.000286 +0.000075 +0.002127 +0.000083 + + +-0.1025126 +0.0000054 +-0.0003737 +0.0000249 + + +0.000235 +0.000045 +-0.000163 +0.000047 + + + + + + + +0.016176 +0.000061 +0.350002 +0.000051 +-0.000294 +0.000074 +0.002432 +0.000082 + + +-0.1022588 +0.0000055 +-0.0001252 +0.0000243 + + +0.000228 +0.000045 +-0.000173 +0.000047 + + + + + + + +0.015932 +0.000062 +0.352479 +0.000052 +-0.000052 +0.000073 +0.002467 +0.000081 + + +-0.1022607 +0.0000063 +0.0001316 +0.0000242 + + +0.000216 +0.000043 +-0.000164 +0.000045 + + + + + + + +0.015997 +0.000064 +0.354926 +0.000051 +0.000129 +0.000073 +0.002452 +0.000081 + + +-0.1024919 +0.0000083 +0.0003072 +0.0000254 + + +0.000200 +0.000042 +-0.000139 +0.000044 + + + + + + + +0.016148 +0.000067 +0.357311 +0.000050 +0.000074 +0.000074 +0.002246 +0.000081 + + +-0.1028423 +0.0000102 +0.0003647 +0.0000267 + + +0.000182 +0.000042 +-0.000106 +0.000044 + + + + + + + +0.016261 +0.000069 +0.359414 +0.000049 +0.000243 +0.000074 +0.001946 +0.000080 + + +-0.1032020 +0.0000094 +0.0003516 +0.0000267 + + +0.000165 +0.000042 +-0.000074 +0.000044 + + + + + + + +0.016685 +0.000068 +0.361221 +0.000048 +0.000737 +0.000074 +0.001652 +0.000080 + + +-0.1035137 +0.0000075 +0.0002601 +0.0000259 + + +0.000152 +0.000043 +-0.000052 +0.000045 + + + + + + + +0.017573 +0.000065 +0.362787 +0.000048 +0.000925 +0.000074 +0.001559 +0.000080 + + +-0.1036950 +0.0000065 +0.0000946 +0.0000255 + + +0.000152 +0.000041 +-0.000072 +0.000043 + + + + + + + +0.018439 +0.000062 +0.364320 +0.000049 +0.000644 +0.000076 +0.001468 +0.000081 + + +-0.1036881 +0.0000059 +-0.0001103 +0.0000258 + + +0.000158 +0.000039 +-0.000108 +0.000040 + + + + + + + +0.018990 +0.000061 +0.365760 +0.000049 +0.000604 +0.000079 +0.001426 +0.000081 + + +-0.1034784 +0.0000057 +-0.0003054 +0.0000267 + + +0.000165 +0.000036 +-0.000143 +0.000038 + + + + + + + +0.019616 +0.000061 +0.367219 +0.000049 +0.000643 +0.000081 +0.001545 +0.000081 + + +-0.1031068 +0.0000065 +-0.0004204 +0.0000286 + + +0.000164 +0.000037 +-0.000148 +0.000038 + + + + + + + +0.020219 +0.000061 +0.368844 +0.000049 +0.000492 +0.000082 +0.001735 +0.000081 + + +-0.1026793 +0.0000079 +-0.0004135 +0.0000299 + + +0.000158 +0.000039 +-0.000134 +0.000040 + + + + + + + +0.020627 +0.000062 +0.370615 +0.000050 +0.000231 +0.000082 +0.001708 +0.000080 + + +-0.1023211 +0.0000087 +-0.0002849 +0.0000294 + + +0.000152 +0.000041 +-0.000112 +0.000042 + + + + + + + +0.020922 +0.000062 +0.372306 +0.000050 +0.000602 +0.000080 +0.001658 +0.000079 + + +-0.1021369 +0.0000087 +-0.0000768 +0.0000285 + + +0.000148 +0.000042 +-0.000088 +0.000044 + + + + + + + +0.021825 +0.000061 +0.374058 +0.000049 +0.001270 +0.000078 +0.002044 +0.000078 + + +-0.1021761 +0.0000082 +0.0001557 +0.0000287 + + +0.000152 +0.000041 +-0.000072 +0.000043 + + + + + + + +0.023321 +0.000060 +0.376250 +0.000048 +0.001602 +0.000076 +0.002220 +0.000078 + + +-0.1024315 +0.0000075 +0.0003453 +0.0000289 + + +0.000160 +0.000039 +-0.000060 +0.000041 + + + + + + + +0.024985 +0.000059 +0.378452 +0.000047 +0.001728 +0.000074 +0.002112 +0.000077 + + +-0.1028301 +0.0000073 +0.0004343 +0.0000284 + + +0.000169 +0.000037 +-0.000051 +0.000039 + + + + + + + +0.026681 +0.000059 +0.380528 +0.000047 +0.001533 +0.000074 +0.002098 +0.000077 + + +-0.1032554 +0.0000081 +0.0003903 +0.0000287 + + +0.000172 +0.000037 +-0.000049 +0.000039 + + + + + + + +0.028125 +0.000060 +0.382637 +0.000047 +0.001417 +0.000074 +0.002138 +0.000077 + + +-0.1035814 +0.0000087 +0.0002491 +0.0000295 + + +0.000173 +0.000039 +-0.000051 +0.000041 + + + + + + + +0.029536 +0.000061 +0.384747 +0.000048 +0.001470 +0.000075 +0.002018 +0.000076 + + +-0.1037355 +0.0000075 +0.0000547 +0.0000295 + + +0.000175 +0.000041 +-0.000055 +0.000043 + + + + + + + +0.030979 +0.000060 +0.386690 +0.000048 +0.001350 +0.000077 +0.001870 +0.000076 + + +-0.1036800 +0.0000060 +-0.0001730 +0.0000280 + + +0.000181 +0.000042 +-0.000059 +0.000044 + + + + + + + +0.032196 +0.000060 +0.388516 +0.000048 +0.001027 +0.000081 +0.001808 +0.000077 + + +-0.1033971 +0.0000052 +-0.0003888 +0.0000265 + + +0.000200 +0.000041 +-0.000061 +0.000042 + + + + + + + +0.033068 +0.000059 +0.390309 +0.000049 +0.000739 +0.000084 +0.001802 +0.000079 + + +-0.1029222 +0.0000047 +-0.0005464 +0.0000255 + + +0.000223 +0.000038 +-0.000061 +0.000040 + + + + + + + +0.033697 +0.000058 +0.392077 +0.000050 +0.000546 +0.000086 +0.001687 +0.000081 + + +-0.1023252 +0.0000046 +-0.0006296 +0.0000248 + + +0.000244 +0.000036 +-0.000059 +0.000038 + + + + + + + +0.034151 +0.000059 +0.393692 +0.000051 +0.000373 +0.000086 +0.001532 +0.000082 + + +-0.1016765 +0.0000053 +-0.0006583 +0.0000251 + + +0.000252 +0.000037 +-0.000049 +0.000038 + + + + + + + +0.034407 +0.000061 +0.395180 +0.000053 +0.000062 +0.000085 +0.001498 +0.000084 + + +-0.1010214 +0.0000064 +-0.0006503 +0.0000260 + + +0.000251 +0.000039 +-0.000036 +0.000041 + + + + + + + +0.034355 +0.000062 +0.396659 +0.000053 +-0.000071 +0.000083 +0.001396 +0.000086 + + +-0.1003996 +0.0000062 +-0.0005866 +0.0000264 + + +0.000243 +0.000042 +-0.000029 +0.000043 + + + + + + + +0.034235 +0.000062 +0.398047 +0.000053 +-0.000189 +0.000081 +0.001450 +0.000087 + + +-0.0998785 +0.0000052 +-0.0004402 +0.0000255 + + +0.000230 +0.000043 +-0.000032 +0.000045 + + + + + + + +0.033954 +0.000062 +0.399568 +0.000052 +-0.000462 +0.000079 +0.001635 +0.000088 + + +-0.0995402 +0.0000051 +-0.0002261 +0.0000243 + + +0.000211 +0.000043 +-0.000068 +0.000045 + + + + + + + +0.033443 +0.000062 +0.401249 +0.000051 +-0.000431 +0.000078 +0.001664 +0.000088 + + +-0.0994318 +0.0000054 +0.0000086 +0.0000236 + + +0.000189 +0.000042 +-0.000116 +0.000044 + + + + + + + +0.033100 +0.000063 +0.402882 +0.000050 +-0.000250 +0.000078 +0.001575 +0.000087 + + +-0.0995463 +0.0000060 +0.0002063 +0.0000237 + + +0.000169 +0.000041 +-0.000162 +0.000044 + + + + + + + +0.032953 +0.000066 +0.404427 +0.000049 +-0.000041 +0.000078 +0.001556 +0.000086 + + +-0.0998254 +0.0000073 +0.0003405 +0.0000250 + + +0.000203 +0.000045 +-0.000202 +0.000049 + + + + + + + +0.033036 +0.000068 +0.405968 +0.000050 +0.000180 +0.000079 +0.001486 +0.000084 + + +-0.1001934 +0.0000092 +0.0003811 +0.0000264 + + +0.000258 +0.000053 +-0.000225 +0.000060 + + + + + + + +0.033397 +0.000069 +0.407426 +0.000051 +0.000621 +0.000080 +0.001396 +0.000083 + + +-0.1005440 +0.0000097 +0.0002991 +0.0000269 + + +0.000293 +0.000063 +-0.000220 +0.000072 + + + + + + + +0.034291 +0.000068 +0.408886 +0.000051 +0.001276 +0.000081 +0.001673 +0.000083 + + +-0.1007593 +0.0000082 +0.0001202 +0.0000264 + + +0.000287 +0.000069 +-0.000176 +0.000081 + + + + + + + +0.035755 +0.000065 +0.410726 +0.000051 +0.001514 +0.000083 +0.001999 +0.000085 + + +-0.1007694 +0.0000066 +-0.0001016 +0.0000253 + + +0.000215 +0.000073 +-0.000083 +0.000086 + + + + + + + +0.037206 +0.000064 +0.412809 +0.000051 +0.001242 +0.000085 +0.002112 +0.000089 + + +-0.1005623 +0.0000059 +-0.0003063 +0.0000243 + + +0.000018 +0.000085 +0.000105 +0.000102 + + + + + + + +0.038308 +0.000062 +0.414905 +0.000051 +0.000982 +0.000087 +0.002062 +0.000092 + + +-0.1001860 +0.0000060 +-0.0004273 +0.0000244 + + +0.000114 +0.000075 +-0.000041 +0.000089 + + + + + + + +0.039263 +0.000061 +0.416878 +0.000051 +0.001016 +0.000089 +0.001798 +0.000094 + + +-0.0997460 +0.0000068 +-0.0004270 +0.0000261 + + +0.000263 +0.000068 +-0.000214 +0.000080 + + + + + + + +0.040356 +0.000062 +0.418566 +0.000051 +0.001246 +0.000090 +0.001646 +0.000096 + + +-0.0993594 +0.0000074 +-0.0003348 +0.0000280 + + +0.000310 +0.000064 +-0.000236 +0.000073 + + + + + + + +0.041632 +0.000064 +0.420168 +0.000050 +0.001225 +0.000088 +0.001554 +0.000097 + + +-0.0990952 +0.0000065 +-0.0001967 +0.0000284 + + +0.000308 +0.000057 +-0.000185 +0.000064 + + + + + + + +0.042725 +0.000066 +0.421679 +0.000050 +0.000895 +0.000086 +0.001411 +0.000099 + + +-0.0989839 +0.0000053 +-0.0000194 +0.0000273 + + +0.000275 +0.000052 +-0.000100 +0.000057 + + + + + + + +0.043392 +0.000068 +0.423114 +0.000049 +0.000384 +0.000084 +0.001582 +0.000101 + + +-0.0990485 +0.0000045 +0.0001330 +0.0000266 + + +0.000250 +0.000049 +-0.000071 +0.000053 + + + + + + + +0.043546 +0.000069 +0.424847 +0.000049 +-0.000149 +0.000083 +0.001943 +0.000103 + + +-0.0992395 +0.0000040 +0.0002393 +0.0000266 + + +0.000226 +0.000044 +-0.000059 +0.000047 + + + + + + + +0.043348 +0.000070 +0.426888 +0.000049 +-0.000029 +0.000082 +0.002003 +0.000103 + + +-0.0995121 +0.0000037 +0.0003098 +0.0000272 + + +0.000201 +0.000040 +-0.000046 +0.000042 + + + + + + + +0.043556 +0.000071 +0.428900 +0.000050 +0.000630 +0.000083 +0.002020 +0.000102 + + +-0.0998212 +0.0000040 +0.0002969 +0.0000288 + + +0.000195 +0.000040 +-0.000030 +0.000042 + + + + + + + +0.044378 +0.000072 +0.431025 +0.000050 +0.000814 +0.000083 +0.002385 +0.000102 + + +-0.1000635 +0.0000046 +0.0001637 +0.0000312 + + +0.000201 +0.000041 +-0.000014 +0.000043 + + + + + + + +0.045120 +0.000073 +0.433553 +0.000049 +0.000505 +0.000082 +0.002631 +0.000101 + + +-0.1001281 +0.0000045 +-0.0000462 +0.0000325 + + +0.000211 +0.000042 +-0.000002 +0.000044 + + + + + + + +0.045585 +0.000074 +0.436130 +0.000049 +0.000576 +0.000082 +0.002355 +0.000099 + + +-0.0999706 +0.0000040 +-0.0002691 +0.0000325 + + +0.000221 +0.000043 +0.000002 +0.000044 + + + + + + + +0.046362 +0.000076 +0.438285 +0.000049 +0.001105 +0.000085 +0.001933 +0.000098 + + +-0.0996043 +0.0000040 +-0.0004565 +0.0000327 + + +0.000220 +0.000041 +-0.000015 +0.000043 + + + + + + + +0.047724 +0.000076 +0.440087 +0.000049 +0.001604 +0.000091 +0.001737 +0.000100 + + +-0.0990850 +0.0000044 +-0.0005617 +0.0000322 + + +0.000218 +0.000039 +-0.000041 +0.000040 + + + + + + + +0.049458 +0.000074 +0.441807 +0.000048 +0.001827 +0.000095 +0.001765 +0.000104 + + +-0.0985045 +0.0000050 +-0.0005842 +0.0000308 + + +0.000218 +0.000037 +-0.000066 +0.000038 + + + + + + + +0.051224 +0.000073 +0.443585 +0.000049 +0.001559 +0.000098 +0.001754 +0.000109 + + +-0.0979388 +0.0000059 +-0.0005182 +0.0000301 + + +0.000240 +0.000037 +-0.000066 +0.000038 + + + + + + + +0.052567 +0.000072 +0.445328 +0.000050 +0.001051 +0.000098 +0.001715 +0.000111 + + +-0.0974558 +0.0000066 +-0.0004593 +0.0000301 + + +0.000272 +0.000039 +-0.000054 +0.000040 + + + + + + + +0.053458 +0.000070 +0.447081 +0.000050 +0.000821 +0.000096 +0.001882 +0.000109 + + +-0.0970164 +0.0000059 +-0.0004658 +0.0000298 + + +0.000302 +0.000040 +-0.000044 +0.000041 + + + + + + + +0.054296 +0.000067 +0.449042 +0.000050 +0.000948 +0.000093 +0.002036 +0.000105 + + +-0.0966030 +0.0000052 +-0.0002955 +0.0000291 + + +0.000324 +0.000041 +-0.000041 +0.000043 + + + + + + + +0.055337 +0.000064 +0.451062 +0.000050 +0.001118 +0.000090 +0.001906 +0.000103 + + +-0.0964409 +0.0000052 +-0.0000199 +0.0000286 + + +0.000323 +0.000041 +-0.000064 +0.000042 + + + + + + + +0.056532 +0.000063 +0.452864 +0.000051 +0.001262 +0.000089 +0.001669 +0.000103 + + +-0.0965581 +0.0000057 +0.0002448 +0.0000293 + + +0.000312 +0.000039 +-0.000095 +0.000040 + + + + + + + +0.057880 +0.000063 +0.454481 +0.000053 +0.001433 +0.000090 +0.001667 +0.000103 + + +-0.0969131 +0.0000070 +0.0004497 +0.0000317 + + +0.000329 +0.000041 +-0.000097 +0.000042 + + + + + + + +0.059435 +0.000066 +0.456160 +0.000054 +0.001674 +0.000093 +0.001662 +0.000105 + + +-0.0974276 +0.0000085 +0.0005607 +0.0000335 + + +0.000365 +0.000047 +-0.000073 +0.000048 + + + + + + + +0.061304 +0.000067 +0.457786 +0.000055 +0.002228 +0.000096 +0.001555 +0.000107 + + +-0.0979965 +0.0000087 +0.0005661 +0.0000330 + + +0.000395 +0.000054 +-0.000041 +0.000056 + + + + + + + +0.063719 +0.000067 +0.459301 +0.000055 +0.002486 +0.000098 +0.001551 +0.000108 + + +-0.0985094 +0.0000076 +0.0004425 +0.0000315 + + +0.000405 +0.000062 +-0.000011 +0.000063 + + + + + + + +0.066166 +0.000066 +0.460799 +0.000054 +0.002243 +0.000099 +0.001336 +0.000107 + + +-0.0988401 +0.0000064 +0.0002029 +0.0000295 + + +0.000379 +0.000067 +0.000008 +0.000068 + + + + + + + +0.068313 +0.000066 +0.462012 +0.000053 +0.002143 +0.000100 +0.001105 +0.000105 + + +-0.0989022 +0.0000057 +-0.0000882 +0.0000274 + + +0.000303 +0.000069 +0.000007 +0.000070 + + + + + + + +0.070482 +0.000065 +0.463058 +0.000053 +0.002212 +0.000100 +0.001032 +0.000104 + + +-0.0986934 +0.0000057 +-0.0003037 +0.0000256 + + +0.000128 +0.000077 +-0.000025 +0.000079 + + + + + + + +0.072763 +0.000064 +0.464082 +0.000053 +0.002378 +0.000101 +0.000991 +0.000105 + + +-0.0983255 +0.0000061 +-0.0004311 +0.0000250 + + +0.000219 +0.000068 +-0.000083 +0.000070 + + + + + + + +0.075234 +0.000063 +0.465104 +0.000054 +0.002597 +0.000105 +0.001078 +0.000107 + + +-0.0978951 +0.0000069 +-0.0003781 +0.0000258 + + +0.000345 +0.000063 +-0.000123 +0.000064 + + + + + + + +0.077884 +0.000062 +0.466316 +0.000056 +0.002669 +0.000110 +0.001452 +0.000108 + + +-0.0975804 +0.0000077 +-0.0002384 +0.0000270 + + +0.000372 +0.000060 +-0.000131 +0.000062 + + + + + + + +0.080492 +0.000061 +0.467956 +0.000057 +0.002474 +0.000113 +0.001871 +0.000107 + + +-0.0973900 +0.0000072 +-0.0002149 +0.0000274 + + +0.000352 +0.000056 +-0.000121 +0.000058 + + + + + + + +0.082822 +0.000059 +0.469870 +0.000058 +0.002186 +0.000113 +0.001782 +0.000104 + + +-0.0972121 +0.0000063 +-0.0000858 +0.0000269 + + +0.000312 +0.000053 +-0.000107 +0.000055 + + + + + + + +0.084843 +0.000059 +0.471495 +0.000059 +0.001797 +0.000112 +0.001457 +0.000101 + + +-0.0972074 +0.0000060 +0.0000705 +0.0000264 + + +0.000313 +0.000051 +-0.000120 +0.000053 + + + + + + + +0.086489 +0.000061 +0.472753 +0.000059 +0.001496 +0.000110 +0.000999 +0.000099 + + +-0.0973270 +0.0000060 +0.0001527 +0.0000263 + + +0.000325 +0.000048 +-0.000141 +0.000050 + + + + + + + +0.087977 +0.000062 +0.473554 +0.000058 +0.001631 +0.000107 +0.000613 +0.000098 + + +-0.0974883 +0.0000063 +0.0001741 +0.0000266 + + +0.000336 +0.000045 +-0.000157 +0.000047 + + + + + + + +0.089733 +0.000062 +0.474085 +0.000057 +0.001909 +0.000105 +0.000538 +0.000096 + + +-0.0976226 +0.0000074 +0.0000462 +0.0000278 + + +0.000293 +0.000046 +-0.000144 +0.000048 + + + + + + + +0.091708 +0.000062 +0.474658 +0.000055 +0.001945 +0.000104 +0.000646 +0.000095 + + +-0.0975847 +0.0000083 +-0.0001270 +0.0000288 + + +0.000232 +0.000049 +-0.000112 +0.000051 + + + + + + + +0.093642 +0.000062 +0.475359 +0.000054 +0.001915 +0.000103 +0.000746 +0.000094 + + +-0.0974023 +0.0000075 +-0.0001863 +0.0000285 + + +0.000191 +0.000053 +-0.000074 +0.000055 + + + + + + + +0.095587 +0.000061 +0.476136 +0.000053 +0.002047 +0.000103 +0.000762 +0.000093 + + +-0.0971931 +0.0000060 +-0.0002309 +0.0000271 + + +0.000197 +0.000056 +-0.000035 +0.000057 + + + + + + + +0.097692 +0.000060 +0.476946 +0.000053 +0.002164 +0.000102 +0.000984 +0.000091 + + +-0.0969144 +0.0000050 +-0.0003595 +0.0000253 + + +0.000271 +0.000058 +-0.000002 +0.000058 + + + + + + + +0.099822 +0.000058 +0.477979 +0.000052 +0.001989 +0.000100 +0.000976 +0.000089 + + +-0.0964993 +0.0000047 +-0.0004525 +0.0000241 + + +0.000478 +0.000063 +0.000021 +0.000063 + + + + + + + +0.101700 +0.000059 +0.478860 +0.000051 +0.001772 +0.000096 +0.000716 +0.000087 + + +-0.0960302 +0.0000049 +-0.0004755 +0.0000243 + + +0.000371 +0.000058 +0.000026 +0.000058 + + + + + + + +0.103415 +0.000061 +0.479481 +0.000050 +0.001720 +0.000092 +0.000607 +0.000086 + + +-0.0955716 +0.0000057 +-0.0004263 +0.0000260 + + +0.000226 +0.000056 +0.000005 +0.000056 + + + + + + + +0.105113 +0.000064 +0.480053 +0.000049 +0.001599 +0.000090 +0.000494 +0.000087 + + +-0.0951911 +0.0000063 +-0.0003326 +0.0000277 + + +0.000211 +0.000058 +-0.000040 +0.000057 + + + + + + + +0.106716 +0.000067 +0.480513 +0.000050 +0.001650 +0.000087 +0.000419 +0.000088 + + +-0.0949241 +0.0000056 +-0.0002100 +0.0000281 + + +0.000246 +0.000061 +-0.000092 +0.000058 + + + + + + + +0.108530 +0.000068 +0.480991 +0.000050 +0.002149 +0.000084 +0.000682 +0.000089 + + +-0.0948159 +0.0000046 +0.0000304 +0.0000277 + + +0.000291 +0.000063 +-0.000138 +0.000058 + + + + + + + +0.110903 +0.000067 +0.481790 +0.000051 +0.002528 +0.000082 +0.000863 +0.000091 + + +-0.0949881 +0.0000041 +0.0003164 +0.0000273 + + +0.000243 +0.000072 +-0.000143 +0.000063 + + + + + + + +0.113502 +0.000066 +0.482649 +0.000051 +0.002607 +0.000083 +0.000785 +0.000093 + + +-0.0954306 +0.0000038 +0.0005540 +0.0000265 + + +0.000211 +0.000082 +-0.000148 +0.000069 + + + + + + + +0.116077 +0.000063 +0.483365 +0.000051 +0.002520 +0.000085 +0.000639 +0.000095 + + +-0.0960633 +0.0000035 +0.0006865 +0.0000258 + + +0.000267 +0.000072 +-0.000180 +0.000062 + + + + + + + +0.118509 +0.000063 +0.483953 +0.000051 +0.002373 +0.000087 +0.000508 +0.000097 + + +-0.0967649 +0.0000035 +0.0006915 +0.0000264 + + +0.000328 +0.000066 +-0.000199 +0.000058 + + + + + + + +0.120701 +0.000064 +0.484488 +0.000053 +0.001893 +0.000089 +0.000757 +0.000100 + + +-0.0974099 +0.0000037 +0.0005839 +0.0000273 + + +0.000357 +0.000063 +-0.000189 +0.000057 + + + + + + + +0.122284 +0.000065 +0.485290 +0.000054 +0.001083 +0.000090 +0.000735 +0.000102 + + +-0.0979015 +0.0000037 +0.0003946 +0.0000272 + + +0.000370 +0.000060 +-0.000165 +0.000055 + + + + + + + +0.123227 +0.000066 +0.485828 +0.000055 +0.001158 +0.000088 +0.000110 +0.000102 + + +-0.0981799 +0.0000036 +0.0001620 +0.0000263 + + +0.000372 +0.000057 +-0.000134 +0.000054 + + + + + + + +0.124611 +0.000067 +0.485713 +0.000054 +0.001683 +0.000084 +-0.000181 +0.000104 + + +-0.0982314 +0.0000036 +-0.0000515 +0.0000257 + + +0.000380 +0.000056 +-0.000119 +0.000054 + + + + + + + +0.126467 +0.000067 +0.485555 +0.000053 +0.001874 +0.000080 +-0.000069 +0.000108 + + +-0.0981086 +0.0000036 +-0.0001787 +0.0000257 + + +0.000386 +0.000055 +-0.000108 +0.000055 + + + + + + + +0.128421 +0.000067 +0.485623 +0.000053 +0.002133 +0.000079 +0.000276 +0.000108 + + +-0.0979196 +0.0000037 +-0.0001713 +0.0000255 + + +0.000386 +0.000055 +-0.000099 +0.000056 + + + + + + + +0.130662 +0.000065 +0.486060 +0.000054 +0.002276 +0.000079 +0.000588 +0.000105 + + +-0.0977899 +0.0000042 +-0.0000815 +0.0000254 + + +0.000376 +0.000054 +-0.000087 +0.000055 + + + + + + + +0.132973 +0.000064 +0.486722 +0.000055 +0.002370 +0.000080 +0.000641 +0.000100 + + +-0.0977637 +0.0000047 +0.0000169 +0.0000256 + + +0.000360 +0.000051 +-0.000073 +0.000053 + + + + + + + +0.135357 +0.000063 +0.487378 +0.000055 +0.002398 +0.000081 +0.000785 +0.000096 + + +-0.0978311 +0.0000047 +0.0001115 +0.0000255 + + +0.000340 +0.000049 +-0.000060 +0.000051 + + + + + + + +0.137681 +0.000061 +0.488138 +0.000056 +0.002112 +0.000083 +0.000591 +0.000094 + + +-0.0979894 +0.0000045 +0.0002137 +0.0000251 + + +0.000319 +0.000047 +-0.000048 +0.000049 + + + + + + + +0.139680 +0.000060 +0.488540 +0.000057 +0.001988 +0.000084 +0.000126 +0.000093 + + +-0.0982327 +0.0000050 +0.0002612 +0.0000253 + + +0.000300 +0.000045 +-0.000039 +0.000047 + + + + + + + +0.141652 +0.000060 +0.488521 +0.000057 +0.002004 +0.000085 +-0.000031 +0.000094 + + +-0.0984800 +0.0000061 +0.0002182 +0.0000261 + + +0.000283 +0.000042 +-0.000031 +0.000044 + + + + + + + +0.143585 +0.000060 +0.488472 +0.000055 +0.001718 +0.000085 +-0.000078 +0.000095 + + +-0.0986404 +0.0000075 +0.0000970 +0.0000274 + + +0.000270 +0.000039 +-0.000025 +0.000041 + + + + + + + +0.145172 +0.000060 +0.488374 +0.000054 +0.001526 +0.000086 +-0.000079 +0.000095 + + +-0.0986373 +0.0000099 +-0.0001225 +0.0000296 + + +0.000255 +0.000039 +-0.000016 +0.000042 + + + + + + + +0.146664 +0.000061 +0.488257 +0.000054 +0.001441 +0.000089 +-0.000155 +0.000093 + + +-0.0983732 +0.0000136 +-0.0004271 +0.0000318 + + +0.000244 +0.000041 +-0.000006 +0.000044 + + + + + + + +0.148142 +0.000063 +0.487950 +0.000053 +0.001576 +0.000092 +-0.000649 +0.000091 + + +-0.0977955 +0.0000171 +-0.0007169 +0.0000320 + + +0.000244 +0.000044 +0.000001 +0.000047 + + + + + + + +0.149870 +0.000063 +0.487111 +0.000053 +0.001977 +0.000092 +-0.000919 +0.000089 + + +-0.0969557 +0.0000162 +-0.0009552 +0.0000301 + + +0.000262 +0.000047 +0.000005 +0.000049 + + + + + + + +0.152014 +0.000062 +0.486196 +0.000052 +0.002263 +0.000087 +-0.000809 +0.000088 + + +-0.0959150 +0.0000131 +-0.0011092 +0.0000280 + + +0.000302 +0.000048 +0.000004 +0.000050 + + + + + + + +0.154327 +0.000061 +0.485456 +0.000051 +0.002288 +0.000083 +-0.000707 +0.000088 + + +-0.0947720 +0.0000119 +-0.0011495 +0.0000268 + + +0.000387 +0.000049 +-0.000006 +0.000051 + + + + + + + +0.156604 +0.000062 +0.484781 +0.000051 +0.002285 +0.000081 +-0.000643 +0.000088 + + +-0.0936351 +0.0000120 +-0.0011228 +0.0000263 + + +0.000477 +0.000050 +-0.000020 +0.000052 + + + + + + + +0.158891 +0.000065 +0.484171 +0.000051 +0.002269 +0.000080 +-0.000579 +0.000088 + + +-0.0925606 +0.0000136 +-0.0009883 +0.0000267 + + +0.000513 +0.000050 +-0.000034 +0.000052 + + + + + + + +0.161168 +0.000070 +0.483627 +0.000053 +0.002253 +0.000081 +-0.000519 +0.000089 + + +-0.0916451 +0.0000161 +-0.0008647 +0.0000269 + + +0.000501 +0.000049 +-0.000048 +0.000051 + + + + + + + +0.163515 +0.000072 +0.483159 +0.000055 +0.002579 +0.000083 +-0.000415 +0.000090 + + +-0.0908397 +0.0000180 +-0.0007592 +0.0000264 + + +0.000462 +0.000048 +-0.000060 +0.000049 + + + + + + + +0.166285 +0.000071 +0.482844 +0.000056 +0.002984 +0.000085 +-0.000132 +0.000093 + + +-0.0901592 +0.0000160 +-0.0005669 +0.0000255 + + +0.000410 +0.000046 +-0.000071 +0.000048 + + + + + + + +0.169354 +0.000068 +0.482823 +0.000056 +0.003031 +0.000086 +0.000044 +0.000095 + + +-0.0896875 +0.0000122 +-0.0003977 +0.0000249 + + +0.000354 +0.000046 +-0.000079 +0.000047 + + + + + + + +0.172332 +0.000066 +0.482882 +0.000056 +0.002921 +0.000087 +0.000045 +0.000097 + + +-0.0893507 +0.0000097 +-0.0002938 +0.0000249 + + +0.000317 +0.000044 +-0.000082 +0.000045 + + + + + + + +0.175177 +0.000066 +0.482865 +0.000057 +0.002746 +0.000090 +-0.000097 +0.000099 + + +-0.0890878 +0.0000084 +-0.0002317 +0.0000252 + + +0.000289 +0.000042 +-0.000083 +0.000043 + + + + + + + +0.177831 +0.000066 +0.482631 +0.000057 +0.002542 +0.000092 +-0.000465 +0.000100 + + +-0.0888458 +0.0000083 +-0.0002720 +0.0000262 + + +0.000273 +0.000043 +-0.000082 +0.000043 + + + + + + + +0.180313 +0.000068 +0.482012 +0.000057 +0.002508 +0.000092 +-0.000709 +0.000101 + + +-0.0884990 +0.0000088 +-0.0004392 +0.0000275 + + +0.000268 +0.000045 +-0.000082 +0.000045 + + + + + + + +0.182782 +0.000069 +0.481239 +0.000057 +0.002371 +0.000090 +-0.000784 +0.000100 + + +-0.0879341 +0.0000083 +-0.0007002 +0.0000278 + + +0.000271 +0.000047 +-0.000087 +0.000048 + + + + + + + +0.185046 +0.000069 +0.480388 +0.000056 +0.002085 +0.000088 +-0.000982 +0.000100 + + +-0.0870906 +0.0000075 +-0.0009872 +0.0000266 + + +0.000281 +0.000049 +-0.000098 +0.000050 + + + + + + + +0.187072 +0.000068 +0.479292 +0.000055 +0.002071 +0.000085 +-0.001224 +0.000101 + + +-0.0859879 +0.0000078 +-0.0012037 +0.0000253 + + +0.000295 +0.000052 +-0.000133 +0.000053 + + + + + + + +0.189221 +0.000068 +0.478002 +0.000055 +0.002288 +0.000081 +-0.001317 +0.000103 + + +-0.0847385 +0.0000081 +-0.0012725 +0.0000247 + + +0.000312 +0.000058 +-0.000174 +0.000057 + + + + + + + +0.191592 +0.000067 +0.476703 +0.000055 +0.002385 +0.000079 +-0.001238 +0.000104 + + +-0.0835084 +0.0000077 +-0.0011447 +0.0000243 + + +0.000328 +0.000063 +-0.000213 +0.000061 + + + + + + + +0.194021 +0.000064 +0.475533 +0.000054 +0.002476 +0.000079 +-0.001082 +0.000106 + + +-0.0824690 +0.0000078 +-0.0009220 +0.0000246 + + +0.000344 +0.000063 +-0.000216 +0.000061 + + + + + + + +0.196594 +0.000062 +0.474514 +0.000053 +0.002756 +0.000084 +-0.000999 +0.000107 + + +-0.0816427 +0.0000087 +-0.0007698 +0.0000253 + + +0.000355 +0.000059 +-0.000197 +0.000058 + + + + + + + +0.199459 +0.000061 +0.473572 +0.000055 +0.002955 +0.000089 +-0.000844 +0.000106 + + +-0.0809309 +0.0000094 +-0.0006532 +0.0000257 + + +0.000358 +0.000054 +-0.000169 +0.000055 + + + + + + + +0.202399 +0.000059 +0.472821 +0.000056 +0.002808 +0.000091 +-0.000623 +0.000103 + + +-0.0803195 +0.0000083 +-0.0005639 +0.0000257 + + +0.000349 +0.000050 +-0.000140 +0.000052 + + + + + + + +0.205100 +0.000058 +0.472247 +0.000056 +0.002597 +0.000088 +-0.000581 +0.000099 + + +-0.0797551 +0.0000065 +-0.0005982 +0.0000253 + + +0.000324 +0.000048 +-0.000116 +0.000050 + + + + + + + +0.207635 +0.000058 +0.471613 +0.000055 +0.002494 +0.000082 +-0.000730 +0.000096 + + +-0.0791013 +0.0000054 +-0.0007229 +0.0000250 + + +0.000264 +0.000048 +-0.000110 +0.000051 + + + + + + + +0.210129 +0.000057 +0.470776 +0.000055 +0.002530 +0.000077 +-0.000959 +0.000093 + + +-0.0783043 +0.0000048 +-0.0008617 +0.0000252 + + +0.000206 +0.000048 +-0.000111 +0.000051 + + + + + + + +0.212707 +0.000057 +0.469708 +0.000055 +0.002638 +0.000075 +-0.001169 +0.000091 + + +-0.0773619 +0.0000050 +-0.0010240 +0.0000263 + + +0.000204 +0.000048 +-0.000117 +0.000051 + + + + + + + +0.215408 +0.000056 +0.468452 +0.000055 +0.002742 +0.000073 +-0.001350 +0.000090 + + +-0.0762391 +0.0000053 +-0.0012383 +0.0000274 + + +0.000248 +0.000048 +-0.000125 +0.000051 + + + + + + + +0.218240 +0.000056 +0.467049 +0.000055 +0.003024 +0.000073 +-0.001423 +0.000089 + + +-0.0748965 +0.0000047 +-0.0014461 +0.0000273 + + +0.000310 +0.000048 +-0.000132 +0.000051 + + + + + + + +0.221351 +0.000057 +0.465614 +0.000055 +0.003125 +0.000072 +-0.001417 +0.000086 + + +-0.0733793 +0.0000039 +-0.0015603 +0.0000261 + + +0.000374 +0.000048 +-0.000134 +0.000051 + + + + + + + +0.224422 +0.000057 +0.464174 +0.000055 +0.002917 +0.000071 +-0.001496 +0.000085 + + +-0.0717947 +0.0000036 +-0.0015972 +0.0000250 + + +0.000396 +0.000049 +-0.000116 +0.000052 + + + + + + + +0.227249 +0.000057 +0.462605 +0.000056 +0.002765 +0.000071 +-0.001709 +0.000084 + + +-0.0702045 +0.0000035 +-0.0015783 +0.0000240 + + +0.000398 +0.000050 +-0.000094 +0.000055 + + + + + + + +0.230018 +0.000057 +0.460858 +0.000055 +0.002821 +0.000071 +-0.001699 +0.000085 + + +-0.0686678 +0.0000036 +-0.0014763 +0.0000233 + + +0.000391 +0.000051 +-0.000075 +0.000057 + + + + + + + +0.232930 +0.000057 +0.459239 +0.000054 +0.003103 +0.000073 +-0.001485 +0.000086 + + +-0.0672683 +0.0000039 +-0.0013134 +0.0000236 + + +0.000369 +0.000051 +-0.000086 +0.000056 + + + + + + + +0.236103 +0.000057 +0.457847 +0.000052 +0.003166 +0.000075 +-0.001295 +0.000088 + + +-0.0660455 +0.0000043 +-0.0011399 +0.0000241 + + +0.000341 +0.000050 +-0.000113 +0.000055 + + + + + + + +0.239173 +0.000056 +0.456559 +0.000051 +0.002844 +0.000078 +-0.001342 +0.000089 + + +-0.0650013 +0.0000042 +-0.0009477 +0.0000239 + + +0.000316 +0.000049 +-0.000142 +0.000053 + + + + + + + +0.241876 +0.000056 +0.455108 +0.000051 +0.002619 +0.000082 +-0.001681 +0.000091 + + +-0.0641610 +0.0000039 +-0.0007164 +0.0000228 + + +0.000301 +0.000048 +-0.000167 +0.000052 + + + + + + + +0.244467 +0.000056 +0.453330 +0.000051 +0.002627 +0.000084 +-0.001769 +0.000094 + + +-0.0635430 +0.0000041 +-0.0005315 +0.0000217 + + +0.000314 +0.000049 +-0.000174 +0.000053 + + + + + + + +0.247109 +0.000056 +0.451620 +0.000050 +0.002645 +0.000086 +-0.001567 +0.000096 + + +-0.0630552 +0.0000046 +-0.0004985 +0.0000212 + + +0.000335 +0.000050 +-0.000171 +0.000055 + + + + + + + +0.249738 +0.000056 +0.450129 +0.000049 +0.002613 +0.000087 +-0.001453 +0.000096 + + +-0.0625547 +0.0000053 +-0.0004682 +0.0000218 + + +0.000340 +0.000050 +-0.000159 +0.000055 + + + + + + + +0.252300 +0.000058 +0.448656 +0.000049 +0.002495 +0.000088 +-0.001579 +0.000096 + + +-0.0620609 +0.0000059 +-0.0005369 +0.0000228 + + +0.000332 +0.000049 +-0.000141 +0.000053 + + + + + + + +0.254688 +0.000061 +0.447030 +0.000049 +0.002286 +0.000089 +-0.001628 +0.000095 + + +-0.0614087 +0.0000058 +-0.0008348 +0.0000237 + + +0.000318 +0.000048 +-0.000122 +0.000052 + + + + + + + +0.256780 +0.000064 +0.445426 +0.000050 +0.001810 +0.000088 +-0.001469 +0.000093 + + +-0.0604085 +0.0000053 +-0.0011465 +0.0000241 + + +0.000303 +0.000047 +-0.000109 +0.000050 + + + + + + + +0.258301 +0.000066 +0.443925 +0.000051 +0.001107 +0.000087 +-0.001732 +0.000090 + + +-0.0591521 +0.0000051 +-0.0013315 +0.0000238 + + +0.000291 +0.000046 +-0.000105 +0.000049 + + + + + + + +0.259228 +0.000068 +0.442028 +0.000052 +0.000937 +0.000086 +-0.002079 +0.000088 + + +-0.0577854 +0.0000056 +-0.0013785 +0.0000237 + + +0.000299 +0.000047 +-0.000138 +0.000050 + + + + + + + +0.260268 +0.000070 +0.439933 +0.000053 +0.001250 +0.000083 +-0.001897 +0.000088 + + +-0.0564442 +0.0000068 +-0.0012784 +0.0000242 + + +0.000314 +0.000048 +-0.000179 +0.000052 + + + + + + + +0.261700 +0.000070 +0.438136 +0.000053 +0.001617 +0.000083 +-0.001757 +0.000088 + + +-0.0552629 +0.0000083 +-0.0010664 +0.0000249 + + +0.000329 +0.000049 +-0.000211 +0.000053 + + + + + + + +0.263439 +0.000070 +0.436349 +0.000054 +0.001804 +0.000084 +-0.001915 +0.000088 + + +-0.0543088 +0.0000105 +-0.0008620 +0.0000260 + + +0.000342 +0.000048 +-0.000194 +0.000053 + + + + + + + +0.265300 +0.000069 +0.434359 +0.000055 +0.001922 +0.000085 +-0.002019 +0.000088 + + +-0.0535323 +0.0000120 +-0.0007059 +0.0000269 + + +0.000351 +0.000048 +-0.000149 +0.000052 + + + + + + + +0.267256 +0.000067 +0.432323 +0.000055 +0.001999 +0.000087 +-0.002035 +0.000086 + + +-0.0528826 +0.0000104 +-0.0005893 +0.0000267 + + +0.000355 +0.000047 +-0.000101 +0.000051 + + + + + + + +0.269222 +0.000064 +0.430278 +0.000055 +0.001849 +0.000086 +-0.002098 +0.000085 + + +-0.0523054 +0.0000085 +-0.0005958 +0.0000259 + + +0.000350 +0.000046 +-0.000063 +0.000051 + + + + + + + +0.270971 +0.000062 +0.428191 +0.000055 +0.001655 +0.000083 +-0.002016 +0.000083 + + +-0.0516628 +0.0000078 +-0.0007040 +0.0000254 + + +0.000322 +0.000047 +-0.000081 +0.000052 + + + + + + + +0.272558 +0.000061 +0.426249 +0.000056 +0.001523 +0.000081 +-0.001842 +0.000082 + + +-0.0508868 +0.0000078 +-0.0008489 +0.0000250 + + +0.000287 +0.000048 +-0.000121 +0.000053 + + + + + + + +0.274060 +0.000062 +0.424461 +0.000056 +0.001549 +0.000079 +-0.001776 +0.000080 + + +-0.0499632 +0.0000081 +-0.0009757 +0.0000247 + + +0.000255 +0.000049 +-0.000165 +0.000055 + + + + + + + +0.275606 +0.000062 +0.422692 +0.000056 +0.001477 +0.000078 +-0.001800 +0.000079 + + +-0.0489015 +0.0000093 +-0.0011859 +0.0000253 + + +0.000256 +0.000050 +-0.000192 +0.000056 + + + + + + + +0.277046 +0.000062 +0.420923 +0.000056 +0.001448 +0.000080 +-0.001663 +0.000079 + + +-0.0476115 +0.0000102 +-0.0013968 +0.0000265 + + +0.000275 +0.000050 +-0.000204 +0.000059 + + + + + + + +0.278479 +0.000062 +0.419342 +0.000055 +0.001409 +0.000083 +-0.001456 +0.000079 + + +-0.0461634 +0.0000091 +-0.0014314 +0.0000271 + + +0.000296 +0.000051 +-0.000209 +0.000062 + + + + + + + +0.279843 +0.000062 +0.417878 +0.000054 +0.001280 +0.000085 +-0.001613 +0.000080 + + +-0.0447322 +0.0000076 +-0.0014476 +0.0000267 + + +0.000313 +0.000052 +-0.000207 +0.000063 + + + + + + + +0.281074 +0.000063 +0.416128 +0.000053 +0.001234 +0.000086 +-0.001869 +0.000082 + + +-0.0432746 +0.0000070 +-0.0014738 +0.0000263 + + +0.000296 +0.000053 +-0.000194 +0.000066 + + + + + + + +0.282275 +0.000065 +0.414127 +0.000053 +0.001083 +0.000087 +-0.002180 +0.000082 + + +-0.0418119 +0.0000068 +-0.0014418 +0.0000262 + + +0.000267 +0.000056 +-0.000180 +0.000069 + + + + + + + +0.283342 +0.000066 +0.411836 +0.000054 +0.001160 +0.000086 +-0.002385 +0.000083 + + +-0.0404227 +0.0000068 +-0.0013205 +0.0000260 + + +0.000240 +0.000059 +-0.000169 +0.000072 + + + + + + + +0.284584 +0.000067 +0.409455 +0.000054 +0.001376 +0.000085 +-0.002264 +0.000085 + + +-0.0391933 +0.0000071 +-0.0011252 +0.0000261 + + +0.000241 +0.000058 +-0.000188 +0.000070 + + + + + + + +0.285965 +0.000067 +0.407278 +0.000054 +0.001280 +0.000084 +-0.002113 +0.000087 + + +-0.0381778 +0.0000075 +-0.0009048 +0.0000263 + + +0.000258 +0.000055 +-0.000222 +0.000064 + + + + + + + +0.287096 +0.000067 +0.405215 +0.000052 +0.000883 +0.000083 +-0.001980 +0.000089 + + +-0.0373774 +0.0000074 +-0.0007027 +0.0000255 + + +0.000278 +0.000051 +-0.000253 +0.000057 + + + + + + + +0.287837 +0.000064 +0.403225 +0.000052 +0.000634 +0.000082 +-0.002109 +0.000090 + + +-0.0367581 +0.0000074 +-0.0005452 +0.0000239 + + +0.000291 +0.000049 +-0.000271 +0.000053 + + + + + + + +0.288505 +0.000063 +0.401030 +0.000052 +0.000909 +0.000082 +-0.002290 +0.000089 + + +-0.0362636 +0.0000083 +-0.0004596 +0.0000228 + + +0.000283 +0.000048 +-0.000254 +0.000052 + + + + + + + +0.289525 +0.000062 +0.398724 +0.000053 +0.001068 +0.000084 +-0.002197 +0.000089 + + +-0.0358134 +0.0000101 +-0.0004552 +0.0000227 + + +0.000269 +0.000048 +-0.000224 +0.000052 + + + + + + + +0.290512 +0.000062 +0.396542 +0.000053 +0.000719 +0.000086 +-0.002255 +0.000089 + + +-0.0353263 +0.0000131 +-0.0005279 +0.0000238 + + +0.000263 +0.000048 +-0.000211 +0.000052 + + + + + + + +0.291078 +0.000063 +0.394199 +0.000053 +0.000468 +0.000088 +-0.002467 +0.000089 + + +-0.0347268 +0.0000158 +-0.0006877 +0.0000256 + + +0.000267 +0.000047 +-0.000215 +0.000051 + + + + + + + +0.291569 +0.000062 +0.391648 +0.000052 +0.000673 +0.000089 +-0.002582 +0.000089 + + +-0.0339408 +0.0000154 +-0.0008867 +0.0000270 + + +0.000274 +0.000047 +-0.000226 +0.000051 + + + + + + + +0.292346 +0.000062 +0.389011 +0.000052 +0.000842 +0.000089 +-0.002717 +0.000089 + + +-0.0329687 +0.0000127 +-0.0010393 +0.0000269 + + +0.000284 +0.000047 +-0.000241 +0.000050 + + + + + + + +0.293177 +0.000063 +0.386216 +0.000051 +0.000717 +0.000089 +-0.002907 +0.000091 + + +-0.0318907 +0.0000100 +-0.0010958 +0.0000259 + + +0.000293 +0.000047 +-0.000257 +0.000049 + + + + + + + +0.293830 +0.000064 +0.383265 +0.000051 +0.000644 +0.000089 +-0.002986 +0.000094 + + +-0.0308176 +0.0000085 +-0.0010272 +0.0000254 + + +0.000297 +0.000046 +-0.000267 +0.000049 + + + + + + + +0.294459 +0.000064 +0.380359 +0.000051 +0.000607 +0.000089 +-0.002710 +0.000096 + + +-0.0298732 +0.0000077 +-0.0008454 +0.0000253 + + +0.000298 +0.000046 +-0.000273 +0.000049 + + + + + + + +0.295044 +0.000061 +0.377846 +0.000050 +0.000564 +0.000088 +-0.002317 +0.000095 + + +-0.0291442 +0.0000074 +-0.0006105 +0.0000258 + + +0.000299 +0.000046 +-0.000274 +0.000049 + + + + + + + +0.295587 +0.000058 +0.375727 +0.000048 +0.000619 +0.000085 +-0.002065 +0.000091 + + +-0.0286417 +0.0000083 +-0.0004104 +0.0000271 + + +0.000300 +0.000048 +-0.000280 +0.000050 + + + + + + + +0.296380 +0.000055 +0.373573 +0.000046 +0.000940 +0.000078 +-0.002195 +0.000085 + + +-0.0282827 +0.0000100 +-0.0003234 +0.0000273 + + +0.000300 +0.000050 +-0.000281 +0.000054 + + + + + + + +0.297490 +0.000053 +0.371314 +0.000044 +0.001141 +0.000072 +-0.002279 +0.000079 + + +-0.0279483 +0.0000109 +-0.0003439 +0.0000257 + + +0.000300 +0.000054 +-0.000266 +0.000059 + + + + + + + +0.298534 +0.000051 +0.369047 +0.000042 +0.000936 +0.000068 +-0.002280 +0.000074 + + +-0.0275740 +0.0000098 +-0.0004278 +0.0000239 + + +0.000300 +0.000057 +-0.000229 +0.000063 + + + + + + + +0.299290 +0.000049 +0.366741 +0.000041 +0.000670 +0.000065 +-0.002326 +0.000072 + + +-0.0270680 +0.0000084 +-0.0006088 +0.0000225 + + +0.000302 +0.000058 +-0.000162 +0.000065 + + + + + + + +0.299933 +0.000048 +0.364397 +0.000041 +0.000732 +0.000063 +-0.002358 +0.000070 + + +-0.0263308 +0.0000083 +-0.0008610 +0.0000215 + + +0.000294 +0.000065 +0.000004 +0.000072 + + + + + + + +0.300900 +0.000048 +0.362032 +0.000041 +0.001103 +0.000062 +-0.002334 +0.000069 + + +-0.0253476 +0.0000091 +-0.0011017 +0.0000210 + + +0.000301 +0.000072 +0.000127 +0.000080 + + + + + + + +0.302113 +0.000048 +0.359767 +0.000040 +0.001148 +0.000062 +-0.002276 +0.000068 + + +-0.0241394 +0.0000112 +-0.0012993 +0.0000210 + + +0.000370 +0.000075 +0.000048 +0.000084 + + + + + + + +0.303009 +0.000048 +0.357418 +0.000040 +0.000800 +0.000061 +-0.002407 +0.000068 + + +-0.0227755 +0.0000138 +-0.0014055 +0.0000213 + + +0.000438 +0.000077 +-0.000118 +0.000087 + + + + + + + +0.303776 +0.000048 +0.354937 +0.000040 +0.000723 +0.000061 +-0.002500 +0.000068 + + +-0.0213685 +0.0000122 +-0.0014091 +0.0000213 + + +0.000371 +0.000072 +-0.000145 +0.000080 + + + + + + + +0.304477 +0.000048 +0.352466 +0.000040 +0.000596 +0.000061 +-0.002491 +0.000067 + + +-0.0199794 +0.0000089 +-0.0013717 +0.0000211 + + +0.000230 +0.000062 +-0.000078 +0.000068 + + + + + + + +0.304896 +0.000048 +0.349928 +0.000039 +0.000332 +0.000061 +-0.002530 +0.000067 + + +-0.0186340 +0.0000070 +-0.0013071 +0.0000211 + + +0.000193 +0.000059 +-0.000067 +0.000064 + + + + + + + +0.305196 +0.000048 +0.347449 +0.000039 +0.000235 +0.000060 +-0.002489 +0.000067 + + +-0.0173800 +0.0000056 +-0.0011874 +0.0000211 + + +0.000230 +0.000057 +-0.000099 +0.000061 + + + + + + + +0.305359 +0.000047 +0.344911 +0.000039 +0.000058 +0.000060 +-0.002555 +0.000067 + + +-0.0162763 +0.0000046 +-0.0009989 +0.0000208 + + +0.000296 +0.000055 +-0.000145 +0.000057 + + + + + + + +0.305275 +0.000048 +0.342353 +0.000039 +-0.000198 +0.000060 +-0.002525 +0.000067 + + +-0.0154056 +0.0000044 +-0.0007605 +0.0000210 + + +0.000318 +0.000054 +-0.000171 +0.000056 + + + + + + + +0.304974 +0.000048 +0.339904 +0.000039 +-0.000350 +0.000060 +-0.002450 +0.000067 + + +-0.0147410 +0.0000044 +-0.0005814 +0.0000215 + + +0.000312 +0.000053 +-0.000183 +0.000056 + + + + + + + +0.304634 +0.000048 +0.337396 +0.000039 +-0.000283 +0.000060 +-0.002486 +0.000067 + + +-0.0142138 +0.0000041 +-0.0004577 +0.0000215 + + +0.000300 +0.000051 +-0.000188 +0.000055 + + + + + + + +0.304486 +0.000048 +0.334985 +0.000039 +-0.000026 +0.000060 +-0.002339 +0.000067 + + +-0.0138184 +0.0000039 +-0.0003166 +0.0000212 + + +0.000287 +0.000050 +-0.000186 +0.000055 + + + + + + + +0.304608 +0.000047 +0.332741 +0.000039 +0.000173 +0.000060 +-0.002258 +0.000067 + + +-0.0135885 +0.0000042 +-0.0001775 +0.0000210 + + +0.000284 +0.000053 +-0.000170 +0.000058 + + + + + + + +0.304750 +0.000048 +0.330370 +0.000039 +0.000018 +0.000060 +-0.002505 +0.000067 + + +-0.0134327 +0.0000049 +-0.0001600 +0.0000208 + + +0.000286 +0.000056 +-0.000152 +0.000064 + + + + + + + +0.304511 +0.000047 +0.327662 +0.000039 +-0.000321 +0.000060 +-0.002731 +0.000067 + + +-0.0132321 +0.0000059 +-0.0002555 +0.0000205 + + +0.000294 +0.000059 +-0.000139 +0.000069 + + + + + + + +0.304218 +0.000048 +0.325053 +0.000040 +-0.000246 +0.000060 +-0.002488 +0.000068 + + +-0.0128989 +0.0000073 +-0.0004245 +0.0000206 + + +0.000315 +0.000067 +-0.000140 +0.000082 + + + + + + + +0.304093 +0.000048 +0.322757 +0.000040 +-0.000157 +0.000061 +-0.002243 +0.000068 + + +-0.0123719 +0.0000083 +-0.0006116 +0.0000208 + + +0.000332 +0.000078 +-0.000156 +0.000100 + + + + + + + +0.303789 +0.000048 +0.320462 +0.000040 +-0.000422 +0.000061 +-0.002370 +0.000068 + + +-0.0117027 +0.0000072 +-0.0007044 +0.0000207 + + +0.000316 +0.000074 +-0.000197 +0.000094 + + + + + + + +0.303221 +0.000048 +0.317942 +0.000040 +-0.000626 +0.000061 +-0.002716 +0.000069 + + +-0.0110105 +0.0000058 +-0.0006749 +0.0000201 + + +0.000288 +0.000064 +-0.000241 +0.000077 + + + + + + + +0.302611 +0.000048 +0.314946 +0.000041 +-0.000713 +0.000062 +-0.003136 +0.000070 + + +-0.0103848 +0.0000049 +-0.0005476 +0.0000196 + + +0.000315 +0.000060 +-0.000218 +0.000071 + + + + + + + +0.301714 +0.000049 +0.311767 +0.000041 +-0.001104 +0.000062 +-0.003178 +0.000071 + + +-0.0099522 +0.0000042 +-0.0003033 +0.0000196 + + +0.000375 +0.000058 +-0.000153 +0.000066 + + + + + + + +0.300337 +0.000050 +0.308684 +0.000042 +-0.001390 +0.000063 +-0.003044 +0.000073 + + +-0.0097938 +0.0000038 +-0.0000019 +0.0000201 + + +0.000440 +0.000055 +-0.000076 +0.000061 + + + + + + + +0.299163 +0.000051 +0.305670 +0.000044 +-0.000950 +0.000065 +-0.002933 +0.000076 + + +-0.0099473 +0.0000036 +0.0002760 +0.0000212 + + +0.000446 +0.000054 +-0.000060 +0.000059 + + + + + + + +0.298558 +0.000054 +0.302866 +0.000047 +-0.000460 +0.000068 +-0.002691 +0.000081 + + +-0.0102963 +0.0000034 +0.0003843 +0.0000225 + + +0.000414 +0.000053 +-0.000082 +0.000058 + + + + + + + +0.298102 +0.000056 +0.300294 +0.000049 +-0.000467 +0.000072 +-0.002462 +0.000085 + + +-0.0106474 +0.0000031 +0.0003126 +0.0000231 + + +0.000372 +0.000051 +-0.000113 +0.000056 + + + + + + + +0.297539 +0.000056 +0.297936 +0.000050 +-0.000581 +0.000074 +-0.002241 +0.000087 + + +-0.0108862 +0.0000028 +0.0001684 +0.0000228 + + +0.000332 +0.000050 +-0.000142 +0.000055 + + + + + + + +0.296976 +0.000057 +0.295821 +0.000050 +-0.000592 +0.000074 +-0.002044 +0.000089 + + +-0.0109810 +0.0000028 +-0.0000023 +0.0000227 + + +0.000359 +0.000052 +-0.000112 +0.000059 + + + + + + + +0.296327 +0.000057 +0.293799 +0.000051 +-0.000691 +0.000074 +-0.002036 +0.000089 + + +-0.0108687 +0.0000030 +-0.0002247 +0.0000231 + + +0.000385 +0.000055 +-0.000080 +0.000064 + + + + + + + +0.295597 +0.000058 +0.291688 +0.000051 +-0.000792 +0.000073 +-0.002112 +0.000090 + + +-0.0105332 +0.0000032 +-0.0004360 +0.0000241 + + +0.000366 +0.000058 +-0.000081 +0.000068 + + + + + + + +0.294720 +0.000059 +0.289620 +0.000051 +-0.000995 +0.000071 +-0.001989 +0.000091 + + +-0.0100150 +0.0000039 +-0.0006069 +0.0000263 + + +0.000193 +0.000064 +-0.000193 +0.000080 + + + + + + + +0.293563 +0.000060 +0.287766 +0.000051 +-0.001241 +0.000071 +-0.001845 +0.000091 + + +-0.0093265 +0.0000047 +-0.0007703 +0.0000280 + + +0.000029 +0.000073 +-0.000311 +0.000096 + + + + + + + +0.292296 +0.000060 +0.285831 +0.000050 +-0.001179 +0.000070 +-0.002073 +0.000088 + + +-0.0084802 +0.0000046 +-0.0008960 +0.0000282 + + +0.000116 +0.000071 +-0.000291 +0.000091 + + + + + + + +0.291347 +0.000060 +0.283521 +0.000049 +-0.000812 +0.000071 +-0.002425 +0.000086 + + +-0.0075644 +0.0000040 +-0.0009053 +0.0000270 + + +0.000350 +0.000062 +-0.000176 +0.000075 + + + + + + + +0.290649 +0.000059 +0.281053 +0.000049 +-0.000721 +0.000071 +-0.002567 +0.000084 + + +-0.0067135 +0.0000037 +-0.0007957 +0.0000258 + + +0.000435 +0.000059 +-0.000082 +0.000069 + + + + + + + +0.289760 +0.000058 +0.278368 +0.000050 +-0.001005 +0.000071 +-0.002762 +0.000083 + + +-0.0059928 +0.0000035 +-0.0006384 +0.0000252 + + +0.000407 +0.000056 +-0.000014 +0.000064 + + + + + + + +0.288617 +0.000058 +0.275562 +0.000051 +-0.001255 +0.000071 +-0.002766 +0.000084 + + +-0.0054487 +0.0000035 +-0.0004407 +0.0000250 + + +0.000330 +0.000054 +0.000028 +0.000059 + + + + + + + +0.287264 +0.000059 +0.272936 +0.000052 +-0.001369 +0.000071 +-0.002555 +0.000085 + + +-0.0051195 +0.0000038 +-0.0002234 +0.0000255 + + +0.000295 +0.000052 +-0.000014 +0.000058 + + + + + + + +0.285969 +0.000060 +0.270435 +0.000053 +-0.001118 +0.000070 +-0.002395 +0.000086 + + +-0.0049910 +0.0000044 +-0.0000441 +0.0000263 + + +0.000286 +0.000051 +-0.000101 +0.000057 + + + + + + + +0.285175 +0.000060 +0.268189 +0.000053 +-0.000603 +0.000069 +-0.002046 +0.000087 + + +-0.0050055 +0.0000049 +0.0000741 +0.0000263 + + +0.000277 +0.000050 +-0.000189 +0.000056 + + + + + + + +0.284705 +0.000060 +0.266417 +0.000052 +-0.000524 +0.000069 +-0.001601 +0.000089 + + +-0.0051191 +0.0000055 +0.0001511 +0.0000256 + + +0.000267 +0.000049 +-0.000254 +0.000055 + + + + + + + +0.283912 +0.000060 +0.264922 +0.000052 +-0.000876 +0.000069 +-0.001496 +0.000089 + + +-0.0052929 +0.0000070 +0.0001599 +0.0000251 + + +0.000239 +0.000047 +-0.000173 +0.000054 + + + + + + + +0.283031 +0.000060 +0.263286 +0.000051 +-0.000840 +0.000070 +-0.001707 +0.000089 + + +-0.0053993 +0.0000094 +0.0000340 +0.0000251 + + +0.000208 +0.000045 +-0.000061 +0.000054 + + + + + + + +0.282318 +0.000060 +0.261508 +0.000051 +-0.000769 +0.000070 +-0.001795 +0.000088 + + +-0.0053333 +0.0000122 +-0.0001887 +0.0000256 + + +0.000187 +0.000043 +0.000010 +0.000054 + + + + + + + +0.281354 +0.000060 +0.259750 +0.000050 +-0.001124 +0.000071 +-0.001787 +0.000087 + + +-0.0050003 +0.0000158 +-0.0004119 +0.0000274 + + +0.000183 +0.000050 +-0.000059 +0.000069 + + + + + + + +0.280036 +0.000060 +0.257894 +0.000050 +-0.001470 +0.000073 +-0.001968 +0.000087 + + +-0.0045789 +0.0000206 +-0.0004560 +0.0000294 + + +0.000200 +0.000061 +-0.000183 +0.000091 + + + + + + + +0.278437 +0.000061 +0.255752 +0.000051 +-0.001750 +0.000076 +-0.002214 +0.000086 + + +-0.0041088 +0.0000260 +-0.0004826 +0.0000296 + + +0.000264 +0.000058 +-0.000232 +0.000086 + + + + + + + +0.276527 +0.000061 +0.253535 +0.000052 +-0.002033 +0.000079 +-0.002191 +0.000085 + + +-0.0036291 +0.0000258 +-0.0004476 +0.0000284 + + +0.000353 +0.000047 +-0.000213 +0.000064 + + + + + + + +0.274404 +0.000062 +0.251433 +0.000052 +-0.002094 +0.000081 +-0.002120 +0.000085 + + +-0.0032454 +0.0000211 +-0.0002887 +0.0000267 + + +0.000426 +0.000037 +-0.000172 +0.000044 + + + + + + + +0.272473 +0.000062 +0.249219 +0.000052 +-0.001779 +0.000082 +-0.002327 +0.000086 + + +-0.0030859 +0.0000180 +-0.0000286 +0.0000254 + + +0.000428 +0.000034 +-0.000147 +0.000038 + + + + + + + +0.270900 +0.000061 +0.246724 +0.000052 +-0.001492 +0.000083 +-0.002518 +0.000087 + + +-0.0031898 +0.0000166 +0.0002339 +0.0000246 + + +0.000393 +0.000031 +-0.000128 +0.000032 + + + + + + + +0.269391 +0.000060 +0.244299 +0.000053 +-0.001649 +0.000082 +-0.002329 +0.000087 + + +-0.0035353 +0.0000174 +0.0004481 +0.0000248 + + +0.000357 +0.000030 +-0.000114 +0.000031 + + + + + + + +0.267429 +0.000060 +0.242127 +0.000054 +-0.002066 +0.000081 +-0.002122 +0.000087 + + +-0.0040573 +0.0000200 +0.0005781 +0.0000253 + + +0.000326 +0.000031 +-0.000105 +0.000032 + + + + + + + +0.265385 +0.000060 +0.239978 +0.000055 +-0.001937 +0.000079 +-0.002139 +0.000084 + + +-0.0046579 +0.0000228 +0.0005882 +0.0000254 + + +0.000299 +0.000031 +-0.000096 +0.000033 + + + + + + + +0.263701 +0.000060 +0.237848 +0.000055 +-0.001623 +0.000078 +-0.002132 +0.000082 + + +-0.0051888 +0.0000203 +0.0004433 +0.0000250 + + +0.000281 +0.000032 +-0.000083 +0.000034 + + + + + + + +0.262018 +0.000060 +0.235704 +0.000055 +-0.001835 +0.000080 +-0.002077 +0.000080 + + +-0.0055034 +0.0000154 +0.0001940 +0.0000249 + + +0.000273 +0.000032 +-0.000064 +0.000035 + + + + + + + +0.259877 +0.000061 +0.233767 +0.000055 +-0.002470 +0.000082 +-0.001825 +0.000080 + + +-0.0055763 +0.0000123 +-0.0000390 +0.0000257 + + +0.000296 +0.000036 +-0.000033 +0.000041 + + + + + + + +0.256979 +0.000061 +0.232063 +0.000055 +-0.003038 +0.000084 +-0.001656 +0.000080 + + +-0.0054440 +0.0000105 +-0.0002106 +0.0000265 + + +0.000317 +0.000040 +0.000004 +0.000049 + + + + + + + +0.254040 +0.000061 +0.230385 +0.000054 +-0.002922 +0.000087 +-0.001623 +0.000080 + + +-0.0051858 +0.0000099 +-0.0002885 +0.0000274 + + +0.000306 +0.000049 +0.000064 +0.000066 + + + + + + + +0.251171 +0.000062 +0.228858 +0.000053 +-0.002936 +0.000088 +-0.001540 +0.000080 + + +-0.0049033 +0.0000096 +-0.0002666 +0.0000280 + + +0.000287 +0.000060 +0.000105 +0.000089 + + + + + + + +0.248065 +0.000064 +0.227218 +0.000051 +-0.003073 +0.000087 +-0.001808 +0.000081 + + +-0.0046818 +0.0000088 +-0.0001737 +0.0000277 + + +0.000304 +0.000056 +0.000045 +0.000081 + + + + + + + +0.245180 +0.000065 +0.225131 +0.000049 +-0.002618 +0.000085 +-0.002185 +0.000083 + + +-0.0045721 +0.0000078 +-0.0000437 +0.0000266 + + +0.000345 +0.000044 +-0.000074 +0.000058 + + + + + + + +0.242984 +0.000065 +0.222972 +0.000049 +-0.002117 +0.000084 +-0.002058 +0.000084 + + +-0.0046016 +0.0000075 +0.0001058 +0.0000261 + + +0.000376 +0.000040 +-0.000142 +0.000049 + + + + + + + +0.240680 +0.000066 +0.221154 +0.000049 +-0.002549 +0.000083 +-0.001765 +0.000084 + + +-0.0047862 +0.0000077 +0.0002714 +0.0000268 + + +0.000394 +0.000036 +-0.000168 +0.000041 + + + + + + + +0.237696 +0.000069 +0.219324 +0.000049 +-0.003114 +0.000082 +-0.001811 +0.000084 + + +-0.0051477 +0.0000080 +0.0004618 +0.0000280 + + +0.000402 +0.000031 +-0.000171 +0.000033 + + + + + + + +0.234662 +0.000073 +0.217555 +0.000049 +-0.002899 +0.000081 +-0.001670 +0.000085 + + +-0.0057141 +0.0000088 +0.0006740 +0.0000298 + + +0.000383 +0.000030 +-0.000168 +0.000031 + + + + + + + +0.232058 +0.000076 +0.216051 +0.000048 +-0.002334 +0.000079 +-0.001427 +0.000085 + + +-0.0064912 +0.0000095 +0.0008691 +0.0000312 + + +0.000348 +0.000029 +-0.000158 +0.000029 + + + + + + + +0.230051 +0.000075 +0.214646 +0.000048 +-0.001874 +0.000079 +-0.001403 +0.000084 + + +-0.0074290 +0.0000093 +0.0009786 +0.0000303 + + +0.000310 +0.000028 +-0.000140 +0.000027 + + + + + + + +0.228143 +0.000072 +0.213195 +0.000049 +-0.001963 +0.000079 +-0.001455 +0.000085 + + +-0.0084019 +0.0000088 +0.0009389 +0.0000281 + + +0.000279 +0.000028 +-0.000115 +0.000025 + + + + + + + +0.226020 +0.000071 +0.211756 +0.000050 +-0.002261 +0.000079 +-0.001421 +0.000087 + + +-0.0092552 +0.0000092 +0.0007559 +0.0000268 + + +0.000270 +0.000028 +-0.000081 +0.000025 + + + + + + + +0.223591 +0.000071 +0.210365 +0.000052 +-0.002608 +0.000079 +-0.001424 +0.000088 + + +-0.0098850 +0.0000102 +0.0005036 +0.0000268 + + +0.000272 +0.000028 +-0.000049 +0.000026 + + + + + + + +0.220778 +0.000070 +0.208851 +0.000054 +-0.002969 +0.000079 +-0.001500 +0.000088 + + +-0.0102641 +0.0000117 +0.0002393 +0.0000285 + + +0.000275 +0.000028 +-0.000055 +0.000026 + + + + + + + +0.217688 +0.000070 +0.207438 +0.000055 +-0.003169 +0.000079 +-0.001314 +0.000088 + + +-0.0103672 +0.0000127 +-0.0000044 +0.0000306 + + +0.000279 +0.000029 +-0.000092 +0.000027 + + + + + + + +0.214501 +0.000069 +0.206273 +0.000055 +-0.003175 +0.000079 +-0.001089 +0.000089 + + +-0.0102993 +0.0000123 +-0.0001088 +0.0000318 + + +0.000285 +0.000029 +-0.000140 +0.000027 + + + + + + + +0.211397 +0.000067 +0.205213 +0.000054 +-0.002995 +0.000079 +-0.001007 +0.000092 + + +-0.0101990 +0.0000111 +-0.0000749 +0.0000316 + + +0.000291 +0.000030 +-0.000188 +0.000028 + + + + + + + +0.208579 +0.000064 +0.204261 +0.000053 +-0.002627 +0.000081 +-0.000891 +0.000095 + + +-0.0101864 +0.0000101 +0.0000922 +0.0000304 + + +0.000299 +0.000030 +-0.000226 +0.000029 + + + + + + + +0.206191 +0.000064 +0.203437 +0.000054 +-0.002293 +0.000083 +-0.000878 +0.000097 + + +-0.0104323 +0.0000098 +0.0004001 +0.0000295 + + +0.000298 +0.000032 +-0.000190 +0.000031 + + + + + + + +0.203873 +0.000065 +0.202388 +0.000055 +-0.002320 +0.000086 +-0.001071 +0.000097 + + +-0.0109960 +0.0000099 +0.0007120 +0.0000288 + + +0.000301 +0.000034 +-0.000148 +0.000035 + + + + + + + +0.201513 +0.000066 +0.201387 +0.000055 +-0.002260 +0.000086 +-0.000905 +0.000095 + + +-0.0118312 +0.0000103 +0.0009407 +0.0000277 + + +0.000317 +0.000036 +-0.000133 +0.000038 + + + + + + + +0.199474 +0.000068 +0.200651 +0.000054 +-0.001819 +0.000084 +-0.000727 +0.000094 + + +-0.0128366 +0.0000112 +0.0010288 +0.0000270 + + +0.000375 +0.000042 +-0.000221 +0.000046 + + + + + + + +0.197932 +0.000069 +0.199809 +0.000053 +-0.001454 +0.000081 +-0.000842 +0.000093 + + +-0.0138232 +0.0000117 +0.0009449 +0.0000271 + + +0.000422 +0.000048 +-0.000302 +0.000055 + + + + + + + +0.196406 +0.000068 +0.199019 +0.000052 +-0.001619 +0.000078 +-0.000737 +0.000093 + + +-0.0146971 +0.0000111 +0.0008084 +0.0000274 + + +0.000367 +0.000046 +-0.000211 +0.000051 + + + + + + + +0.194594 +0.000066 +0.198363 +0.000052 +-0.001911 +0.000076 +-0.000678 +0.000095 + + +-0.0154390 +0.0000102 +0.0006454 +0.0000279 + + +0.000266 +0.000041 +-0.000037 +0.000041 + + + + + + + +0.192626 +0.000064 +0.197574 +0.000052 +-0.002101 +0.000075 +-0.000834 +0.000097 + + +-0.0159681 +0.0000101 +0.0004043 +0.0000289 + + +0.000242 +0.000042 +0.000023 +0.000040 + + + + + + + +0.190335 +0.000063 +0.196714 +0.000053 +-0.002414 +0.000076 +-0.000861 +0.000096 + + +-0.0162396 +0.0000105 +0.0001380 +0.0000297 + + +0.000270 +0.000046 +0.000001 +0.000040 + + + + + + + +0.187835 +0.000064 +0.195885 +0.000053 +-0.002510 +0.000077 +-0.000837 +0.000093 + + +-0.0162474 +0.0000112 +-0.0000989 +0.0000299 + + +0.000284 +0.000046 +-0.000019 +0.000039 + + + + + + + +0.185411 +0.000064 +0.195017 +0.000054 +-0.002302 +0.000077 +-0.000941 +0.000091 + + +-0.0160721 +0.0000119 +-0.0002360 +0.0000297 + + +0.000283 +0.000042 +-0.000031 +0.000036 + + + + + + + +0.183314 +0.000063 +0.193950 +0.000057 +-0.001813 +0.000077 +-0.001112 +0.000090 + + +-0.0158089 +0.0000123 +-0.0002800 +0.0000288 + + +0.000277 +0.000037 +-0.000046 +0.000032 + + + + + + + +0.181906 +0.000062 +0.192849 +0.000060 +-0.001077 +0.000076 +-0.000946 +0.000091 + + +-0.0155390 +0.0000114 +-0.0002410 +0.0000274 + + +0.000271 +0.000031 +-0.000064 +0.000028 + + + + + + + +0.181146 +0.000062 +0.192231 +0.000061 +-0.000587 +0.000076 +-0.000372 +0.000093 + + +-0.0153565 +0.0000099 +-0.0001163 +0.0000262 + + +0.000266 +0.000028 +-0.000085 +0.000025 + + + + + + + +0.180582 +0.000063 +0.192108 +0.000061 +-0.000597 +0.000076 +0.000054 +0.000094 + + +-0.0153247 +0.0000093 +0.0000593 +0.0000261 + + +0.000275 +0.000028 +-0.000114 +0.000025 + + + + + + + +0.179821 +0.000064 +0.192267 +0.000060 +-0.000980 +0.000078 +0.000272 +0.000094 + + +-0.0154855 +0.0000092 +0.0002632 +0.0000271 + + +0.000286 +0.000028 +-0.000142 +0.000025 + + + + + + + +0.178503 +0.000066 +0.192627 +0.000058 +-0.001587 +0.000080 +0.000368 +0.000094 + + +-0.0158513 +0.0000096 +0.0004662 +0.0000279 + + +0.000293 +0.000027 +-0.000165 +0.000025 + + + + + + + +0.176657 +0.000067 +0.192909 +0.000056 +-0.001984 +0.000084 +0.000172 +0.000096 + + +-0.0164084 +0.0000108 +0.0006421 +0.0000288 + + +0.000288 +0.000028 +-0.000169 +0.000025 + + + + + + + +0.174660 +0.000067 +0.192899 +0.000055 +-0.002081 +0.000087 +-0.000118 +0.000099 + + +-0.0171161 +0.0000123 +0.0007606 +0.0000298 + + +0.000274 +0.000028 +-0.000160 +0.000025 + + + + + + + +0.172485 +0.000067 +0.192709 +0.000053 +-0.002263 +0.000090 +-0.000226 +0.000103 + + +-0.0178983 +0.0000113 +0.0007898 +0.0000297 + + +0.000258 +0.000028 +-0.000146 +0.000026 + + + + + + + +0.170136 +0.000067 +0.192501 +0.000053 +-0.002343 +0.000092 +-0.000273 +0.000107 + + +-0.0186610 +0.0000093 +0.0007137 +0.0000288 + + +0.000243 +0.000028 +-0.000131 +0.000026 + + + + + + + +0.167891 +0.000069 +0.192107 +0.000052 +-0.002184 +0.000094 +-0.000443 +0.000111 + + +-0.0192899 +0.0000083 +0.0005251 +0.0000283 + + +0.000245 +0.000030 +-0.000130 +0.000029 + + + + + + + +0.165780 +0.000071 +0.191660 +0.000052 +-0.002200 +0.000095 +-0.000393 +0.000111 + + +-0.0196873 +0.0000078 +0.0002644 +0.0000280 + + +0.000248 +0.000034 +-0.000130 +0.000033 + + + + + + + +0.163336 +0.000072 +0.191401 +0.000051 +-0.002677 +0.000095 +-0.000227 +0.000108 + + +-0.0198207 +0.0000076 +0.0000096 +0.0000278 + + +0.000239 +0.000036 +-0.000120 +0.000036 + + + + + + + +0.160359 +0.000072 +0.191143 +0.000049 +-0.003216 +0.000095 +-0.000295 +0.000104 + + +-0.0197331 +0.0000080 +-0.0001702 +0.0000284 + + +0.000191 +0.000044 +-0.000086 +0.000045 + + + + + + + +0.156930 +0.000072 +0.190773 +0.000049 +-0.003579 +0.000096 +-0.000440 +0.000100 + + +-0.0195182 +0.0000087 +-0.0002347 +0.0000290 + + +0.000142 +0.000053 +-0.000044 +0.000057 + + + + + + + +0.153279 +0.000073 +0.190248 +0.000049 +-0.003572 +0.000096 +-0.000528 +0.000096 + + +-0.0193049 +0.0000093 +-0.0001665 +0.0000291 + + +0.000164 +0.000051 +-0.000019 +0.000054 + + + + + + + +0.149976 +0.000071 +0.189791 +0.000051 +-0.003129 +0.000095 +-0.000290 +0.000096 + + +-0.0192183 +0.0000095 +0.0000025 +0.0000283 + + +0.000234 +0.000043 +-0.000014 +0.000043 + + + + + + + +0.147020 +0.000068 +0.189802 +0.000053 +-0.002895 +0.000093 +0.000167 +0.000096 + + +-0.0193183 +0.0000097 +0.0002032 +0.0000273 + + +0.000301 +0.000034 +-0.000027 +0.000033 + + + + + + + +0.144073 +0.000064 +0.190046 +0.000053 +-0.002941 +0.000092 +0.000167 +0.000098 + + +-0.0196188 +0.0000108 +0.0003736 +0.0000273 + + +0.000300 +0.000031 +-0.000068 +0.000029 + + + + + + + +0.141140 +0.000064 +0.189944 +0.000053 +-0.002842 +0.000091 +-0.000224 +0.000101 + + +-0.0200282 +0.0000130 +0.0004297 +0.0000273 + + +0.000273 +0.000028 +-0.000118 +0.000026 + + + + + + + +0.138473 +0.000065 +0.189650 +0.000054 +-0.002673 +0.000091 +-0.000275 +0.000104 + + +-0.0204411 +0.0000165 +0.0003958 +0.0000274 + + +0.000277 +0.000028 +-0.000150 +0.000025 + + + + + + + +0.135655 +0.000066 +0.189510 +0.000056 +-0.002880 +0.000092 +-0.000202 +0.000110 + + +-0.0208038 +0.0000189 +0.0003048 +0.0000274 + + +0.000307 +0.000027 +-0.000163 +0.000025 + + + + + + + +0.132727 +0.000068 +0.189106 +0.000058 +-0.002800 +0.000091 +-0.000411 +0.000117 + + +-0.0210244 +0.0000164 +0.0001304 +0.0000269 + + +0.000341 +0.000027 +-0.000164 +0.000024 + + + + + + + +0.130238 +0.000071 +0.188816 +0.000059 +-0.002382 +0.000089 +-0.000177 +0.000126 + + +-0.0210542 +0.0000132 +-0.0000757 +0.0000266 + + +0.000367 +0.000027 +-0.000153 +0.000024 + + + + + + + +0.127852 +0.000076 +0.188806 +0.000058 +-0.002346 +0.000087 +0.000009 +0.000136 + + +-0.0208721 +0.0000112 +-0.0002859 +0.0000269 + + +0.000344 +0.000027 +-0.000125 +0.000024 + + + + + + + +0.125533 +0.000081 +0.188712 +0.000058 +-0.002388 +0.000087 +-0.000024 +0.000144 + + +-0.0204924 +0.0000096 +-0.0004655 +0.0000271 + + +0.000303 +0.000027 +-0.000093 +0.000024 + + + + + + + +0.122973 +0.000081 +0.188871 +0.000058 +-0.002734 +0.000086 +0.000264 +0.000146 + + +-0.0199601 +0.0000085 +-0.0005906 +0.0000277 + + +0.000261 +0.000027 +-0.000068 +0.000024 + + + + + + + +0.120012 +0.000076 +0.189217 +0.000057 +-0.003004 +0.000084 +0.000327 +0.000138 + + +-0.0193329 +0.0000085 +-0.0006488 +0.0000292 + + +0.000238 +0.000028 +-0.000088 +0.000025 + + + + + + + +0.117124 +0.000072 +0.189412 +0.000058 +-0.002775 +0.000082 +0.000082 +0.000127 + + +-0.0186891 +0.0000089 +-0.0006255 +0.0000301 + + +0.000230 +0.000029 +-0.000132 +0.000027 + + + + + + + +0.114539 +0.000069 +0.189345 +0.000058 +-0.002439 +0.000081 +-0.000075 +0.000118 + + +-0.0181068 +0.0000087 +-0.0005350 +0.0000285 + + +0.000235 +0.000031 +-0.000178 +0.000028 + + + + + + + +0.112243 +0.000067 +0.189383 +0.000059 +-0.002210 +0.000079 +0.000172 +0.000114 + + +-0.0176316 +0.0000081 +-0.0004096 +0.0000254 + + +0.000251 +0.000032 +-0.000212 +0.000029 + + + + + + + +0.110058 +0.000068 +0.189769 +0.000059 +-0.002194 +0.000078 +0.000596 +0.000113 + + +-0.0172943 +0.0000075 +-0.0002654 +0.0000229 + + +0.000300 +0.000036 +-0.000213 +0.000033 + + + + + + + +0.107791 +0.000068 +0.190612 +0.000057 +-0.002365 +0.000078 +0.000951 +0.000113 + + +-0.0170979 +0.0000071 +-0.0001210 +0.0000218 + + +0.000333 +0.000040 +-0.000190 +0.000037 + + + + + + + +0.105271 +0.000068 +0.191552 +0.000056 +-0.002601 +0.000078 +0.000835 +0.000112 + + +-0.0170512 +0.0000073 +0.0000316 +0.0000220 + + +0.000264 +0.000049 +-0.000106 +0.000047 + + + + + + + +0.102635 +0.000067 +0.192130 +0.000055 +-0.002574 +0.000079 +0.000435 +0.000112 + + +-0.0171579 +0.0000079 +0.0001739 +0.0000226 + + +0.000185 +0.000060 +-0.000044 +0.000058 + + + + + + + +0.100241 +0.000065 +0.192460 +0.000054 +-0.002206 +0.000079 +0.000282 +0.000114 + + +-0.0173822 +0.0000081 +0.0002681 +0.0000231 + + +0.000237 +0.000063 +-0.000125 +0.000060 + + + + + + + +0.098288 +0.000065 +0.192768 +0.000053 +-0.001737 +0.000079 +0.000302 +0.000115 + + +-0.0176727 +0.0000082 +0.0002984 +0.0000233 + + +0.000278 +0.000057 +-0.000157 +0.000053 + + + + + + + +0.096763 +0.000066 +0.193071 +0.000054 +-0.001278 +0.000080 +0.000305 +0.000119 + + +-0.0179522 +0.0000086 +0.0002389 +0.0000233 + + +0.000255 +0.000045 +-0.000091 +0.000042 + + + + + + + +0.095763 +0.000070 +0.193384 +0.000056 +-0.000905 +0.000082 +0.000411 +0.000124 + + +-0.0181223 +0.0000099 +0.0000866 +0.0000236 + + +0.000231 +0.000039 +-0.000069 +0.000037 + + + + + + + +0.094785 +0.000075 +0.193985 +0.000058 +-0.001123 +0.000085 +0.000769 +0.000128 + + +-0.0181128 +0.0000122 +-0.0001117 +0.0000248 + + +0.000210 +0.000034 +-0.000081 +0.000032 + + + + + + + +0.093362 +0.000079 +0.194945 +0.000058 +-0.001708 +0.000087 +0.001082 +0.000129 + + +-0.0179066 +0.0000162 +-0.0002853 +0.0000269 + + +0.000191 +0.000033 +-0.000100 +0.000030 + + + + + + + +0.091305 +0.000082 +0.196091 +0.000055 +-0.002443 +0.000086 +0.001089 +0.000125 + + +-0.0175776 +0.0000197 +-0.0003346 +0.0000285 + + +0.000178 +0.000033 +-0.000123 +0.000030 + + + + + + + +0.088406 +0.000081 +0.196974 +0.000053 +-0.003126 +0.000085 +0.000678 +0.000118 + + +-0.0172973 +0.0000180 +-0.0001965 +0.0000288 + + +0.000169 +0.000032 +-0.000146 +0.000030 + + + + + + + +0.085247 +0.000077 +0.197375 +0.000051 +-0.003239 +0.000084 +0.000327 +0.000112 + + +-0.0172338 +0.0000149 +0.0000738 +0.0000287 + + +0.000168 +0.000032 +-0.000168 +0.000029 + + + + + + + +0.081980 +0.000072 +0.197797 +0.000049 +-0.003433 +0.000083 +0.000447 +0.000109 + + +-0.0174543 +0.0000126 +0.0003522 +0.0000286 + + +0.000174 +0.000032 +-0.000184 +0.000029 + + + + + + + +0.078268 +0.000069 +0.198281 +0.000049 +-0.003737 +0.000082 +0.000443 +0.000106 + + +-0.0179078 +0.0000111 +0.0005273 +0.0000281 + + +0.000190 +0.000031 +-0.000191 +0.000029 + + + + + + + +0.074706 +0.000068 +0.198611 +0.000050 +-0.003528 +0.000082 +0.000398 +0.000103 + + +-0.0184525 +0.0000105 +0.0005628 +0.0000273 + + +0.000225 +0.000031 +-0.000180 +0.000028 + + + + + + + +0.071168 +0.000067 +0.199220 +0.000051 +-0.003741 +0.000082 +0.000744 +0.000101 + + +-0.0190020 +0.0000107 +0.0005361 +0.0000263 + + +0.000261 +0.000030 +-0.000161 +0.000028 + + + + + + + +0.067009 +0.000068 +0.200094 +0.000053 +-0.004286 +0.000082 +0.000906 +0.000099 + + +-0.0195134 +0.0000122 +0.0004430 +0.0000258 + + +0.000243 +0.000030 +-0.000095 +0.000028 + + + + + + + +0.062781 +0.000070 +0.200934 +0.000054 +-0.004076 +0.000081 +0.000827 +0.000097 + + +-0.0198475 +0.0000136 +0.0002181 +0.0000256 + + +0.000190 +0.000029 +-0.000005 +0.000027 + + + + + + + +0.059045 +0.000072 +0.201751 +0.000055 +-0.003512 +0.000081 +0.000774 +0.000096 + + +-0.0199319 +0.0000133 +-0.0000281 +0.0000251 + + +0.000140 +0.000029 +0.000064 +0.000027 + + + + + + + +0.055733 +0.000072 +0.202451 +0.000056 +-0.003061 +0.000084 +0.000638 +0.000096 + + +-0.0198120 +0.0000128 +-0.0002214 +0.0000242 + + +0.000119 +0.000028 +0.000081 +0.000027 + + + + + + + +0.052963 +0.000071 +0.203022 +0.000058 +-0.002485 +0.000088 +0.000639 +0.000098 + + +-0.0194966 +0.0000131 +-0.0004025 +0.0000233 + + +0.000150 +0.000028 +0.000013 +0.000027 + + + + + + + +0.050776 +0.000070 +0.203862 +0.000059 +-0.002021 +0.000091 +0.000993 +0.000102 + + +-0.0190229 +0.0000148 +-0.0005254 +0.0000232 + + +0.000270 +0.000029 +-0.000186 +0.000029 + + + + + + + +0.048799 +0.000072 +0.205028 +0.000058 +-0.001915 +0.000089 +0.001230 +0.000106 + + +-0.0184769 +0.0000175 +-0.0005576 +0.0000240 + + +0.000360 +0.000029 +-0.000326 +0.000029 + + + + + + + +0.046903 +0.000074 +0.206221 +0.000056 +-0.001843 +0.000084 +0.001255 +0.000111 + + +-0.0179339 +0.0000204 +-0.0005183 +0.0000250 + + +0.000390 +0.000029 +-0.000362 +0.000028 + + + + + + + +0.045126 +0.000077 +0.207587 +0.000053 +-0.001759 +0.000079 +0.001424 +0.000118 + + +-0.0174629 +0.0000219 +-0.0004110 +0.0000254 + + +0.000382 +0.000028 +-0.000331 +0.000027 + + + + + + + +0.043343 +0.000081 +0.209042 +0.000051 +-0.001874 +0.000075 +0.001379 +0.000125 + + +-0.0171323 +0.0000199 +-0.0002524 +0.0000249 + + +0.000349 +0.000027 +-0.000260 +0.000026 + + + + + + + +0.041288 +0.000081 +0.210224 +0.000050 +-0.002192 +0.000073 +0.001036 +0.000129 + + +-0.0169604 +0.0000165 +-0.0000895 +0.0000238 + + +0.000305 +0.000027 +-0.000173 +0.000025 + + + + + + + +0.038959 +0.000079 +0.211103 +0.000050 +-0.002395 +0.000073 +0.000815 +0.000129 + + +-0.0169497 +0.0000142 +0.0000534 +0.0000230 + + +0.000282 +0.000027 +-0.000138 +0.000026 + + + + + + + +0.036569 +0.000076 +0.211941 +0.000050 +-0.002402 +0.000073 +0.000917 +0.000125 + + +-0.0170444 +0.0000124 +0.0001339 +0.0000230 + + +0.000264 +0.000028 +-0.000117 +0.000026 + + + + + + + +0.034173 +0.000072 +0.213038 +0.000051 +-0.002395 +0.000073 +0.001238 +0.000120 + + +-0.0171983 +0.0000112 +0.0001773 +0.0000233 + + +0.000246 +0.000028 +-0.000100 +0.000027 + + + + + + + +0.031784 +0.000068 +0.214430 +0.000051 +-0.002397 +0.000074 +0.001522 +0.000115 + + +-0.0173875 +0.0000112 +0.0001966 +0.0000242 + + +0.000226 +0.000028 +-0.000092 +0.000027 + + + + + + + +0.029367 +0.000064 +0.216061 +0.000053 +-0.002438 +0.000076 +0.001761 +0.000116 + + +-0.0175767 +0.0000118 +0.0001749 +0.0000251 + + +0.000208 +0.000028 +-0.000092 +0.000027 + + + + + + + +0.026901 +0.000062 +0.217965 +0.000056 +-0.002504 +0.000078 +0.002060 +0.000121 + + +-0.0177203 +0.0000120 +0.0000988 +0.0000252 + + +0.000198 +0.000028 +-0.000098 +0.000026 + + + + + + + +0.024347 +0.000062 +0.220203 +0.000060 +-0.002601 +0.000081 +0.002291 +0.000126 + + +-0.0177548 +0.0000122 +-0.0000485 +0.0000249 + + +0.000199 +0.000027 +-0.000109 +0.000026 + + + + + + + +0.021695 +0.000064 +0.222431 +0.000065 +-0.002638 +0.000083 +0.002081 +0.000129 + + +-0.0176052 +0.0000136 +-0.0002494 +0.0000252 + + +0.000236 +0.000029 +-0.000132 +0.000027 + + + + + + + +0.019134 +0.000069 +0.224221 +0.000071 +-0.002508 +0.000086 +0.001602 +0.000130 + + +-0.0172655 +0.0000162 +-0.0004267 +0.0000254 + + +0.000282 +0.000031 +-0.000157 +0.000029 + + + + + + + +0.016690 +0.000076 +0.225666 +0.000076 +-0.002517 +0.000087 +0.001297 +0.000130 + + +-0.0167782 +0.0000189 +-0.0005345 +0.0000256 + + +0.000319 +0.000032 +-0.000175 +0.000031 + + + + + + + +0.013968 +0.000083 +0.226839 +0.000077 +-0.002899 +0.000087 +0.001045 +0.000125 + + +-0.0162345 +0.0000208 +-0.0005543 +0.0000265 + + +0.000294 +0.000032 +-0.000158 +0.000031 + + + + + + + +0.010854 +0.000088 +0.227763 +0.000074 +-0.003160 +0.000086 +0.000944 +0.000117 + + +-0.0156846 +0.0000206 +-0.0004944 +0.0000276 + + +0.000234 +0.000031 +-0.000122 +0.000029 + + + + + + + +0.007798 +0.000087 +0.228872 +0.000070 +-0.002830 +0.000085 +0.001215 +0.000108 + + +-0.0152875 +0.0000173 +-0.0002553 +0.0000281 + + +0.000170 +0.000029 +-0.000087 +0.000027 + + + + + + + +0.005392 +0.000081 +0.230205 +0.000068 +-0.001995 +0.000086 +0.001367 +0.000101 + + +-0.0152158 +0.0000141 +0.0000661 +0.0000280 + + +0.000120 +0.000028 +-0.000064 +0.000026 + + + + + + + +0.003896 +0.000076 +0.231528 +0.000068 +-0.001192 +0.000088 +0.001508 +0.000096 + + +-0.0153738 +0.0000123 +0.0002410 +0.0000283 + + +0.000136 +0.000028 +-0.000093 +0.000026 + + + + + + + +0.002856 +0.000073 +0.233412 +0.000069 +-0.001058 +0.000092 +0.002168 +0.000094 + + +-0.0156563 +0.0000111 +0.0002925 +0.0000285 + + +0.000182 +0.000028 +-0.000140 +0.000025 + + + + + + + +0.001535 +0.000071 +0.235865 +0.000069 +-0.001318 +0.000097 +0.002492 +0.000092 + + +-0.0159088 +0.0000105 +0.0001905 +0.0000285 + + +0.000241 +0.000028 +-0.000189 +0.000025 + + + + + + + +0.000366 +0.000071 +0.238151 +0.000070 +-0.000970 +0.000100 +0.002246 +0.000092 + + +-0.0160008 +0.0000112 +0.0000009 +0.0000292 + + +0.000300 +0.000029 +-0.000208 +0.000025 + + + + + + + +-0.000284 +0.000071 +0.240398 +0.000072 +-0.000548 +0.000103 +0.002263 +0.000091 + + +-0.0159127 +0.0000127 +-0.0001799 +0.0000302 + + +0.000354 +0.000032 +-0.000206 +0.000027 + + + + + + + +-0.000887 +0.000071 +0.242713 +0.000073 +-0.000575 +0.000102 +0.002347 +0.000092 + + +-0.0156504 +0.0000138 +-0.0003481 +0.0000300 + + +0.000399 +0.000034 +-0.000198 +0.000029 + + + + + + + +-0.001428 +0.000072 +0.245092 +0.000073 +-0.000653 +0.000099 +0.002493 +0.000095 + + +-0.0152254 +0.0000140 +-0.0005151 +0.0000286 + + +0.000429 +0.000036 +-0.000187 +0.000030 + + + + + + + +-0.002337 +0.000073 +0.247781 +0.000073 +-0.001098 +0.000094 +0.002714 +0.000100 + + +-0.0146165 +0.0000130 +-0.0006862 +0.0000274 + + +0.000423 +0.000036 +-0.000191 +0.000030 + + + + + + + +-0.003627 +0.000075 +0.250389 +0.000072 +-0.001365 +0.000089 +0.002443 +0.000107 + + +-0.0138706 +0.0000113 +-0.0007762 +0.0000264 + + +0.000396 +0.000035 +-0.000199 +0.000030 + + + + + + + +-0.004951 +0.000078 +0.252542 +0.000070 +-0.001401 +0.000085 +0.001945 +0.000114 + + +-0.0131037 +0.0000099 +-0.0007411 +0.0000257 + + +0.000359 +0.000035 +-0.000202 +0.000029 + + + + + + + +-0.006491 +0.000079 +0.254297 +0.000067 +-0.001648 +0.000083 +0.001655 +0.000121 + + +-0.0124243 +0.0000096 +-0.0006174 +0.0000257 + + +0.000309 +0.000034 +-0.000192 +0.000029 + + + + + + + +-0.008248 +0.000080 +0.255953 +0.000065 +-0.001795 +0.000083 +0.001688 +0.000127 + + +-0.0118846 +0.0000096 +-0.0004627 +0.0000260 + + +0.000257 +0.000033 +-0.000172 +0.000029 + + + + + + + +-0.010008 +0.000078 +0.257753 +0.000064 +-0.001829 +0.000082 +0.001872 +0.000128 + + +-0.0115012 +0.0000093 +-0.0003019 +0.0000260 + + +0.000214 +0.000032 +-0.000148 +0.000029 + + + + + + + +-0.011976 +0.000075 +0.259699 +0.000064 +-0.002065 +0.000081 +0.001976 +0.000127 + + +-0.0112776 +0.0000089 +-0.0001461 +0.0000256 + + +0.000189 +0.000032 +-0.000122 +0.000028 + + + + + + + +-0.014130 +0.000072 +0.261663 +0.000063 +-0.002002 +0.000081 +0.001923 +0.000124 + + +-0.0111991 +0.0000087 +-0.0000050 +0.0000256 + + +0.000222 +0.000032 +-0.000095 +0.000028 + + + + + + + +-0.015736 +0.000070 +0.263496 +0.000061 +-0.001320 +0.000081 +0.001898 +0.000121 + + +-0.0112620 +0.0000087 +0.0001383 +0.0000257 + + +0.000276 +0.000032 +-0.000072 +0.000028 + + + + + + + +-0.016760 +0.000070 +0.265590 +0.000059 +-0.000978 +0.000083 +0.002246 +0.000120 + + +-0.0114750 +0.0000089 +0.0002783 +0.0000254 + + +0.000330 +0.000032 +-0.000058 +0.000028 + + + + + + + +-0.017938 +0.000071 +0.268007 +0.000058 +-0.001397 +0.000084 +0.002491 +0.000118 + + +-0.0118054 +0.0000096 +0.0003547 +0.0000251 + + +0.000341 +0.000032 +-0.000077 +0.000028 + + + + + + + +-0.019696 +0.000072 +0.270485 +0.000059 +-0.002030 +0.000085 +0.002541 +0.000114 + + +-0.0121489 +0.0000106 +0.0003115 +0.0000250 + + +0.000323 +0.000033 +-0.000112 +0.000028 + + + + + + + +-0.021982 +0.000069 +0.273122 +0.000061 +-0.002452 +0.000084 +0.002645 +0.000107 + + +-0.0123922 +0.0000113 +0.0001667 +0.0000249 + + +0.000291 +0.000033 +-0.000148 +0.000029 + + + + + + + +-0.024499 +0.000067 +0.275706 +0.000063 +-0.002547 +0.000084 +0.002548 +0.000102 + + +-0.0124631 +0.0000120 +-0.0000227 +0.0000250 + + +0.000251 +0.000034 +-0.000173 +0.000029 + + + + + + + +-0.026992 +0.000068 +0.278207 +0.000065 +-0.002459 +0.000084 +0.002485 +0.000101 + + +-0.0123523 +0.0000138 +-0.0002044 +0.0000256 + + +0.000207 +0.000039 +-0.000145 +0.000034 + + + + + + + +-0.029396 +0.000070 +0.280700 +0.000069 +-0.002384 +0.000084 +0.002393 +0.000102 + + +-0.0120680 +0.0000163 +-0.0003493 +0.0000264 + + +0.000166 +0.000047 +-0.000102 +0.000041 + + + + + + + +-0.031785 +0.000074 +0.282897 +0.000072 +-0.002331 +0.000086 +0.002066 +0.000102 + + +-0.0116897 +0.0000184 +-0.0003668 +0.0000270 + + +0.000137 +0.000054 +-0.000064 +0.000046 + + + + + + + +-0.034006 +0.000075 +0.284849 +0.000074 +-0.002062 +0.000087 +0.001928 +0.000100 + + +-0.0113975 +0.0000207 +-0.0001942 +0.0000281 + + +0.000149 +0.000054 +-0.000078 +0.000047 + + + + + + + +-0.035834 +0.000075 +0.286849 +0.000075 +-0.001626 +0.000088 +0.002054 +0.000100 + + +-0.0113473 +0.0000228 +0.0001015 +0.0000293 + + +0.000188 +0.000049 +-0.000123 +0.000044 + + + + + + + +-0.037251 +0.000076 +0.288985 +0.000075 +-0.001212 +0.000088 +0.002288 +0.000101 + + +-0.0116101 +0.0000220 +0.0004224 +0.0000293 + + +0.000234 +0.000044 +-0.000172 +0.000041 + + + + + + + +-0.038261 +0.000076 +0.291510 +0.000074 +-0.000788 +0.000087 +0.002790 +0.000103 + + +-0.0121708 +0.0000199 +0.0006816 +0.0000281 + + +0.000277 +0.000039 +-0.000208 +0.000037 + + + + + + + +-0.038807 +0.000076 +0.294635 +0.000071 +-0.000341 +0.000086 +0.003386 +0.000102 + + +-0.0129266 +0.0000191 +0.0008065 +0.0000272 + + +0.000289 +0.000041 +-0.000200 +0.000041 + + + + + + + +-0.038971 +0.000075 +0.298243 +0.000068 +-0.000123 +0.000087 +0.003685 +0.000100 + + +-0.0137318 +0.0000190 +0.0007968 +0.0000271 + + +0.000286 +0.000045 +-0.000170 +0.000046 + + + + + + + +-0.039202 +0.000073 +0.301841 +0.000065 +-0.000344 +0.000088 +0.003488 +0.000097 + + +-0.0144946 +0.0000194 +0.0007136 +0.0000274 + + +0.000241 +0.000048 +-0.000088 +0.000039 + + + + + + + +-0.039740 +0.000072 +0.305113 +0.000066 +-0.000821 +0.000090 +0.003141 +0.000097 + + +-0.0151458 +0.0000212 +0.0005449 +0.0000283 + + +0.000246 +0.000053 +-0.000066 +0.000043 + + + + + + + +-0.040975 +0.000071 +0.308155 +0.000070 +-0.001517 +0.000094 +0.002848 +0.000098 + + +-0.0155490 +0.0000228 +0.0002517 +0.0000294 + + +0.000264 +0.000061 +-0.000059 +0.000050 + + + + + + + +-0.042707 +0.000073 +0.310732 +0.000076 +-0.001684 +0.000097 +0.002511 +0.000098 + + +-0.0156331 +0.0000224 +-0.0000424 +0.0000293 + + +0.000287 +0.000070 +-0.000050 +0.000059 + + + + + + + +-0.044046 +0.000073 +0.313340 +0.000079 +-0.001184 +0.000098 +0.002717 +0.000095 + + +-0.0155037 +0.0000208 +-0.0001924 +0.0000284 + + +0.000307 +0.000077 +-0.000031 +0.000065 + + + + + + + +-0.045115 +0.000073 +0.316259 +0.000080 +-0.000984 +0.000097 +0.002833 +0.000091 + + +-0.0152954 +0.0000193 +-0.0002300 +0.0000278 + + +0.000312 +0.000079 +0.000039 +0.000068 + + + + + + + +-0.046065 +0.000073 +0.318766 +0.000081 +-0.000818 +0.000097 +0.002393 +0.000090 + + +-0.0150624 +0.0000176 +-0.0002387 +0.0000276 + + +0.000311 +0.000082 +0.000120 +0.000071 + + + + + + + +-0.046677 +0.000075 +0.321138 +0.000081 +-0.000420 +0.000096 +0.002443 +0.000092 + + +-0.0148245 +0.0000161 +-0.0002062 +0.0000272 + + +0.000309 +0.000085 +0.000187 +0.000073 + + + + + + + +-0.046882 +0.000079 +0.323792 +0.000079 +0.000045 +0.000093 +0.002760 +0.000094 + + +-0.0146819 +0.0000156 +-0.0000776 +0.0000273 + + +0.000320 +0.000081 +0.000173 +0.000073 + + + + + + + +-0.046540 +0.000080 +0.326621 +0.000078 +0.000614 +0.000088 +0.002981 +0.000097 + + +-0.0146823 +0.0000154 +0.0001030 +0.0000279 + + +0.000336 +0.000086 +0.000111 +0.000071 + + + + + + + +-0.045657 +0.000079 +0.329817 +0.000075 +0.001032 +0.000082 +0.003351 +0.000099 + + +-0.0149129 +0.0000152 +0.0003456 +0.0000282 + + +0.000350 +0.000087 +0.000035 +0.000069 + + + + + + + +-0.044599 +0.000076 +0.333296 +0.000071 +0.001090 +0.000078 +0.003473 +0.000098 + + +-0.0153659 +0.0000151 +0.0005547 +0.0000280 + + +0.000354 +0.000088 +-0.000038 +0.000067 + + + + + + + +-0.043530 +0.000074 +0.336616 +0.000068 +0.001107 +0.000076 +0.003249 +0.000097 + + +-0.0160051 +0.0000148 +0.0007164 +0.0000280 + + +0.000329 +0.000107 +-0.000056 +0.000074 + + + + + + + +-0.042352 +0.000073 +0.339803 +0.000066 +0.001147 +0.000074 +0.003196 +0.000095 + + +-0.0167760 +0.0000144 +0.0008130 +0.0000280 + + +0.000294 +0.000139 +-0.000053 +0.000086 + + + + + + + +-0.041322 +0.000072 +0.343084 +0.000064 +0.000845 +0.000072 +0.003305 +0.000094 + + +-0.0176027 +0.0000144 +0.0008282 +0.0000277 + + +0.000259 +0.000166 +-0.000043 +0.000097 + + + + + + + +-0.040776 +0.000071 +0.346390 +0.000064 +0.000369 +0.000072 +0.003244 +0.000096 + + +-0.0184047 +0.0000157 +0.0007582 +0.0000276 + + +0.000249 +0.000273 +-0.000048 +0.000149 + + + + + + + +-0.040519 +0.000070 +0.349496 +0.000064 +0.000264 +0.000074 +0.003063 +0.000099 + + +-0.0190910 +0.0000182 +0.0006061 +0.0000280 + + +0.000255 +0.000480 +-0.000062 +0.000250 + + + + + + + +-0.040097 +0.000069 +0.352572 +0.000066 +0.000435 +0.000077 +0.003180 +0.000102 + + +-0.0196023 +0.0000225 +0.0004110 +0.0000283 + + +0.000265 +0.000700 +-0.000077 +0.000359 + + + + + + + +-0.039717 +0.000069 +0.355976 +0.000068 +0.000253 +0.000081 +0.003464 +0.000103 + + +-0.0199116 +0.0000292 +0.0002175 +0.0000281 + + +0.000276 +0.000851 +-0.000091 +0.000433 + + + + + + + +-0.039696 +0.000070 +0.359397 +0.000070 +-0.000088 +0.000083 +0.003234 +0.000103 + + +-0.0200604 +0.0000393 +0.0000717 +0.0000282 + + +0.000278 +0.001427 +-0.000100 +0.000715 + + + + + + + +-0.039824 +0.000072 +0.362249 +0.000071 +-0.000057 +0.000084 +0.002600 +0.000101 + + +-0.0200712 +0.0000484 +-0.0000295 +0.0000282 + + +0.000276 +0.002212 +-0.000106 +0.001098 + + + + + + + +-0.039666 +0.000074 +0.364628 +0.000072 +0.000359 +0.000085 +0.002247 +0.000099 + + +-0.0200376 +0.0000417 +0.0000017 +0.0000284 + + +0.000268 +0.002248 +-0.000111 +0.001115 + + + + + + + +-0.039047 +0.000074 +0.366850 +0.000074 +0.000805 +0.000083 +0.002170 +0.000095 + + +-0.0201299 +0.0000312 +0.0002064 +0.0000285 + + +0.000256 +0.001835 +-0.000114 +0.000914 + + + + + + + +-0.038102 +0.000071 +0.368994 +0.000073 +0.001083 +0.000078 +0.002180 +0.000091 + + +-0.0204875 +0.0000251 +0.0005080 +0.0000283 + + +0.000244 +0.001231 +-0.000113 +0.000618 + + + + + + + +-0.036907 +0.000068 +0.371284 +0.000069 +0.001314 +0.000073 +0.002447 +0.000085 + + +-0.0211416 +0.0000205 +0.0007792 +0.0000272 + + +0.000234 +0.000662 +-0.000107 +0.000340 + + + + + + + +-0.035480 +0.000063 +0.373974 +0.000063 +0.001502 +0.000067 +0.002893 +0.000079 + + +-0.0220007 +0.0000167 +0.0009296 +0.0000250 + + +0.000229 +0.000311 +-0.000095 +0.000168 + + + + + + + +-0.033918 +0.000058 +0.377072 +0.000055 +0.001540 +0.000060 +0.003232 +0.000071 + + +-0.0229616 +0.0000139 +0.0009596 +0.0000226 + + +0.000240 +0.000224 +-0.000071 +0.000125 + + + + + + + +-0.032503 +0.000057 +0.380245 +0.000052 +0.001351 +0.000061 +0.003138 +0.000070 + + +-0.0238876 +0.0000121 +0.0008776 +0.0000224 + + +0.000259 +0.000156 +-0.000040 +0.000090 + + + + + + + +-0.031173 +0.000059 +0.383274 +0.000050 +0.001308 +0.000061 +0.003045 +0.000068 + + +-0.0246908 +0.0000117 +0.0006877 +0.0000216 + + +0.000279 +0.000097 +-0.000005 +0.000061 + + + + + + + +-0.029863 +0.000059 +0.386430 +0.000049 +0.001361 +0.000061 +0.003150 +0.000073 + + +-0.0252241 +0.0000125 +0.0003734 +0.0000213 + + +0.000299 +0.000080 +0.000044 +0.000052 + + + + + + + +-0.028402 +0.000061 +0.389466 +0.000048 +0.001615 +0.000060 +0.003066 +0.000071 + + +-0.0254315 +0.0000137 +0.0000649 +0.0000232 + + +0.000318 +0.000068 +0.000095 +0.000046 + + + + + + + +-0.026589 +0.000062 +0.392633 +0.000051 +0.001898 +0.000061 +0.003085 +0.000080 + + +-0.0253926 +0.0000138 +-0.0001361 +0.0000250 + + +0.000331 +0.000056 +0.000135 +0.000040 + + + + + + + +-0.024718 +0.000058 +0.395445 +0.000050 +0.001825 +0.000060 +0.002624 +0.000080 + + +-0.0251809 +0.0000133 +-0.0002733 +0.0000248 + + +0.000335 +0.000041 +0.000156 +0.000035 + + + + + + + +-0.023029 +0.000055 +0.397858 +0.000049 +0.001533 +0.000063 +0.002262 +0.000079 + + +-0.0248761 +0.0000128 +-0.0003175 +0.0000228 + + +0.000317 +0.000044 +0.000120 +0.000034 + + + + + + + +-0.021735 +0.000055 +0.400017 +0.000050 +0.001248 +0.000066 +0.002121 +0.000073 + + +-0.0245819 +0.0000126 +-0.0002598 +0.0000228 + + +0.000291 +0.000040 +0.000058 +0.000032 + + + + + + + +-0.020397 +0.000060 +0.402194 +0.000053 +0.001426 +0.000071 +0.002246 +0.000071 + + +-0.0243867 +0.0000125 +-0.0001267 +0.0000226 + + +0.000266 +0.000037 +-0.000009 +0.000031 + + + + + + + +-0.018832 +0.000063 +0.404562 +0.000056 +0.001589 +0.000077 +0.002569 +0.000071 + + +-0.0243447 +0.0000133 +0.0000379 +0.0000215 + + +0.000273 +0.000037 +-0.000056 +0.000032 + + + + + + + +-0.017339 +0.000063 +0.407431 +0.000053 +0.001557 +0.000074 +0.002849 +0.000070 + + +-0.0244585 +0.0000147 +0.0002013 +0.0000221 + + +0.000295 +0.000038 +-0.000086 +0.000034 + + + + + + + +-0.015648 +0.000063 +0.409984 +0.000051 +0.001785 +0.000072 +0.002529 +0.000071 + + +-0.0247405 +0.0000167 +0.0003819 +0.0000225 + + +0.000314 +0.000039 +-0.000106 +0.000036 + + + + + + + +-0.013789 +0.000060 +0.412618 +0.000053 +0.001778 +0.000068 +0.002741 +0.000070 + + +-0.0252154 +0.0000185 +0.0005579 +0.0000234 + + +0.000322 +0.000041 +-0.000116 +0.000038 + + + + + + + +-0.012263 +0.000058 +0.415517 +0.000052 +0.001462 +0.000072 +0.002899 +0.000072 + + +-0.0258359 +0.0000183 +0.0006744 +0.0000224 + + +0.000295 +0.000042 +-0.000118 +0.000039 + + + + + + + +-0.010755 +0.000057 +0.418256 +0.000049 +0.001566 +0.000073 +0.002808 +0.000069 + + +-0.0265377 +0.0000175 +0.0007197 +0.0000234 + + +0.000254 +0.000044 +-0.000111 +0.000039 + + + + + + + +-0.009065 +0.000056 +0.421259 +0.000048 +0.001553 +0.000075 +0.002890 +0.000069 + + +-0.0272490 +0.0000184 +0.0006993 +0.0000221 + + +0.000228 +0.000046 +-0.000089 +0.000041 + + + + + + + +-0.007878 +0.000057 +0.423780 +0.000049 +0.001158 +0.000075 +0.002451 +0.000066 + + +-0.0279197 +0.0000217 +0.0006258 +0.0000221 + + +0.000216 +0.000051 +-0.000055 +0.000046 + + + + + + + +-0.006541 +0.000058 +0.426318 +0.000051 +0.001324 +0.000074 +0.002538 +0.000067 + + +-0.0284814 +0.0000270 +0.0004743 +0.0000229 + + +0.000216 +0.000059 +-0.000019 +0.000055 + + + + + + + +-0.005307 +0.000057 +0.428835 +0.000053 +0.001214 +0.000073 +0.002519 +0.000067 + + +-0.0288476 +0.0000336 +0.0002659 +0.0000241 + + +0.000259 +0.000066 +0.000039 +0.000074 + + + + + + + +-0.004073 +0.000059 +0.431351 +0.000053 +0.001211 +0.000072 +0.002627 +0.000067 + + +-0.0290184 +0.0000390 +0.0000866 +0.0000242 + + +0.000282 +0.000071 +0.000065 +0.000083 + + + + + + + +-0.002903 +0.000057 +0.434183 +0.000053 +0.000922 +0.000072 +0.002788 +0.000073 + + +-0.0290346 +0.0000396 +-0.0000344 +0.0000237 + + +0.000308 +0.000074 +0.000070 +0.000088 + + + + + + + +-0.002436 +0.000057 +0.436715 +0.000057 +0.000425 +0.000075 +0.002499 +0.000083 + + +-0.0289857 +0.0000333 +-0.0000349 +0.0000223 + + +0.000337 +0.000075 +0.000028 +0.000083 + + + + + + + +-0.001750 +0.000059 +0.439264 +0.000060 +0.000681 +0.000078 +0.002527 +0.000086 + + +-0.0290056 +0.0000263 +0.0001070 +0.0000228 + + +0.000359 +0.000076 +-0.000027 +0.000073 + + + + + + + +-0.001196 +0.000065 +0.441724 +0.000061 +0.000823 +0.000085 +0.002234 +0.000084 + + +-0.0292618 +0.0000198 +0.0003882 +0.0000217 + + +0.000350 +0.000075 +-0.000046 +0.000068 + + + + + + + +0.000214 +0.000071 +0.443555 +0.000063 +0.001587 +0.000085 +0.001700 +0.000081 + + +-0.0298215 +0.0000158 +0.0007070 +0.0000226 + + +0.000317 +0.000068 +-0.000053 +0.000061 + + + + + + + +0.001716 +0.000067 +0.445308 +0.000060 +0.001430 +0.000075 +0.001697 +0.000078 + + +-0.0306441 +0.0000133 +0.0009188 +0.0000231 + + +0.000280 +0.000063 +-0.000052 +0.000052 + + + + + + + +0.002942 +0.000068 +0.446930 +0.000058 +0.001235 +0.000070 +0.001626 +0.000081 + + +-0.0316096 +0.0000121 +0.0009803 +0.0000235 + + +0.000249 +0.000058 +-0.000050 +0.000046 + + + + + + + +0.004306 +0.000064 +0.448627 +0.000054 +0.001416 +0.000066 +0.001755 +0.000079 + + +-0.0325371 +0.0000117 +0.0008834 +0.0000223 + + +0.000260 +0.000067 +-0.000054 +0.000051 + + + + + + + +0.005723 +0.000063 +0.450456 +0.000053 +0.001529 +0.000063 +0.001976 +0.000076 + + +-0.0333578 +0.0000108 +0.0007316 +0.0000230 + + +0.000289 +0.000082 +-0.000060 +0.000061 + + + + + + + +0.007430 +0.000061 +0.452633 +0.000057 +0.001766 +0.000061 +0.002358 +0.000075 + + +-0.0339749 +0.0000099 +0.0004803 +0.0000231 + + +0.000321 +0.000096 +-0.000060 +0.000070 + + + + + + + +0.009155 +0.000057 +0.455143 +0.000056 +0.001670 +0.000062 +0.002638 +0.000077 + + +-0.0342997 +0.0000094 +0.0001878 +0.0000221 + + +0.000333 +0.000092 +-0.000028 +0.000067 + + + + + + + +0.010695 +0.000053 +0.457843 +0.000055 +0.001640 +0.000060 +0.002760 +0.000079 + + +-0.0343744 +0.0000091 +-0.0000276 +0.0000225 + + +0.000334 +0.000078 +0.000015 +0.000058 + + + + + + + +0.012615 +0.000053 +0.460612 +0.000054 +0.002125 +0.000060 +0.002635 +0.000079 + + +-0.0342760 +0.0000088 +-0.0001638 +0.0000235 + + +0.000332 +0.000063 +0.000047 +0.000048 + + + + + + + +0.014950 +0.000052 +0.462927 +0.000051 +0.002422 +0.000058 +0.002163 +0.000077 + + +-0.0340746 +0.0000087 +-0.0002294 +0.0000226 + + +0.000331 +0.000053 +0.000056 +0.000041 + + + + + + + +0.017333 +0.000050 +0.465001 +0.000048 +0.002289 +0.000059 +0.002056 +0.000078 + + +-0.0338446 +0.0000084 +-0.0002297 +0.0000211 + + +0.000342 +0.000054 +-0.000017 +0.000042 + + + + + + + +0.019395 +0.000053 +0.467135 +0.000051 +0.002005 +0.000059 +0.002087 +0.000085 + + +-0.0336272 +0.0000084 +-0.0001836 +0.0000214 + + +0.000358 +0.000058 +-0.000122 +0.000045 + + + + + + + +0.021422 +0.000055 +0.469079 +0.000053 +0.002155 +0.000061 +0.001801 +0.000087 + + +-0.0335007 +0.0000085 +-0.0000486 +0.0000220 + + +0.000375 +0.000063 +-0.000226 +0.000048 + + + + + + + +0.023823 +0.000055 +0.470667 +0.000053 +0.002392 +0.000060 +0.001478 +0.000087 + + +-0.0335561 +0.0000088 +0.0001491 +0.0000216 + + +0.000399 +0.000062 +-0.000255 +0.000048 + + + + + + + +0.025987 +0.000054 +0.472089 +0.000055 +0.001950 +0.000061 +0.001386 +0.000088 + + +-0.0338124 +0.0000088 +0.0003309 +0.0000220 + + +0.000424 +0.000058 +-0.000231 +0.000046 + + + + + + + +0.027605 +0.000055 +0.473468 +0.000056 +0.001391 +0.000060 +0.001306 +0.000085 + + +-0.0342084 +0.0000098 +0.0004987 +0.0000230 + + +0.000443 +0.000054 +-0.000188 +0.000043 + + + + + + + +0.028796 +0.000054 +0.474630 +0.000056 +0.001208 +0.000061 +0.001097 +0.000080 + + +-0.0347850 +0.0000107 +0.0006431 +0.0000237 + + +0.000450 +0.000050 +-0.000137 +0.000041 + + + + + + + +0.030234 +0.000055 +0.475690 +0.000055 +0.001569 +0.000062 +0.000983 +0.000079 + + +-0.0354724 +0.0000115 +0.0007120 +0.0000241 + + +0.000440 +0.000047 +-0.000090 +0.000039 + + + + + + + +0.031924 +0.000054 +0.476551 +0.000055 +0.001812 +0.000061 +0.000907 +0.000074 + + +-0.0361704 +0.0000122 +0.0006840 +0.0000237 + + +0.000393 +0.000047 +-0.000071 +0.000040 + + + + + + + +0.033840 +0.000054 +0.477643 +0.000058 +0.001933 +0.000060 +0.001113 +0.000069 + + +-0.0368208 +0.0000132 +0.0006055 +0.0000237 + + +0.000341 +0.000048 +-0.000067 +0.000041 + + + + + + + +0.035678 +0.000061 +0.478685 +0.000059 +0.001950 +0.000063 +0.001062 +0.000073 + + +-0.0373656 +0.0000143 +0.0004770 +0.0000237 + + +0.000315 +0.000050 +-0.000092 +0.000042 + + + + + + + +0.037872 +0.000061 +0.479777 +0.000061 +0.002165 +0.000065 +0.001260 +0.000071 + + +-0.0377721 +0.0000153 +0.0003168 +0.0000239 + + +0.000311 +0.000055 +-0.000137 +0.000044 + + + + + + + +0.039787 +0.000060 +0.481291 +0.000062 +0.001885 +0.000066 +0.001559 +0.000072 + + +-0.0379959 +0.0000164 +0.0001477 +0.0000259 + + +0.000321 +0.000060 +-0.000184 +0.000046 + + + + + + + +0.041742 +0.000059 +0.482696 +0.000058 +0.001911 +0.000063 +0.001462 +0.000074 + + +-0.0381001 +0.0000181 +0.0000777 +0.0000273 + + +0.000341 +0.000064 +-0.000222 +0.000047 + + + + + + + +0.043548 +0.000059 +0.484305 +0.000056 +0.001754 +0.000063 +0.001513 +0.000077 + + +-0.0381955 +0.0000119 +0.0001209 +0.0000243 + + +0.000361 +0.000075 +-0.000208 +0.000051 + + + + + + + +0.045281 +0.000058 +0.485514 +0.000053 +0.001829 +0.000065 +0.001111 +0.000082 + + +-0.0383861 +0.0000124 +0.0002941 +0.0000261 + + +0.000383 +0.000093 +-0.000175 +0.000057 + + + + + + + +0.047344 +0.000060 +0.486618 +0.000054 +0.002128 +0.000067 +0.001101 +0.000077 + + +-0.0388214 +0.0000123 +0.0005745 +0.0000252 + + +0.000405 +0.000108 +-0.000142 +0.000063 + + + + + + + +0.049455 +0.000063 +0.487750 +0.000053 +0.002367 +0.000071 +0.001214 +0.000075 + + +-0.0395385 +0.0000123 +0.0008647 +0.0000240 + + +0.000423 +0.000296 +-0.000147 +0.000066 + + + + + + + +0.052341 +0.000066 +0.489096 +0.000051 +0.003216 +0.000070 +0.001445 +0.000073 + + +-0.0405378 +0.0000125 +0.0011312 +0.0000243 + + +0.000436 +0.000520 +-0.000174 +0.000071 + + + + + + + +0.055853 +0.000060 +0.490619 +0.000051 +0.003551 +0.000071 +0.001600 +0.000072 + + +-0.0417757 +0.0000132 +0.0013088 +0.0000251 + + +0.000442 +0.000772 +-0.000202 +0.000075 + + + + + + + +0.059171 +0.000056 +0.492264 +0.000053 +0.003165 +0.000068 +0.001518 +0.000069 + + +-0.0430984 +0.0000143 +0.0012961 +0.0000254 + + +0.000441 +0.000398 +-0.000220 +0.000079 + + + + + + + +0.062111 +0.000055 +0.493466 +0.000052 +0.002699 +0.000064 +0.001107 +0.000071 + + +-0.0442928 +0.0000144 +0.0010916 +0.0000246 + + +0.000409 +0.000700 +-0.000193 +0.000110 + + + + + + + +0.064515 +0.000053 +0.494600 +0.000051 +0.002295 +0.000063 +0.001120 +0.000067 + + +-0.0452544 +0.0000143 +0.0008148 +0.0000257 + + +0.000366 +0.001186 +-0.000149 +0.000160 + + + + + + + +0.066867 +0.000054 +0.495722 +0.000050 +0.002389 +0.000063 +0.001051 +0.000066 + + +-0.0459063 +0.0000144 +0.0004982 +0.0000266 + + +0.000323 +0.001618 +-0.000103 +0.000205 + + + + + + + +0.069368 +0.000053 +0.496634 +0.000050 +0.002458 +0.000068 +0.000883 +0.000068 + + +-0.0462641 +0.0000147 +0.0002209 +0.0000251 + + +0.000298 +0.001522 +-0.000078 +0.000197 + + + + + + + +0.071680 +0.000054 +0.497569 +0.000046 +0.002166 +0.000069 +0.000820 +0.000072 + + +-0.0463853 +0.0000128 +-0.0000170 +0.0000253 + + +0.000288 +0.001123 +-0.000068 +0.000159 + + + + + + + +0.073653 +0.000056 +0.498156 +0.000045 +0.002096 +0.000068 +0.000608 +0.000074 + + +-0.0462514 +0.0000144 +-0.0002163 +0.0000246 + + +0.000303 +0.001645 +-0.000063 +0.000117 + + + + + + + +0.076168 +0.000061 +0.498968 +0.000048 +0.002767 +0.000064 +0.000949 +0.000077 + + +-0.0459896 +0.0000160 +-0.0003017 +0.0000235 + + +0.000287 +0.000112 +-0.000057 +0.000087 + + + + + + + +0.079160 +0.000061 +0.500036 +0.000050 +0.003086 +0.000061 +0.001096 +0.000074 + + +-0.0456668 +0.0000175 +-0.0003301 +0.0000219 + + +0.000328 +0.000098 +-0.000037 +0.000079 + + + + + + + +0.082179 +0.000056 +0.501016 +0.000053 +0.002917 +0.000062 +0.001141 +0.000073 + + +-0.0453568 +0.0000175 +-0.0002884 +0.0000218 + + +0.000358 +0.000085 +-0.000014 +0.000072 + + + + + + + +0.084860 +0.000067 +0.502475 +0.000073 +0.002340 +0.000084 +0.001463 +0.000110 + + +-0.0451088 +0.0000229 +-0.0002078 +0.0000300 + + +0.000383 +0.000129 +0.000004 +0.000067 + + + + + + + +0.086695 +0.000068 +0.503689 +0.000074 +0.001736 +0.000085 +0.001011 +0.000112 + + +-0.0449627 +0.0000219 +-0.0000620 +0.0000304 + + +0.000441 +0.000115 +-0.000008 +0.000068 + + + + + + + +0.088657 +0.000070 +0.504421 +0.000074 +0.001885 +0.000084 +0.000689 +0.000113 + + +-0.0450081 +0.0000220 +0.0001365 +0.0000299 + + +0.000504 +0.000115 +-0.000040 +0.000074 + + + + + + + +0.090324 +0.000073 +0.505265 +0.000074 +0.001579 +0.000083 +0.000830 +0.000115 + + +-0.0452249 +0.0000225 +0.0002690 +0.0000293 + + +0.000550 +0.000115 +-0.000075 +0.000082 + + + + + + + +0.091875 +0.000076 +0.506012 +0.000073 +0.001652 +0.000082 +0.000810 +0.000117 + + +-0.0455065 +0.0000235 +0.0002945 +0.0000290 + + +0.000564 +0.000114 +-0.000108 +0.000088 + + + + + + + +0.093785 +0.000078 +0.506996 +0.000071 +0.002187 +0.000082 +0.000956 +0.000117 + + +-0.0457887 +0.0000235 +0.0002782 +0.0000289 + + +0.000531 +0.000114 +-0.000130 +0.000091 + + + + + + + +0.096350 +0.000078 +0.507780 +0.000068 +0.002796 +0.000082 +0.000791 +0.000116 + + +-0.0460547 +0.0000209 +0.0002314 +0.0000288 + + +0.000430 +0.000107 +-0.000133 +0.000082 + + + + + + + +0.099280 +0.000077 +0.508684 +0.000065 +0.002986 +0.000084 +0.000840 +0.000113 + + +-0.0462257 +0.0000180 +0.0000916 +0.0000287 + + +0.000337 +0.000117 +-0.000131 +0.000078 + + + + + + + +0.102195 +0.000076 +0.509333 +0.000063 +0.002950 +0.000087 +0.000633 +0.000109 + + +-0.0462142 +0.0000161 +-0.0001268 +0.0000289 + + +0.000277 +0.000149 +-0.000128 +0.000075 + + + + + + + +0.105220 +0.000075 +0.510064 +0.000062 +0.002963 +0.000087 +0.000605 +0.000105 + + +-0.0459633 +0.0000158 +-0.0003677 +0.0000299 + + +0.000225 +0.000118 +-0.000127 +0.000070 + + + + + + + +0.108006 +0.000074 +0.510376 +0.000064 +0.002888 +0.000086 +0.000283 +0.000099 + + +-0.0454993 +0.0000181 +-0.0005265 +0.0000315 + + +0.000216 +0.000130 +-0.000132 +0.000065 + + + + + + + +0.111217 +0.000071 +0.510811 +0.000066 +0.003202 +0.000083 +0.000483 +0.000092 + + +-0.0449675 +0.0000232 +-0.0005304 +0.0000327 + + +0.000239 +0.000138 +-0.000145 +0.000062 + + + + + + + +0.114187 +0.000068 +0.511330 +0.000070 +0.002713 +0.000081 +0.000425 +0.000087 + + +-0.0444769 +0.0000336 +-0.0004348 +0.0000324 + + +0.000336 +0.000208 +-0.000183 +0.000077 + + + + + + + +0.116506 +0.000066 +0.511524 +0.000072 +0.002326 +0.000080 +0.000172 +0.000083 + + +-0.0441244 +0.0000513 +-0.0002531 +0.0000312 + + +0.000451 +0.000321 +-0.000229 +0.000103 + + + + + + + +0.119171 +0.000064 +0.511814 +0.000072 +0.002676 +0.000079 +0.000316 +0.000082 + + +-0.0439844 +0.0000682 +-0.0000223 +0.0000301 + + +0.000395 +0.001041 +-0.000270 +0.000125 + + + + + + + +0.121697 +0.000062 +0.512135 +0.000073 +0.002373 +0.000078 +0.000360 +0.000082 + + +-0.0440725 +0.0000595 +0.0001874 +0.0000295 + + +0.000403 +0.000958 +-0.000303 +0.000122 + + + + + + + +0.123832 +0.000063 +0.512556 +0.000074 +0.002112 +0.000078 +0.000413 +0.000083 + + +-0.0443300 +0.0000429 +0.0002974 +0.0000291 + + +0.000525 +0.000139 +-0.000322 +0.000106 + + + + + + + +0.126093 +0.000064 +0.512902 +0.000075 +0.002383 +0.000080 +0.000259 +0.000085 + + +-0.0446157 +0.0000335 +0.0002673 +0.0000286 + + +0.000431 +0.000109 +-0.000319 +0.000090 + + + + + + + +0.128655 +0.000066 +0.513023 +0.000075 +0.002628 +0.000082 +0.000097 +0.000087 + + +-0.0448327 +0.0000287 +0.0001509 +0.0000282 + + +0.000346 +0.000126 +-0.000289 +0.000079 + + + + + + + +0.131265 +0.000067 +0.513186 +0.000073 +0.002582 +0.000081 +0.000060 +0.000089 + + +-0.0448926 +0.0000269 +-0.0000520 +0.0000282 + + +0.000345 +0.000117 +-0.000204 +0.000088 + + + + + + + +0.133765 +0.000067 +0.513018 +0.000069 +0.002551 +0.000080 +-0.000212 +0.000090 + + +-0.0447062 +0.0000251 +-0.0003110 +0.0000284 + + +0.000360 +0.000116 +-0.000112 +0.000100 + + + + + + + +0.136473 +0.000066 +0.512884 +0.000063 +0.002860 +0.000078 +-0.000076 +0.000089 + + +-0.0442808 +0.0000212 +-0.0005311 +0.0000285 + + +0.000352 +0.000130 +-0.000056 +0.000107 + + + + + + + +0.139533 +0.000065 +0.512907 +0.000059 +0.003069 +0.000078 +-0.000008 +0.000087 + + +-0.0436680 +0.0000174 +-0.0006963 +0.0000289 + + +0.000336 +0.000100 +-0.000036 +0.000112 + + + + + + + +0.142445 +0.000064 +0.512759 +0.000057 +0.002994 +0.000080 +-0.000222 +0.000087 + + +-0.0429057 +0.0000156 +-0.0008189 +0.0000297 + + +0.000350 +0.000087 +-0.000084 +0.000088 + + + + + + + +0.145679 +0.000065 +0.512473 +0.000057 +0.003201 +0.000080 +-0.000248 +0.000087 + + +-0.0420518 +0.0000157 +-0.0008735 +0.0000305 + + +0.000395 +0.000074 +-0.000118 +0.000074 + + + + + + + +0.148651 +0.000066 +0.512369 +0.000058 +0.003053 +0.000080 +-0.000177 +0.000088 + + +-0.0411848 +0.0000176 +-0.0008457 +0.0000309 + + +0.000435 +0.000063 +-0.000152 +0.000060 + + + + + + + +0.151998 +0.000067 +0.511955 +0.000058 +0.003460 +0.000080 +-0.000462 +0.000087 + + +-0.0403854 +0.0000214 +-0.0007621 +0.0000302 + + +0.000419 +0.000066 +-0.000144 +0.000061 + + + + + + + +0.155498 +0.000067 +0.511551 +0.000059 +0.003301 +0.000078 +-0.000382 +0.000083 + + +-0.0396589 +0.0000273 +-0.0006812 +0.0000286 + + +0.000374 +0.000076 +-0.000119 +0.000067 + + + + + + + +0.158323 +0.000067 +0.511208 +0.000058 +0.002786 +0.000074 +-0.000351 +0.000079 + + +-0.0390256 +0.0000338 +-0.0005803 +0.0000275 + + +0.000327 +0.000085 +-0.000095 +0.000073 + + + + + + + +0.161368 +0.000064 +0.510809 +0.000056 +0.003022 +0.000069 +-0.000304 +0.000073 + + +-0.0384982 +0.0000404 +-0.0004743 +0.0000263 + + +0.000320 +0.000088 +-0.000122 +0.000077 + + + + + + + +0.164234 +0.000060 +0.510724 +0.000053 +0.002862 +0.000064 +-0.000132 +0.000066 + + +-0.0380713 +0.0000465 +-0.0003725 +0.0000247 + + +0.000338 +0.000090 +-0.000175 +0.000082 + + + + + + + +0.167176 +0.000063 +0.510340 +0.000054 +0.003060 +0.000071 +-0.000428 +0.000068 + + +-0.0377554 +0.0000534 +-0.0002695 +0.0000250 + + +0.000364 +0.000093 +-0.000226 +0.000088 + + + + + + + +0.170433 +0.000060 +0.509962 +0.000056 +0.003398 +0.000072 +-0.000248 +0.000067 + + +-0.0375206 +0.0000557 +-0.0002088 +0.0000253 + + +0.000389 +0.000095 +-0.000261 +0.000092 + + + + + + + +0.173953 +0.000055 +0.509955 +0.000056 +0.003397 +0.000069 +-0.000051 +0.000071 + + +-0.0373143 +0.0000681 +-0.0002234 +0.0000265 + + +0.000395 +0.000112 +-0.000223 +0.000105 + + + + + + + +0.176991 +0.000054 +0.509637 +0.000054 +0.003039 +0.000067 +-0.000444 +0.000075 + + +-0.0370420 +0.0000451 +-0.0003110 +0.0000255 + + +0.000391 +0.000140 +-0.000158 +0.000126 + + + + + + + +0.180263 +0.000054 +0.509120 +0.000051 +0.003347 +0.000065 +-0.000599 +0.000075 + + +-0.0366739 +0.0000523 +-0.0004450 +0.0000252 + + +0.000383 +0.000166 +-0.000095 +0.000144 + + + + + + + +0.183626 +0.000054 +0.508483 +0.000049 +0.003466 +0.000062 +-0.000660 +0.000074 + + +-0.0361336 +0.0000558 +-0.0006398 +0.0000242 + + +0.000367 +0.000162 +-0.000079 +0.000139 + + + + + + + +0.187226 +0.000054 +0.507848 +0.000048 +0.003531 +0.000063 +-0.000504 +0.000076 + + +-0.0353981 +0.0000605 +-0.0008316 +0.0000241 + + +0.000349 +0.000146 +-0.000097 +0.000120 + + + + + + + +0.190492 +0.000056 +0.507593 +0.000051 +0.003021 +0.000070 +-0.000190 +0.000079 + + +-0.0344896 +0.0000394 +-0.0009610 +0.0000239 + + +0.000334 +0.000127 +-0.000133 +0.000100 + + + + + + + +0.193198 +0.000057 +0.507334 +0.000052 +0.002608 +0.000070 +-0.000260 +0.000086 + + +-0.0335318 +0.0000942 +-0.0009460 +0.0000239 + + +0.000327 +0.000114 +-0.000180 +0.000085 + + + + + + + +0.195908 +0.000058 +0.507073 +0.000051 +0.002642 +0.000067 +-0.000405 +0.000086 + + +-0.0326419 +0.0000753 +-0.0008177 +0.0000231 + + +0.000339 +0.000105 +-0.000245 +0.000080 + + + + + + + +0.198426 +0.000057 +0.506374 +0.000052 +0.002582 +0.000068 +-0.000812 +0.000085 + + +-0.0319144 +0.0000551 +-0.0006116 +0.0000232 + + +0.000363 +0.000094 +-0.000305 +0.000075 + + + + + + + +0.201220 +0.000055 +0.505514 +0.000055 +0.002898 +0.000065 +-0.000914 +0.000081 + + +-0.0314343 +0.0000436 +-0.0003658 +0.0000230 + + +0.000391 +0.000086 +-0.000342 +0.000071 + + + + + + + +0.204156 +0.000053 +0.504535 +0.000052 +0.002937 +0.000061 +-0.001122 +0.000077 + + +-0.0311542 +0.0000252 +-0.0002083 +0.0000226 + + +0.000429 +0.000079 +-0.000332 +0.000072 + + + + + + + +0.207001 +0.000052 +0.503185 +0.000052 +0.002728 +0.000061 +-0.001354 +0.000076 + + +-0.0309623 +0.0000278 +-0.0002072 +0.0000233 + + +0.000419 +0.000084 +-0.000285 +0.000075 + + + + + + + +0.209533 +0.000053 +0.502013 +0.000052 +0.002510 +0.000062 +-0.001112 +0.000077 + + +-0.0306936 +0.0000244 +-0.0003264 +0.0000242 + + +0.000408 +0.000075 +-0.000203 +0.000068 + + + + + + + +0.212151 +0.000053 +0.500923 +0.000053 +0.002573 +0.000064 +-0.001051 +0.000080 + + +-0.0302988 +0.0000200 +-0.0004836 +0.0000245 + + +0.000381 +0.000062 +-0.000113 +0.000055 + + + + + + + +0.214585 +0.000053 +0.499885 +0.000054 +0.002373 +0.000068 +-0.001031 +0.000081 + + +-0.0297092 +0.0000182 +-0.0007161 +0.0000244 + + +0.000341 +0.000058 +-0.000090 +0.000051 + + + + + + + +0.216926 +0.000055 +0.498812 +0.000052 +0.002361 +0.000070 +-0.001129 +0.000078 + + +-0.0288524 +0.0000170 +-0.0009714 +0.0000256 + + +0.000300 +0.000058 +-0.000114 +0.000049 + + + + + + + +0.219379 +0.000053 +0.497583 +0.000051 +0.002488 +0.000069 +-0.001192 +0.000076 + + +-0.0277983 +0.0000162 +-0.0011411 +0.0000323 + + +0.000267 +0.000055 +-0.000157 +0.000047 + + + + + + + +0.221871 +0.000054 +0.496538 +0.000050 +0.002511 +0.000070 +-0.001022 +0.000073 + + +-0.0265972 +0.0000159 +-0.0012592 +0.0000302 + + +0.000294 +0.000054 +-0.000163 +0.000046 + + + + + + + +0.224374 +0.000053 +0.495464 +0.000049 +0.002537 +0.000069 +-0.001026 +0.000072 + + +-0.0253009 +0.0000156 +-0.0013057 +0.0000283 + + +0.000353 +0.000053 +-0.000147 +0.000044 + + + + + + + +0.226953 +0.000053 +0.494546 +0.000049 +0.002408 +0.000073 +-0.000828 +0.000069 + + +-0.0240270 +0.0000148 +-0.0012355 +0.0000273 + + +0.000418 +0.000049 +-0.000131 +0.000041 + + + + + + + +0.228983 +0.000053 +0.493824 +0.000050 +0.001931 +0.000071 +-0.000706 +0.000069 + + +-0.0228538 +0.0000143 +-0.0011031 +0.0000274 + + +0.000473 +0.000048 +-0.000120 +0.000039 + + + + + + + +0.230996 +0.000054 +0.493045 +0.000052 +0.002118 +0.000077 +-0.000777 +0.000069 + + +-0.0218364 +0.0000128 +-0.0009241 +0.0000262 + + +0.000476 +0.000045 +-0.000142 +0.000037 + + + + + + + +0.233340 +0.000055 +0.492282 +0.000053 +0.002469 +0.000083 +-0.000902 +0.000073 + + +-0.0210160 +0.0000123 +-0.0007087 +0.0000257 + + +0.000455 +0.000040 +-0.000174 +0.000035 + + + + + + + +0.235893 +0.000055 +0.491075 +0.000051 +0.002517 +0.000078 +-0.001240 +0.000071 + + +-0.0204242 +0.0000122 +-0.0004857 +0.0000254 + + +0.000425 +0.000037 +-0.000205 +0.000032 + + + + + + + +0.238218 +0.000053 +0.489978 +0.000049 +0.002247 +0.000076 +-0.001163 +0.000073 + + +-0.0200295 +0.0000129 +-0.0003219 +0.0000243 + + +0.000405 +0.000036 +-0.000212 +0.000032 + + + + + + + +0.240409 +0.000058 +0.488602 +0.000048 +0.002198 +0.000074 +-0.001453 +0.000071 + + +-0.0197499 +0.0000137 +-0.0002342 +0.0000233 + + +0.000389 +0.000036 +-0.000202 +0.000032 + + + + + + + +0.242675 +0.000059 +0.487119 +0.000049 +0.002298 +0.000071 +-0.001541 +0.000071 + + +-0.0195468 +0.0000135 +-0.0001810 +0.0000228 + + +0.000375 +0.000037 +-0.000186 +0.000033 + + + + + + + +0.244998 +0.000063 +0.485494 +0.000049 +0.002344 +0.000071 +-0.001606 +0.000068 + + +-0.0193720 +0.0000128 +-0.0001947 +0.0000227 + + +0.000360 +0.000037 +-0.000165 +0.000033 + + + + + + + +0.247369 +0.000058 +0.483986 +0.000047 +0.002397 +0.000069 +-0.001544 +0.000071 + + +-0.0191211 +0.0000133 +-0.0003134 +0.0000231 + + +0.000339 +0.000040 +-0.000141 +0.000036 + + + + + + + +0.249864 +0.000057 +0.482311 +0.000045 +0.002489 +0.000069 +-0.001636 +0.000072 + + +-0.0187234 +0.0000136 +-0.0004844 +0.0000235 + + +0.000319 +0.000045 +-0.000121 +0.000040 + + + + + + + +0.252271 +0.000055 +0.480832 +0.000047 +0.002338 +0.000069 +-0.001523 +0.000068 + + +-0.0181468 +0.0000149 +-0.0006677 +0.0000243 + + +0.000305 +0.000050 +-0.000112 +0.000044 + + + + + + + +0.254520 +0.000053 +0.479116 +0.000048 +0.002072 +0.000073 +-0.001779 +0.000067 + + +-0.0174005 +0.0000159 +-0.0008228 +0.0000251 + + +0.000314 +0.000049 +-0.000144 +0.000044 + + + + + + + +0.256321 +0.000052 +0.477330 +0.000051 +0.001590 +0.000074 +-0.001868 +0.000066 + + +-0.0165264 +0.0000168 +-0.0009135 +0.0000249 + + +0.000337 +0.000047 +-0.000199 +0.000042 + + + + + + + +0.257717 +0.000055 +0.475332 +0.000051 +0.001475 +0.000074 +-0.002039 +0.000065 + + +-0.0156145 +0.0000178 +-0.0008861 +0.0000249 + + +0.000364 +0.000044 +-0.000253 +0.000040 + + + + + + + +0.259552 +0.000058 +0.473316 +0.000053 +0.001959 +0.000073 +-0.001890 +0.000065 + + +-0.0148009 +0.0000146 +-0.0007225 +0.0000253 + + +0.000391 +0.000043 +-0.000293 +0.000038 + + + + + + + +0.261538 +0.000061 +0.471686 +0.000052 +0.001985 +0.000075 +-0.001550 +0.000075 + + +-0.0141968 +0.0000164 +-0.0004612 +0.0000256 + + +0.000409 +0.000044 +-0.000273 +0.000039 + + + + + + + +0.263444 +0.000063 +0.470105 +0.000053 +0.001955 +0.000077 +-0.001667 +0.000075 + + +-0.0138930 +0.0000194 +-0.0001511 +0.0000257 + + +0.000416 +0.000047 +-0.000229 +0.000041 + + + + + + + +0.265536 +0.000065 +0.468247 +0.000053 +0.002224 +0.000078 +-0.001902 +0.000076 + + +-0.0138656 +0.0000227 +0.0000812 +0.0000262 + + +0.000411 +0.000050 +-0.000179 +0.000043 + + + + + + + +0.267930 +0.000064 +0.466413 +0.000053 +0.002497 +0.000076 +-0.001739 +0.000074 + + +-0.0140118 +0.0000267 +0.0001727 +0.0000249 + + +0.000365 +0.000050 +-0.000153 +0.000043 + + + + + + + +0.270493 +0.000060 +0.464868 +0.000054 +0.002523 +0.000077 +-0.001411 +0.000071 + + +-0.0141542 +0.0000209 +0.0000858 +0.0000241 + + +0.000298 +0.000049 +-0.000142 +0.000041 + + + + + + + +0.272864 +0.000057 +0.463581 +0.000057 +0.002157 +0.000079 +-0.001263 +0.000069 + + +-0.0141484 +0.0000189 +-0.0001127 +0.0000238 + + +0.000235 +0.000048 +-0.000139 +0.000039 + + + + + + + +0.274685 +0.000056 +0.462231 +0.000057 +0.001674 +0.000081 +-0.001445 +0.000067 + + +-0.0139140 +0.0000164 +-0.0003629 +0.0000236 + + +0.000190 +0.000047 +-0.000141 +0.000037 + + + + + + + +0.276337 +0.000054 +0.460613 +0.000056 +0.001556 +0.000080 +-0.001717 +0.000072 + + +-0.0134271 +0.0000150 +-0.0006123 +0.0000236 + + +0.000215 +0.000051 +-0.000150 +0.000040 + + + + + + + +0.277790 +0.000053 +0.458807 +0.000055 +0.001340 +0.000080 +-0.002007 +0.000071 + + +-0.0127031 +0.0000141 +-0.0008460 +0.0000240 + + +0.000272 +0.000058 +-0.000158 +0.000044 + + + + + + + +0.278994 +0.000055 +0.456494 +0.000056 +0.001091 +0.000081 +-0.002214 +0.000071 + + +-0.0117351 +0.0000137 +-0.0010614 +0.0000242 + + +0.000336 +0.000064 +-0.000156 +0.000048 + + + + + + + +0.279970 +0.000054 +0.454733 +0.000057 +0.000888 +0.000082 +-0.001791 +0.000076 + + +-0.0106076 +0.0000138 +-0.0011748 +0.0000245 + + +0.000374 +0.000082 +-0.000105 +0.000057 + + + + + + + +0.280771 +0.000055 +0.452604 +0.000056 +0.000870 +0.000082 +-0.002123 +0.000076 + + +-0.0094177 +0.0000150 +-0.0011952 +0.0000241 + + +0.000394 +0.000119 +-0.000030 +0.000074 + + + + + + + +0.281823 +0.000055 +0.450679 +0.000056 +0.001059 +0.000078 +-0.001844 +0.000075 + + +-0.0082485 +0.0000170 +-0.0011327 +0.0000235 + + +0.000404 +0.000159 +0.000039 +0.000093 + + + + + + + +0.282744 +0.000055 +0.448904 +0.000056 +0.000877 +0.000078 +-0.001796 +0.000074 + + +-0.0071781 +0.0000219 +-0.0009947 +0.0000239 + + +0.000407 +0.000187 +0.000083 +0.000106 + + + + + + + +0.283572 +0.000058 +0.447025 +0.000059 +0.000740 +0.000078 +-0.001879 +0.000076 + + +-0.0062732 +0.0000188 +-0.0007974 +0.0000245 + + +0.000416 +0.000262 +0.000036 +0.000131 + + + + + + + +0.284154 +0.000058 +0.445183 +0.000060 +0.000699 +0.000077 +-0.001974 +0.000079 + + +-0.0055690 +0.0000186 +-0.0006196 +0.0000249 + + +0.000414 +0.000370 +-0.000048 +0.000167 + + + + + + + +0.285203 +0.000065 +0.442912 +0.000060 +0.001231 +0.000076 +-0.002328 +0.000078 + + +-0.0050148 +0.0000187 +-0.0004897 +0.0000256 + + +0.000392 +0.000457 +-0.000133 +0.000195 + + + + + + + +0.286566 +0.000064 +0.440690 +0.000058 +0.001358 +0.000071 +-0.002339 +0.000076 + + +-0.0045717 +0.0000199 +-0.0004010 +0.0000272 + + +0.000324 +0.000708 +-0.000193 +0.000280 + + + + + + + +0.287747 +0.000063 +0.438093 +0.000059 +0.001064 +0.000071 +-0.002659 +0.000077 + + +-0.0041923 +0.0000367 +-0.0003720 +0.0000270 + + +0.000243 +0.001050 +-0.000230 +0.000397 + + + + + + + +0.288671 +0.000062 +0.435493 +0.000057 +0.000929 +0.000068 +-0.002780 +0.000080 + + +-0.0038044 +0.0000462 +-0.0004111 +0.0000271 + + +0.000182 +0.000974 +-0.000256 +0.000373 + + + + + + + +0.289769 +0.000063 +0.432356 +0.000055 +0.001291 +0.000068 +-0.003035 +0.000083 + + +-0.0033527 +0.0000474 +-0.0004982 +0.0000252 + + +0.000158 +0.000660 +-0.000259 +0.000269 + + + + + + + +0.291358 +0.000062 +0.429819 +0.000059 +0.001541 +0.000071 +-0.002434 +0.000085 + + +-0.0027933 +0.0000400 +-0.0006287 +0.0000243 + + +0.000185 +0.000374 +-0.000224 +0.000175 + + + + + + + +0.292539 +0.000062 +0.427299 +0.000057 +0.001266 +0.000077 +-0.002521 +0.000095 + + +-0.0020899 +0.0000344 +-0.0007810 +0.0000242 + + +0.000315 +0.000272 +-0.000105 +0.000150 + + + + + + + +0.294147 +0.000064 +0.424781 +0.000059 +0.001781 +0.000077 +-0.002390 +0.000097 + + +-0.0012425 +0.0000302 +-0.0009207 +0.0000247 + + +0.000479 +0.000196 +0.000034 +0.000137 + + + + + + + +0.296042 +0.000063 +0.422672 +0.000061 +0.002072 +0.000078 +-0.002013 +0.000090 + + +-0.0002644 +0.0000263 +-0.0010216 +0.0000248 + + +0.000550 +0.000165 +0.000054 +0.000125 + + + + + + + +0.298312 +0.000060 +0.420663 +0.000061 +0.002250 +0.000077 +-0.001890 +0.000087 + + +0.0007542 +0.0000203 +-0.0010209 +0.0000261 + + +0.000540 +0.000137 +-0.000022 +0.000106 + + + + + + + +0.300336 +0.000060 +0.418954 +0.000060 +0.001884 +0.000074 +-0.001532 +0.000084 + + +0.0017213 +0.0000207 +-0.0008780 +0.0000256 + + +0.000494 +0.000104 +-0.000133 +0.000082 + + + + + diff --git a/src/test/resources/rapid-data-xml/finals.daily.xml b/src/test/resources/eop-xml/finals.daily.xml similarity index 100% rename from src/test/resources/rapid-data-xml/finals.daily.xml rename to src/test/resources/eop-xml/finals.daily.xml diff --git a/src/test/resources/rapid-data-xml/finals2000A.daily.xml b/src/test/resources/eop-xml/finals2000A.daily.xml similarity index 100% rename from src/test/resources/rapid-data-xml/finals2000A.daily.xml rename to src/test/resources/eop-xml/finals2000A.daily.xml diff --git a/src/test/resources/rapid-data-xml/inconsistent-date.xml b/src/test/resources/eop-xml/inconsistent-date.xml similarity index 100% rename from src/test/resources/rapid-data-xml/inconsistent-date.xml rename to src/test/resources/eop-xml/inconsistent-date.xml diff --git a/src/test/resources/rapid-data-xml/malformed.xml b/src/test/resources/eop-xml/malformed.xml similarity index 100% rename from src/test/resources/rapid-data-xml/malformed.xml rename to src/test/resources/eop-xml/malformed.xml diff --git a/src/test/resources/eopc04/eopc04.11 b/src/test/resources/eopc04/eopc04.11 new file mode 100644 index 0000000000..5da0853bfd --- /dev/null +++ b/src/test/resources/eopc04/eopc04.11 @@ -0,0 +1,362 @@ + + + INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE + EARTH ORIENTATION PARAMETERS + EOP (IERS) 05 C04 + + + FORMAT(3(I4),I7,2(F11.6),2(F12.7),2(F11.6),2(F11.6),2(F11.7),2F12.6) +********************************************************************************** + + Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err + " " s s " " " " s s " " + (0h UTC) + +2011 1 1 55562 0.130953 0.203252 -0.1405482 0.0002963 -0.067879 -0.006038 0.000028 0.000024 0.0000600 0.0000033 0.000279 0.000306 +2011 1 2 55563 0.127702 0.202440 -0.1407924 0.0002144 -0.067658 -0.006277 0.000040 0.000025 0.0000533 0.0000032 0.000132 0.000098 +2011 1 3 55564 0.124367 0.201525 -0.1409762 0.0001755 -0.067253 -0.006496 0.000025 0.000024 0.0000220 0.0000031 0.000133 0.000095 +2011 1 4 55565 0.121345 0.200745 -0.1411331 0.0002009 -0.066786 -0.006511 0.000020 0.000024 0.0000165 0.0000031 0.000133 0.000095 +2011 1 5 55566 0.117684 0.200500 -0.1413422 0.0002530 -0.066478 -0.006315 0.000019 0.000026 0.0000210 0.0000030 0.000133 0.000095 +2011 1 6 55567 0.113695 0.199953 -0.1416534 0.0003628 -0.066362 -0.006061 0.000019 0.000031 0.0000390 0.0000029 0.000242 0.000169 +2011 1 7 55568 0.110114 0.199402 -0.1420742 0.0004834 -0.066392 -0.005938 0.000019 0.000031 0.0000284 0.0000030 0.000332 0.000226 +2011 1 8 55569 0.106313 0.198821 -0.1425794 0.0005456 -0.066577 -0.006009 0.000019 0.000026 0.0000396 0.0000031 0.000332 0.000226 +2011 1 9 55570 0.103097 0.197749 -0.1431393 0.0005774 -0.066900 -0.006162 0.000020 0.000026 0.0000530 0.0000031 0.000253 0.000191 +2011 1 10 55571 0.101098 0.196801 -0.1437380 0.0005865 -0.067199 -0.006232 0.000021 0.000026 0.0000275 0.0000031 0.000132 0.000104 +2011 1 11 55572 0.099966 0.196146 -0.1443274 0.0005776 -0.066996 -0.006232 0.000021 0.000026 0.0000152 0.0000031 0.000132 0.000104 +2011 1 12 55573 0.099065 0.196037 -0.1449055 0.0005591 -0.066437 -0.006240 0.000021 0.000027 0.0000190 0.0000031 0.000133 0.000104 +2011 1 13 55574 0.097614 0.196178 -0.1454642 0.0005268 -0.065959 -0.006338 0.000020 0.000033 0.0000531 0.0000030 0.000254 0.000193 +2011 1 14 55575 0.095266 0.196121 -0.1459501 0.0004350 -0.065988 -0.006474 0.000019 0.000046 0.0000459 0.0000030 0.000376 0.000275 +2011 1 15 55576 0.092282 0.195877 -0.1463268 0.0003458 -0.066548 -0.006520 0.000020 0.000030 0.0000502 0.0000031 0.000377 0.000276 +2011 1 16 55577 0.088843 0.195858 -0.1466309 0.0002872 -0.067276 -0.006461 0.000020 0.000025 0.0000550 0.0000031 0.000376 0.000275 +2011 1 17 55578 0.084928 0.195615 -0.1468833 0.0002625 -0.067712 -0.006491 0.000019 0.000030 0.0000475 0.0000030 0.000133 0.000103 +2011 1 18 55579 0.081302 0.195082 -0.1471625 0.0003399 -0.067883 -0.006705 0.000018 0.000032 0.0000335 0.0000030 0.000129 0.000100 +2011 1 19 55580 0.077889 0.194904 -0.1475545 0.0004309 -0.067591 -0.007037 0.000018 0.000032 0.0000199 0.0000030 0.000129 0.000100 +2011 1 20 55581 0.074628 0.194900 -0.1480898 0.0006027 -0.066956 -0.007194 0.000018 0.000031 0.0000224 0.0000029 0.000145 0.000111 +2011 1 21 55582 0.071292 0.194816 -0.1488025 0.0008466 -0.066338 -0.007022 0.000018 0.000033 0.0000341 0.0000029 0.000253 0.000180 +2011 1 22 55583 0.067891 0.194597 -0.1497217 0.0010382 -0.066130 -0.006674 0.000018 0.000046 0.0000420 0.0000029 0.000269 0.000189 +2011 1 23 55584 0.064768 0.194714 -0.1508257 0.0011348 -0.066388 -0.006424 0.000018 0.000028 0.0000588 0.0000028 0.000252 0.000169 +2011 1 24 55585 0.061673 0.195044 -0.1519968 0.0011049 -0.066853 -0.006359 0.000017 0.000022 0.0000273 0.0000026 0.000179 0.000101 +2011 1 25 55586 0.058537 0.195687 -0.1530891 0.0009767 -0.067091 -0.006432 0.000017 0.000021 0.0000151 0.0000025 0.000179 0.000101 +2011 1 26 55587 0.055408 0.196413 -0.1540037 0.0008026 -0.067088 -0.006517 0.000017 0.000021 0.0000178 0.0000026 0.000179 0.000101 +2011 1 27 55588 0.052328 0.197382 -0.1547093 0.0005812 -0.067026 -0.006592 0.000017 0.000022 0.0000523 0.0000027 0.000229 0.000165 +2011 1 28 55589 0.049399 0.198174 -0.1552187 0.0003745 -0.067100 -0.006694 0.000016 0.000021 0.0000494 0.0000027 0.000246 0.000205 +2011 1 29 55590 0.047494 0.198671 -0.1555753 0.0003220 -0.067311 -0.006844 0.000017 0.000020 0.0000527 0.0000027 0.000246 0.000205 +2011 1 30 55591 0.046056 0.199382 -0.1558724 0.0003190 -0.067467 -0.007026 0.000016 0.000019 0.0000550 0.0000025 0.000236 0.000176 +2011 1 31 55592 0.045066 0.199929 -0.1561907 0.0003288 -0.067363 -0.007206 0.000014 0.000019 0.0000244 0.0000023 0.000138 0.000080 +2011 2 1 55593 0.044412 0.200694 -0.1565715 0.0004128 -0.067200 -0.007249 0.000014 0.000018 0.0000169 0.0000023 0.000138 0.000080 +2011 2 2 55594 0.043629 0.201569 -0.1570587 0.0005610 -0.067098 -0.007110 0.000014 0.000018 0.0000259 0.0000023 0.000136 0.000097 +2011 2 3 55595 0.042784 0.202346 -0.1576755 0.0006886 -0.067067 -0.006852 0.000014 0.000019 0.0000439 0.0000022 0.000197 0.000199 +2011 2 4 55596 0.041522 0.202697 -0.1584183 0.0007761 -0.066984 -0.006636 0.000014 0.000020 0.0000323 0.0000022 0.000224 0.000337 +2011 2 5 55597 0.040192 0.202971 -0.1592430 0.0008034 -0.066813 -0.006611 0.000014 0.000024 0.0000374 0.0000021 0.000224 0.000337 +2011 2 6 55598 0.039014 0.203474 -0.1600169 0.0007710 -0.066686 -0.006768 0.000014 0.000023 0.0000554 0.0000021 0.000199 0.000251 +2011 2 7 55599 0.038196 0.204393 -0.1607630 0.0007304 -0.066752 -0.006933 0.000017 0.000019 0.0000392 0.0000021 0.000123 0.000118 +2011 2 8 55600 0.037467 0.205835 -0.1614627 0.0006615 -0.066848 -0.007018 0.000018 0.000018 0.0000263 0.0000020 0.000123 0.000118 +2011 2 9 55601 0.036207 0.207353 -0.1620783 0.0005865 -0.066860 -0.007088 0.000014 0.000017 0.0000315 0.0000020 0.000123 0.000118 +2011 2 10 55602 0.034230 0.208704 -0.1626338 0.0005097 -0.066820 -0.007287 0.000013 0.000017 0.0000455 0.0000021 0.000242 0.000201 +2011 2 11 55603 0.032079 0.209652 -0.1630947 0.0004255 -0.066874 -0.007611 0.000013 0.000018 0.0000412 0.0000021 0.000374 0.000256 +2011 2 12 55604 0.030642 0.210548 -0.1634700 0.0003789 -0.067068 -0.007862 0.000014 0.000018 0.0000516 0.0000021 0.000374 0.000256 +2011 2 13 55605 0.029417 0.211674 -0.1638102 0.0003669 -0.067323 -0.007889 0.000014 0.000019 0.0000769 0.0000022 0.000377 0.000254 +2011 2 14 55606 0.027713 0.212466 -0.1641771 0.0004180 -0.067569 -0.007829 0.000015 0.000020 0.0000543 0.0000022 0.000399 0.000222 +2011 2 15 55607 0.026111 0.213212 -0.1646638 0.0005869 -0.067700 -0.007994 0.000015 0.000019 0.0000407 0.0000022 0.000399 0.000221 +2011 2 16 55608 0.024876 0.214497 -0.1653806 0.0008737 -0.067603 -0.008374 0.000015 0.000015 0.0000514 0.0000023 0.000394 0.000221 +2011 2 17 55609 0.023428 0.216345 -0.1663826 0.0011625 -0.067202 -0.008603 0.000014 0.000015 0.0000599 0.0000023 0.000244 0.000223 +2011 2 18 55610 0.021729 0.217954 -0.1676682 0.0013996 -0.066706 -0.008373 0.000014 0.000019 0.0000419 0.0000023 0.000229 0.000223 +2011 2 19 55611 0.020042 0.218966 -0.1691499 0.0015993 -0.066470 -0.007846 0.000014 0.000026 0.0000480 0.0000022 0.000229 0.000223 +2011 2 20 55612 0.018505 0.220088 -0.1707659 0.0016564 -0.066603 -0.007485 0.000015 0.000040 0.0000567 0.0000023 0.000208 0.000180 +2011 2 21 55613 0.016797 0.220835 -0.1723814 0.0015182 -0.066880 -0.007523 0.000015 0.000016 0.0000449 0.0000023 0.000157 0.000122 +2011 2 22 55614 0.015164 0.221326 -0.1738280 0.0012907 -0.067241 -0.007745 0.000016 0.000007 0.0000321 0.0000023 0.000157 0.000122 +2011 2 23 55615 0.013781 0.222041 -0.1750060 0.0010020 -0.067537 -0.007951 0.000016 0.000007 0.0000393 0.0000023 0.000157 0.000123 +2011 2 24 55616 0.012247 0.223010 -0.1758540 0.0007099 -0.067669 -0.008123 0.000021 0.000014 0.0000667 0.0000022 0.000243 0.000203 +2011 2 25 55617 0.010152 0.224063 -0.1764729 0.0005412 -0.067576 -0.008327 0.000020 0.000020 0.0000580 0.0000022 0.000286 0.000251 +2011 2 26 55618 0.007600 0.224829 -0.1769643 0.0004583 -0.067345 -0.008539 0.000016 0.000021 0.0000588 0.0000023 0.000286 0.000251 +2011 2 27 55619 0.004925 0.225756 -0.1773942 0.0004328 -0.067154 -0.008694 0.000014 0.000020 0.0000550 0.0000022 0.000273 0.000231 +2011 2 28 55620 0.002579 0.227009 -0.1778481 0.0005256 -0.067166 -0.008772 0.000013 0.000019 0.0000415 0.0000021 0.000267 0.000139 +2011 3 1 55621 0.000808 0.228337 -0.1784503 0.0006834 -0.067204 -0.008798 0.000013 0.000024 0.0000324 0.0000021 0.000267 0.000139 +2011 3 2 55622 -0.000731 0.229462 -0.1792062 0.0008108 -0.067210 -0.008741 0.000013 0.000023 0.0000392 0.0000021 0.000260 0.000135 +2011 3 3 55623 -0.001842 0.230601 -0.1800931 0.0009709 -0.067164 -0.008589 0.000013 0.000019 0.0000622 0.0000020 0.000465 0.000267 +2011 3 4 55624 -0.002663 0.232054 -0.1811521 0.0011178 -0.067095 -0.008359 0.000013 0.000019 0.0000610 0.0000020 0.000642 0.000431 +2011 3 5 55625 -0.003099 0.233584 -0.1823186 0.0011983 -0.067030 -0.008214 0.000013 0.000023 0.0000569 0.0000021 0.000643 0.000432 +2011 3 6 55626 -0.003439 0.235611 -0.1835250 0.0011797 -0.066961 -0.008320 0.000014 0.000023 0.0000534 0.0000022 0.000595 0.000313 +2011 3 7 55627 -0.004519 0.237628 -0.1846900 0.0010787 -0.066863 -0.008659 0.000014 0.000019 0.0000280 0.0000022 0.000416 0.000139 +2011 3 8 55628 -0.005841 0.239035 -0.1857529 0.0009908 -0.066563 -0.009036 0.000014 0.000020 0.0000183 0.0000023 0.000415 0.000139 +2011 3 9 55629 -0.006998 0.240602 -0.1867170 0.0009019 -0.066105 -0.009355 0.000014 0.000025 0.0000241 0.0000023 0.000138 0.000109 +2011 3 10 55630 -0.007854 0.242392 -0.1875945 0.0008240 -0.065748 -0.009548 0.000014 0.000038 0.0000555 0.0000023 0.000233 0.000185 +2011 3 11 55631 -0.008914 0.244361 -0.1884102 0.0007478 -0.065842 -0.009706 0.000014 0.000024 0.0000491 0.0000022 0.000297 0.000236 +2011 3 12 55632 -0.010382 0.246298 -0.1891271 0.0006411 -0.066446 -0.009787 0.000014 0.000019 0.0000527 0.0000022 0.000297 0.000236 +2011 3 13 55633 -0.011773 0.248111 -0.1897081 0.0005711 -0.067227 -0.009718 0.000013 0.000023 0.0000784 0.0000020 0.000297 0.000236 +2011 3 14 55634 -0.013537 0.249789 -0.1903037 0.0006450 -0.067760 -0.009590 0.000012 0.000022 0.0000697 0.0000020 0.000381 0.000305 +2011 3 15 55635 -0.015304 0.250708 -0.1910373 0.0008472 -0.067809 -0.009646 0.000012 0.000021 0.0000490 0.0000020 0.000391 0.000313 +2011 3 16 55636 -0.016839 0.251756 -0.1919995 0.0010923 -0.067423 -0.009915 0.000012 0.000022 0.0000448 0.0000020 0.000319 0.000278 +2011 3 17 55637 -0.018376 0.253176 -0.1932353 0.0013521 -0.066687 -0.010140 0.000012 0.000017 0.0000630 0.0000020 0.000313 0.000290 +2011 3 18 55638 -0.019580 0.254529 -0.1946574 0.0015329 -0.065978 -0.009938 0.000012 0.000016 0.0000632 0.0000019 0.000296 0.000330 +2011 3 19 55639 -0.020701 0.256107 -0.1962325 0.0016111 -0.065653 -0.009413 0.000012 0.000021 0.0000653 0.0000019 0.000296 0.000333 +2011 3 20 55640 -0.021693 0.257773 -0.1978521 0.0015883 -0.065769 -0.009063 0.000012 0.000034 0.0000947 0.0000019 0.000283 0.000305 +2011 3 21 55641 -0.022825 0.259873 -0.1993772 0.0014545 -0.066024 -0.009195 0.000013 0.000022 0.0000815 0.0000020 0.000225 0.000213 +2011 3 22 55642 -0.024481 0.261911 -0.2007277 0.0012179 -0.066262 -0.009574 0.000013 0.000017 0.0000612 0.0000021 0.000225 0.000213 +2011 3 23 55643 -0.025785 0.263563 -0.2018508 0.0009497 -0.066467 -0.009915 0.000013 0.000016 0.0000678 0.0000021 0.000225 0.000213 +2011 3 24 55644 -0.026903 0.265372 -0.2027581 0.0008152 -0.066564 -0.010149 0.000013 0.000016 0.0000774 0.0000020 0.000243 0.000211 +2011 3 25 55645 -0.027257 0.267146 -0.2035745 0.0008376 -0.066439 -0.010348 0.000012 0.000016 0.0000588 0.0000020 0.000247 0.000211 +2011 3 26 55646 -0.027502 0.269052 -0.2044271 0.0008786 -0.066185 -0.010492 0.000012 0.000016 0.0000591 0.0000020 0.000247 0.000211 +2011 3 27 55647 -0.028491 0.270867 -0.2053144 0.0009498 -0.066119 -0.010512 0.000013 0.000016 0.0000894 0.0000021 0.000253 0.000217 +2011 3 28 55648 -0.029423 0.272754 -0.2063424 0.0010942 -0.066305 -0.010469 0.000013 0.000017 0.0000448 0.0000021 0.000264 0.000192 +2011 3 29 55649 -0.030355 0.274394 -0.2075307 0.0012470 -0.066508 -0.010401 0.000012 0.000017 0.0000310 0.0000021 0.000264 0.000192 +2011 3 30 55650 -0.030987 0.275855 -0.2088558 0.0013638 -0.066496 -0.010366 0.000012 0.000017 0.0000466 0.0000021 0.000203 0.000188 +2011 3 31 55651 -0.031769 0.277560 -0.2102534 0.0014327 -0.066242 -0.010293 0.000013 0.000018 0.0000499 0.0000021 0.000215 0.000199 +2011 4 1 55652 -0.033100 0.279169 -0.2117101 0.0014790 -0.065935 -0.010166 0.000013 0.000022 0.0000344 0.0000022 0.000218 0.000202 +2011 4 2 55653 -0.034766 0.280724 -0.2132062 0.0014919 -0.065715 -0.010130 0.000013 0.000022 0.0000403 0.0000022 0.000218 0.000202 +2011 4 3 55654 -0.036255 0.282023 -0.2146919 0.0014377 -0.065597 -0.010369 0.000012 0.000017 0.0000674 0.0000020 0.000218 0.000202 +2011 4 4 55655 -0.037489 0.283688 -0.2160635 0.0013023 -0.065444 -0.010867 0.000011 0.000015 0.0000763 0.0000018 0.000249 0.000202 +2011 4 5 55656 -0.039380 0.285549 -0.2172703 0.0011326 -0.065257 -0.011385 0.000011 0.000016 0.0000724 0.0000018 0.000403 0.000260 +2011 4 6 55657 -0.041469 0.287080 -0.2183374 0.0009757 -0.065369 -0.011636 0.000011 0.000017 0.0000746 0.0000018 0.000414 0.000264 +2011 4 7 55658 -0.042435 0.288778 -0.2192399 0.0008177 -0.065088 -0.011608 0.000011 0.000021 0.0000661 0.0000018 0.000414 0.000264 +2011 4 8 55659 -0.042977 0.291017 -0.2199891 0.0007268 -0.064937 -0.011459 0.000011 0.000019 0.0000483 0.0000018 0.000414 0.000264 +2011 4 9 55660 -0.043413 0.293514 -0.2207308 0.0007614 -0.064982 -0.011354 0.000011 0.000016 0.0000581 0.0000018 0.000413 0.000264 +2011 4 10 55661 -0.043344 0.295781 -0.2215086 0.0008275 -0.065135 -0.011329 0.000011 0.000021 0.0000817 0.0000017 0.000345 0.000262 +2011 4 11 55662 -0.043623 0.297745 -0.2223474 0.0009143 -0.065245 -0.011397 0.000011 0.000034 0.0000483 0.0000017 0.000191 0.000196 +2011 4 12 55663 -0.043976 0.299666 -0.2233127 0.0010898 -0.065262 -0.011448 0.000011 0.000034 0.0000332 0.0000017 0.000190 0.000195 +2011 4 13 55664 -0.044064 0.301936 -0.2244755 0.0012686 -0.065173 -0.011516 0.000011 0.000018 0.0000402 0.0000017 0.000253 0.000162 +2011 4 14 55665 -0.044236 0.304276 -0.2258149 0.0014187 -0.065030 -0.011508 0.000010 0.000018 0.0000575 0.0000016 0.000256 0.000208 +2011 4 15 55666 -0.044091 0.306762 -0.2273045 0.0015802 -0.065056 -0.011254 0.000013 0.000030 0.0000532 0.0000016 0.000257 0.000223 +2011 4 16 55667 -0.043910 0.309467 -0.2289318 0.0016714 -0.065430 -0.010852 0.000013 0.000019 0.0000533 0.0000011 0.000257 0.000223 +2011 4 17 55668 -0.044070 0.312039 -0.2305879 0.0016123 -0.066042 -0.010641 0.000009 0.000013 0.0000307 0.0000005 0.000233 0.000209 +2011 4 18 55669 -0.044300 0.314427 -0.2321248 0.0014391 -0.066043 -0.010768 0.000008 0.000011 0.0000216 0.0000004 0.000193 0.000145 +2011 4 19 55670 -0.044565 0.316440 -0.2334590 0.0012204 -0.066021 -0.011085 0.000008 0.000011 0.0000196 0.0000004 0.000195 0.000151 +2011 4 20 55671 -0.044680 0.318471 -0.2345620 0.0009763 -0.065816 -0.011292 0.000008 0.000011 0.0000200 0.0000004 0.000195 0.000182 +2011 4 21 55672 -0.044974 0.320537 -0.2354486 0.0007900 -0.065430 -0.011370 0.000008 0.000011 0.0000202 0.0000004 0.000209 0.000215 +2011 4 22 55673 -0.045106 0.322299 -0.2361944 0.0007021 -0.064921 -0.011464 0.000008 0.000011 0.0000199 0.0000004 0.000212 0.000223 +2011 4 23 55674 -0.045238 0.324007 -0.2369136 0.0007207 -0.064531 -0.011613 0.000008 0.000011 0.0000213 0.0000004 0.000212 0.000225 +2011 4 24 55675 -0.045893 0.325902 -0.2376991 0.0008310 -0.064598 -0.011725 0.000008 0.000011 0.0000219 0.0000004 0.000213 0.000220 +2011 4 25 55676 -0.046205 0.327592 -0.2385597 0.0009196 -0.065126 -0.011746 0.000007 0.000010 0.0000205 0.0000004 0.000190 0.000202 +2011 4 26 55677 -0.046046 0.329276 -0.2395221 0.0010006 -0.065695 -0.011737 0.000004 0.000009 0.0000196 0.0000004 0.000190 0.000202 +2011 4 27 55678 -0.045788 0.331401 -0.2405569 0.0010745 -0.065843 -0.011704 0.000004 0.000009 0.0000184 0.0000004 0.000190 0.000202 +2011 4 28 55679 -0.045359 0.333686 -0.2416499 0.0011133 -0.065560 -0.011589 0.000006 0.000010 0.0000183 0.0000004 0.000207 0.000209 +2011 4 29 55680 -0.045138 0.336236 -0.2427742 0.0011360 -0.065132 -0.011364 0.000008 0.000011 0.0000184 0.0000004 0.000291 0.000233 +2011 4 30 55681 -0.044728 0.338611 -0.2438794 0.0010802 -0.064826 -0.011147 0.000008 0.000011 0.0000190 0.0000004 0.000300 0.000235 +2011 5 1 55682 -0.044052 0.341214 -0.2449190 0.0009972 -0.064749 -0.011128 0.000008 0.000011 0.0000202 0.0000004 0.000274 0.000216 +2011 5 2 55683 -0.043230 0.344119 -0.2459043 0.0009540 -0.064907 -0.011395 0.000007 0.000010 0.0000199 0.0000004 0.000153 0.000175 +2011 5 3 55684 -0.042712 0.346842 -0.2468435 0.0009194 -0.065242 -0.011743 0.000007 0.000009 0.0000190 0.0000004 0.000153 0.000175 +2011 5 4 55685 -0.042441 0.349160 -0.2477514 0.0008839 -0.065581 -0.011972 0.000007 0.000009 0.0000190 0.0000004 0.000153 0.000176 +2011 5 5 55686 -0.041948 0.351350 -0.2486033 0.0008154 -0.065712 -0.011997 0.000007 0.000009 0.0000201 0.0000004 0.000297 0.000277 +2011 5 6 55687 -0.041650 0.353535 -0.2494044 0.0007927 -0.065555 -0.011907 0.000007 0.000009 0.0000194 0.0000004 0.000456 0.000335 +2011 5 7 55688 -0.041533 0.355438 -0.2502107 0.0008421 -0.065257 -0.011838 0.000007 0.000010 0.0000189 0.0000004 0.000457 0.000335 +2011 5 8 55689 -0.041713 0.357396 -0.2510712 0.0009191 -0.065077 -0.011859 0.000007 0.000010 0.0000192 0.0000004 0.000411 0.000307 +2011 5 9 55690 -0.041837 0.359077 -0.2520906 0.0011287 -0.065158 -0.011969 0.000008 0.000010 0.0000191 0.0000004 0.000197 0.000189 +2011 5 10 55691 -0.041186 0.360815 -0.2533298 0.0013642 -0.065421 -0.012116 0.000008 0.000009 0.0000186 0.0000004 0.000196 0.000189 +2011 5 11 55692 -0.039893 0.362991 -0.2547744 0.0015334 -0.065617 -0.012203 0.000007 0.000009 0.0000187 0.0000004 0.000200 0.000198 +2011 5 12 55693 -0.038486 0.365504 -0.2563931 0.0016986 -0.065613 -0.012121 0.000007 0.000008 0.0000191 0.0000004 0.000185 0.000177 +2011 5 13 55694 -0.036629 0.368305 -0.2581554 0.0018020 -0.065464 -0.011794 0.000007 0.000009 0.0000174 0.0000004 0.000182 0.000173 +2011 5 14 55695 -0.034767 0.371219 -0.2599834 0.0018233 -0.065369 -0.011354 0.000008 0.000009 0.0000184 0.0000004 0.000182 0.000173 +2011 5 15 55696 -0.033421 0.374180 -0.2617820 0.0017417 -0.065493 -0.011093 0.000007 0.000009 0.0000219 0.0000004 0.000185 0.000178 +2011 5 16 55697 -0.032615 0.376476 -0.2634292 0.0015346 -0.065836 -0.011196 0.000007 0.000009 0.0000214 0.0000003 0.000210 0.000199 +2011 5 17 55698 -0.031956 0.378068 -0.2648389 0.0012878 -0.066089 -0.011480 0.000007 0.000009 0.0000200 0.0000003 0.000210 0.000199 +2011 5 18 55699 -0.031294 0.380049 -0.2660096 0.0010628 -0.066189 -0.011714 0.000007 0.000009 0.0000199 0.0000003 0.000161 0.000174 +2011 5 19 55700 -0.030744 0.381739 -0.2669764 0.0008848 -0.066023 -0.011701 0.000007 0.000009 0.0000200 0.0000003 0.000157 0.000142 +2011 5 20 55701 -0.029852 0.383302 -0.2677958 0.0007784 -0.065573 -0.011537 0.000007 0.000009 0.0000182 0.0000003 0.000156 0.000138 +2011 5 21 55702 -0.029015 0.384884 -0.2685682 0.0007803 -0.065085 -0.011373 0.000007 0.000009 0.0000188 0.0000003 0.000156 0.000138 +2011 5 22 55703 -0.028087 0.386263 -0.2693669 0.0008248 -0.064965 -0.011315 0.000007 0.000009 0.0000219 0.0000003 0.000161 0.000143 +2011 5 23 55704 -0.026823 0.387795 -0.2702082 0.0008703 -0.065370 -0.011458 0.000007 0.000009 0.0000218 0.0000003 0.000239 0.000183 +2011 5 24 55705 -0.025438 0.389307 -0.2710953 0.0009147 -0.065820 -0.011736 0.000007 0.000009 0.0000209 0.0000003 0.000240 0.000184 +2011 5 25 55706 -0.023722 0.390933 -0.2719963 0.0008942 -0.065904 -0.012028 0.000007 0.000009 0.0000208 0.0000003 0.000240 0.000184 +2011 5 26 55707 -0.021973 0.393153 -0.2729198 0.0009488 -0.065633 -0.012154 0.000007 0.000009 0.0000203 0.0000003 0.000353 0.000239 +2011 5 27 55708 -0.020353 0.395679 -0.2738958 0.0009954 -0.065410 -0.012046 0.000007 0.000009 0.0000193 0.0000003 0.000403 0.000259 +2011 5 28 55709 -0.019057 0.397409 -0.2748641 0.0009381 -0.065591 -0.011814 0.000007 0.000009 0.0000199 0.0000003 0.000404 0.000259 +2011 5 29 55710 -0.017965 0.398983 -0.2757722 0.0008786 -0.066166 -0.011662 0.000007 0.000009 0.0000212 0.0000003 0.000403 0.000259 +2011 5 30 55711 -0.017307 0.400758 -0.2765874 0.0007490 -0.067012 -0.011687 0.000007 0.000009 0.0000204 0.0000003 0.000284 0.000194 +2011 5 31 55712 -0.016517 0.402071 -0.2772766 0.0006180 -0.067595 -0.011797 0.000007 0.000009 0.0000201 0.0000003 0.000278 0.000190 +2011 6 1 55713 -0.015285 0.403299 -0.2778773 0.0005584 -0.067754 -0.011853 0.000007 0.000009 0.0000206 0.0000003 0.000269 0.000191 +2011 6 2 55714 -0.014268 0.404547 -0.2784186 0.0005064 -0.067552 -0.011811 0.000007 0.000009 0.0000203 0.0000003 0.000255 0.000207 +2011 6 3 55715 -0.013072 0.405759 -0.2789167 0.0004629 -0.067170 -0.011704 0.000007 0.000009 0.0000178 0.0000003 0.000226 0.000287 +2011 6 4 55716 -0.011657 0.407526 -0.2794505 0.0005529 -0.066977 -0.011602 0.000007 0.000009 0.0000137 0.0000003 0.000225 0.000295 +2011 6 5 55717 -0.009801 0.409377 -0.2800969 0.0007515 -0.067213 -0.011550 0.000009 0.000012 0.0000153 0.0000004 0.000201 0.000229 +2011 6 6 55718 -0.007727 0.410862 -0.2808795 0.0008981 -0.067742 -0.011559 0.000007 0.000006 0.0000117 0.0000003 0.000131 0.000044 +2011 6 7 55719 -0.005617 0.412313 -0.2818613 0.0010632 -0.068220 -0.011615 0.000007 0.000003 0.0000074 0.0000003 0.000131 0.000044 +2011 6 8 55720 -0.003236 0.414051 -0.2829846 0.0011684 -0.068280 -0.011651 0.000007 0.000008 0.0000095 0.0000003 0.000132 0.000044 +2011 6 9 55721 -0.000945 0.415754 -0.2841601 0.0011581 -0.067937 -0.011573 0.000007 0.000009 0.0000173 0.0000003 0.000199 0.000095 +2011 6 10 55722 0.001710 0.416911 -0.2853026 0.0010985 -0.067514 -0.011330 0.000007 0.000009 0.0000173 0.0000003 0.000233 0.000190 +2011 6 11 55723 0.005013 0.418239 -0.2863529 0.0009884 -0.067356 -0.011000 0.000007 0.000009 0.0000180 0.0000003 0.000233 0.000191 +2011 6 12 55724 0.008016 0.419898 -0.2872437 0.0007859 -0.067558 -0.010778 0.000006 0.000008 0.0000209 0.0000003 0.000233 0.000191 +2011 6 13 55725 0.010619 0.421161 -0.2879334 0.0005852 -0.067968 -0.010794 0.000005 0.000007 0.0000204 0.0000003 0.000193 0.000172 +2011 6 14 55726 0.013150 0.422247 -0.2884425 0.0004348 -0.068384 -0.010989 0.000005 0.000007 0.0000200 0.0000003 0.000190 0.000171 +2011 6 15 55727 0.015192 0.423013 -0.2887917 0.0002763 -0.068649 -0.011168 0.000005 0.000007 0.0000203 0.0000003 0.000191 0.000171 +2011 6 16 55728 0.017037 0.423607 -0.2890157 0.0001875 -0.068627 -0.011176 0.000005 0.000007 0.0000200 0.0000003 0.000202 0.000185 +2011 6 17 55729 0.018934 0.424489 -0.2891967 0.0001956 -0.068398 -0.011009 0.000005 0.000007 0.0000171 0.0000003 0.000245 0.000240 +2011 6 18 55730 0.020825 0.425476 -0.2894110 0.0002691 -0.068376 -0.010795 0.000006 0.000008 0.0000131 0.0000003 0.000248 0.000245 +2011 6 19 55731 0.022828 0.426503 -0.2897023 0.0003555 -0.068848 -0.010703 0.000009 0.000013 0.0000153 0.0000004 0.000259 0.000257 +2011 6 20 55732 0.025051 0.427144 -0.2900685 0.0003963 -0.069499 -0.010817 0.000024 0.000030 0.0000339 0.0000012 0.000408 0.000423 +2011 6 21 55733 0.027181 0.427804 -0.2904892 0.0004019 -0.069661 -0.011086 0.000050 0.000050 0.0000825 0.0000055 0.000446 0.000451 +2011 6 22 55734 0.029133 0.428499 -0.2909151 0.0003765 -0.068750 -0.011365 0.000066 0.000047 0.0001154 0.0000083 0.001471 0.000747 +2011 6 23 55735 0.031075 0.429288 -0.2912812 0.0003306 -0.068894 -0.011425 0.000057 0.000047 0.0001312 0.0000116 0.001471 0.000747 +2011 6 24 55736 0.032763 0.430184 -0.2915483 0.0002425 -0.068997 -0.011310 0.000054 0.000056 0.0000914 0.0000131 0.001471 0.000747 +2011 6 25 55737 0.033714 0.430868 -0.2916878 0.0001185 -0.069252 -0.011135 0.000067 0.000053 0.0000635 0.0000158 0.001471 0.000747 +2011 6 26 55738 0.034721 0.431559 -0.2917259 0.0000110 -0.069709 -0.011031 0.000106 0.000059 0.0000774 0.0000244 0.001471 0.000747 +2011 6 27 55739 0.036635 0.432242 -0.2917264 -0.0000651 -0.070254 -0.011032 0.000067 0.000064 0.0001019 0.0000233 0.001471 0.000747 +2011 6 28 55740 0.038536 0.433091 -0.2916342 -0.0001363 -0.070735 -0.011076 0.000066 0.000073 0.0001192 0.0000180 0.001471 0.000747 +2011 6 29 55741 0.040230 0.433850 -0.2914248 -0.0002027 -0.071048 -0.011106 0.000129 0.000079 0.0001285 0.0000148 0.001471 0.000747 +2011 6 30 55742 0.042132 0.434691 -0.2911828 -0.0002797 -0.071139 -0.011132 0.000139 0.000082 0.0001323 0.0000132 0.001471 0.000747 +2011 7 1 55743 0.043755 0.435604 -0.2908970 -0.0002918 -0.071010 -0.011185 0.000075 0.000104 0.0001260 0.0000144 0.001471 0.000747 +2011 7 2 55744 0.045282 0.436426 -0.2906691 -0.0001801 -0.070760 -0.011236 0.000074 0.000114 0.0001291 0.0000184 0.001471 0.000747 +2011 7 3 55745 0.046690 0.437195 -0.2905240 0.0000154 -0.070585 -0.011196 0.000125 0.000102 0.0001557 0.0000166 0.001471 0.000747 +2011 7 4 55746 0.048346 0.437909 -0.2906765 0.0002643 -0.070677 -0.011023 0.000100 0.000082 0.0002334 0.0000102 0.001471 0.000747 +2011 7 5 55747 0.050433 0.439020 -0.2910896 0.0004549 -0.071077 -0.010782 0.000103 0.000088 0.0003386 0.0000037 0.001471 0.000747 +2011 7 6 55748 0.052629 0.440137 -0.2916145 0.0005111 -0.071626 -0.010595 0.000200 0.000125 0.0003587 0.0000014 0.001471 0.000747 +2011 7 7 55749 0.055034 0.441029 -0.2921806 0.0004612 -0.072077 -0.010521 0.000224 0.000160 0.0005178 0.0000014 0.001471 0.000747 +2011 7 8 55750 0.057527 0.441765 -0.2926817 0.0003049 -0.072272 -0.010532 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 9 55751 0.059764 0.442377 -0.2930257 0.0001004 -0.072313 -0.010522 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 10 55752 0.061673 0.442883 -0.2931314 -0.0001225 -0.072388 -0.010508 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 11 55753 0.063582 0.443270 -0.2930126 -0.0003189 -0.072628 -0.010556 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 12 55754 0.065589 0.443809 -0.2927091 -0.0004516 -0.072968 -0.010705 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 13 55755 0.067524 0.444330 -0.2923096 -0.0005005 -0.073213 -0.010895 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 14 55756 0.069428 0.444729 -0.2918925 -0.0004668 -0.073223 -0.011001 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 15 55757 0.071367 0.445201 -0.2915313 -0.0003729 -0.073043 -0.010941 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 16 55758 0.073205 0.445617 -0.2912777 -0.0002531 -0.072850 -0.010759 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 17 55759 0.074805 0.445978 -0.2911327 -0.0001423 -0.072813 -0.010605 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 18 55760 0.076356 0.446214 -0.2910784 -0.0000652 -0.072988 -0.010608 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 19 55761 0.077981 0.446345 -0.2910783 -0.0000338 -0.073326 -0.010765 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 20 55762 0.079424 0.446547 -0.2910811 -0.0000500 -0.073715 -0.010942 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 21 55763 0.080871 0.446756 -0.2910411 -0.0001088 -0.074029 -0.011000 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 22 55764 0.082595 0.447021 -0.2909233 -0.0002016 -0.074207 -0.010926 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 23 55765 0.084465 0.447157 -0.2906998 -0.0003152 -0.074330 -0.010834 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 24 55766 0.086282 0.447140 -0.2903579 -0.0004325 -0.074562 -0.010840 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 25 55767 0.087977 0.447127 -0.2898991 -0.0005340 -0.074979 -0.010935 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 26 55768 0.089644 0.447156 -0.2893567 -0.0005998 -0.075462 -0.011014 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 27 55769 0.091169 0.447152 -0.2887756 -0.0006109 -0.075799 -0.011019 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 28 55770 0.092696 0.447011 -0.2882148 -0.0005517 -0.075862 -0.011020 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 29 55771 0.094422 0.446794 -0.2877492 -0.0004152 -0.075674 -0.011106 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 30 55772 0.096455 0.446691 -0.2874598 -0.0002119 -0.075358 -0.011224 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 7 31 55773 0.098757 0.446687 -0.2873897 0.0000243 -0.075093 -0.011194 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 1 55774 0.101088 0.446813 -0.2875491 0.0002398 -0.075090 -0.010921 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 2 55775 0.103340 0.446982 -0.2878918 0.0003780 -0.075479 -0.010533 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 3 55776 0.105402 0.447084 -0.2883192 0.0004006 -0.076172 -0.010266 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 4 55777 0.107312 0.447038 -0.2887031 0.0003043 -0.076861 -0.010244 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 5 55778 0.109005 0.446841 -0.2889490 0.0001209 -0.077239 -0.010396 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 6 55779 0.110615 0.446511 -0.2889902 -0.0000964 -0.077229 -0.010580 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 7 55780 0.112408 0.446146 -0.2888153 -0.0002933 -0.077039 -0.010716 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 8 55781 0.114159 0.445868 -0.2884738 -0.0004292 -0.076955 -0.010811 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 9 55782 0.115851 0.445515 -0.2880343 -0.0004846 -0.077085 -0.010907 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 10 55783 0.117440 0.445281 -0.2875826 -0.0004597 -0.077288 -0.011012 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 11 55784 0.118912 0.444962 -0.2871916 -0.0003711 -0.077365 -0.011083 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 12 55785 0.120336 0.444559 -0.2869076 -0.0002458 -0.077277 -0.011052 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 13 55786 0.121735 0.444165 -0.2867583 -0.0001153 -0.077147 -0.010892 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 14 55787 0.123273 0.443701 -0.2867297 -0.0000079 -0.077100 -0.010666 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 15 55788 0.124808 0.443318 -0.2867878 0.0000571 -0.077183 -0.010498 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 16 55789 0.126327 0.442894 -0.2868851 0.0000722 -0.077406 -0.010477 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 17 55790 0.127844 0.442454 -0.2869753 0.0000397 -0.077733 -0.010580 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 18 55791 0.129412 0.442045 -0.2870143 -0.0000311 -0.078027 -0.010696 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 19 55792 0.130891 0.441648 -0.2869696 -0.0001268 -0.078114 -0.010761 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 20 55793 0.132233 0.441308 -0.2868241 -0.0002310 -0.077997 -0.010825 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 21 55794 0.133674 0.440912 -0.2865778 -0.0003254 -0.077926 -0.010981 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 22 55795 0.135101 0.440496 -0.2862496 -0.0003910 -0.078144 -0.011210 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 23 55796 0.136446 0.440086 -0.2858804 -0.0004098 -0.078578 -0.011371 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 24 55797 0.137869 0.439576 -0.2855208 -0.0003656 -0.078893 -0.011359 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 25 55798 0.139388 0.439016 -0.2852441 -0.0002467 -0.078857 -0.011253 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 26 55799 0.140717 0.438478 -0.2851262 -0.0000528 -0.078542 -0.011222 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 27 55800 0.142011 0.437952 -0.2852342 0.0001966 -0.078180 -0.011289 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 28 55801 0.143417 0.437319 -0.2856027 0.0004576 -0.077963 -0.011275 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 29 55802 0.144755 0.436635 -0.2862130 0.0006692 -0.077995 -0.011037 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 30 55803 0.146081 0.435933 -0.2869853 0.0007743 -0.078289 -0.010687 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 8 31 55804 0.147327 0.435359 -0.2877994 0.0007439 -0.078729 -0.010460 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 1 55805 0.148610 0.434713 -0.2885195 0.0005927 -0.079150 -0.010433 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 2 55806 0.149927 0.433934 -0.2890521 0.0003733 -0.079453 -0.010501 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 3 55807 0.151103 0.433134 -0.2893577 0.0001538 -0.079592 -0.010580 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 4 55808 0.152252 0.432224 -0.2894732 -0.0000087 -0.079521 -0.010693 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 5 55809 0.153387 0.431446 -0.2894646 -0.0000850 -0.079268 -0.010849 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 6 55810 0.154502 0.430706 -0.2894247 -0.0000746 -0.078994 -0.010970 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 7 55811 0.155453 0.429961 -0.2894324 0.0000035 -0.078842 -0.010983 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 8 55812 0.156202 0.429191 -0.2895464 0.0001224 -0.078797 -0.010904 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 9 55813 0.156982 0.428401 -0.2897851 0.0002543 -0.078759 -0.010786 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 10 55814 0.158023 0.427634 -0.2901558 0.0003737 -0.078699 -0.010637 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 11 55815 0.159166 0.426926 -0.2906305 0.0004602 -0.078663 -0.010441 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 12 55816 0.160240 0.426140 -0.2911690 0.0005006 -0.078702 -0.010235 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 13 55817 0.161278 0.425212 -0.2917263 0.0004916 -0.078847 -0.010133 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 14 55818 0.162278 0.424258 -0.2922513 0.0004392 -0.079083 -0.010212 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 15 55819 0.163221 0.423291 -0.2927101 0.0003562 -0.079291 -0.010416 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 16 55820 0.163984 0.422284 -0.2930763 0.0002594 -0.079283 -0.010622 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 17 55821 0.164786 0.421239 -0.2933491 0.0001668 -0.079018 -0.010784 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 18 55822 0.165893 0.420217 -0.2935379 0.0000963 -0.078733 -0.010964 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 19 55823 0.167089 0.419280 -0.2936812 0.0000648 -0.078733 -0.011193 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 20 55824 0.167949 0.418377 -0.2938134 0.0000872 -0.079018 -0.011368 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 21 55825 0.168590 0.417428 -0.2940020 0.0001755 -0.079245 -0.011349 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 22 55826 0.169282 0.416704 -0.2943166 0.0003352 -0.079118 -0.011160 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 23 55827 0.169888 0.415968 -0.2948267 0.0005593 -0.078714 -0.010988 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 24 55828 0.170508 0.415042 -0.2955816 0.0008207 -0.078345 -0.010950 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 25 55829 0.171139 0.414072 -0.2965997 0.0010706 -0.078232 -0.010941 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 26 55830 0.171692 0.413040 -0.2978393 0.0012484 -0.078373 -0.010813 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 27 55831 0.172312 0.411930 -0.2991974 0.0013040 -0.078580 -0.010631 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 28 55832 0.172835 0.410820 -0.3005446 0.0012218 -0.078608 -0.010560 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 29 55833 0.173125 0.409755 -0.3017506 0.0010316 -0.078415 -0.010575 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 9 30 55834 0.173307 0.408649 -0.3027419 0.0007986 -0.078294 -0.010483 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 1 55835 0.173589 0.407599 -0.3035081 0.0005956 -0.078506 -0.010268 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 2 55836 0.173723 0.406540 -0.3041106 0.0004736 -0.078844 -0.010159 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 3 55837 0.173819 0.405451 -0.3046373 0.0004476 -0.078845 -0.010301 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 4 55838 0.174209 0.404289 -0.3051825 0.0005008 -0.078407 -0.010528 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 5 55839 0.174733 0.403198 -0.3058073 0.0006011 -0.077891 -0.010582 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 6 55840 0.175203 0.402141 -0.3065432 0.0007169 -0.077618 -0.010424 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 7 55841 0.175633 0.401089 -0.3073967 0.0008237 -0.077546 -0.010212 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 8 55842 0.176111 0.400106 -0.3083455 0.0009043 -0.077490 -0.010063 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 9 55843 0.176574 0.399017 -0.3093528 0.0009466 -0.077405 -0.009948 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 10 55844 0.177027 0.397704 -0.3103843 0.0009440 -0.077369 -0.009813 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 11 55845 0.177432 0.396377 -0.3113912 0.0008971 -0.077444 -0.009711 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 12 55846 0.177756 0.395044 -0.3123333 0.0008152 -0.077606 -0.009750 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 13 55847 0.178153 0.393725 -0.3131795 0.0007139 -0.077752 -0.009933 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 14 55848 0.178402 0.392407 -0.3139237 0.0006114 -0.077742 -0.010130 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 15 55849 0.178402 0.391083 -0.3145745 0.0005258 -0.077509 -0.010232 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 16 55850 0.178396 0.389890 -0.3151541 0.0004734 -0.077171 -0.010274 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 17 55851 0.178471 0.388687 -0.3157068 0.0004679 -0.076964 -0.010353 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 18 55852 0.178480 0.387562 -0.3162812 0.0005197 -0.076980 -0.010459 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 19 55853 0.178310 0.386378 -0.3169375 0.0006344 -0.077027 -0.010473 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 20 55854 0.178057 0.385166 -0.3177387 0.0008097 -0.076854 -0.010337 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 21 55855 0.177859 0.383924 -0.3187434 0.0010302 -0.076467 -0.010149 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 22 55856 0.177747 0.382743 -0.3199752 0.0012635 -0.076123 -0.010036 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 23 55857 0.177679 0.381692 -0.3214290 0.0014622 -0.076049 -0.009974 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 24 55858 0.177659 0.380518 -0.3230442 0.0015748 -0.076229 -0.009870 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 25 55859 0.177664 0.379252 -0.3247111 0.0015658 -0.076404 -0.009772 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 26 55860 0.177598 0.378034 -0.3263089 0.0014349 -0.076238 -0.009816 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 27 55861 0.177439 0.376773 -0.3277295 0.0012232 -0.075672 -0.009938 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 28 55862 0.177287 0.375373 -0.3289293 0.0009989 -0.075141 -0.009874 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 29 55863 0.177129 0.374004 -0.3299224 0.0008287 -0.075161 -0.009542 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 30 55864 0.176952 0.372730 -0.3307928 0.0007511 -0.075646 -0.009231 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 10 31 55865 0.176900 0.371526 -0.3316286 0.0007656 -0.075946 -0.009241 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 1 55866 0.176864 0.370288 -0.3325134 0.0008421 -0.075672 -0.009491 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 2 55867 0.176734 0.369003 -0.3334907 0.0009411 -0.075131 -0.009659 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 3 55868 0.176532 0.367685 -0.3345634 0.0010311 -0.074806 -0.009587 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 4 55869 0.176280 0.366308 -0.3357166 0.0010938 -0.074754 -0.009399 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 5 55870 0.175852 0.364934 -0.3369100 0.0011207 -0.074727 -0.009262 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 6 55871 0.175254 0.363479 -0.3381134 0.0011078 -0.074619 -0.009185 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 7 55872 0.174635 0.362114 -0.3392829 0.0010548 -0.074540 -0.009091 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 8 55873 0.174050 0.360772 -0.3403807 0.0009666 -0.074582 -0.008974 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 9 55874 0.173558 0.359383 -0.3413775 0.0008553 -0.074706 -0.008919 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 10 55875 0.173098 0.358126 -0.3422553 0.0007381 -0.074828 -0.008966 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 11 55876 0.172624 0.356882 -0.3430220 0.0006337 -0.074880 -0.009030 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 12 55877 0.172013 0.355764 -0.3436961 0.0005590 -0.074811 -0.009011 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 13 55878 0.171241 0.354594 -0.3443152 0.0005275 -0.074595 -0.008927 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 14 55879 0.170310 0.353357 -0.3449282 0.0005488 -0.074304 -0.008890 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 15 55880 0.169266 0.352280 -0.3455945 0.0006276 -0.074065 -0.008954 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 16 55881 0.168339 0.351074 -0.3463627 0.0007608 -0.073919 -0.009042 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 17 55882 0.167519 0.349798 -0.3472857 0.0009359 -0.073768 -0.009045 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 18 55883 0.166784 0.348613 -0.3483949 0.0011286 -0.073534 -0.008945 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 19 55884 0.166182 0.347551 -0.3496888 0.0013047 -0.073299 -0.008793 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 20 55885 0.165663 0.346504 -0.3511358 0.0014243 -0.073227 -0.008608 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 21 55886 0.165092 0.345423 -0.3526578 0.0014530 -0.073377 -0.008385 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 22 55887 0.164429 0.344284 -0.3541545 0.0013761 -0.073591 -0.008209 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 23 55888 0.163728 0.343215 -0.3555254 0.0012095 -0.073579 -0.008233 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 24 55889 0.162907 0.342183 -0.3567066 0.0010001 -0.073201 -0.008442 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 25 55890 0.161989 0.341064 -0.3576785 0.0008102 -0.072699 -0.008584 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 26 55891 0.161136 0.340027 -0.3584925 0.0006922 -0.072495 -0.008450 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 27 55892 0.160221 0.339040 -0.3592344 0.0006676 -0.072662 -0.008157 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 28 55893 0.159392 0.338084 -0.3599889 0.0007215 -0.072808 -0.008001 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 29 55894 0.158528 0.337146 -0.3608209 0.0008150 -0.072613 -0.008087 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 11 30 55895 0.157592 0.336266 -0.3617499 0.0009069 -0.072249 -0.008257 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 1 55896 0.156871 0.335371 -0.3627516 0.0009689 -0.072089 -0.008335 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 2 55897 0.156079 0.334436 -0.3637972 0.0009894 -0.072184 -0.008308 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 3 55898 0.155334 0.333352 -0.3648441 0.0009680 -0.072301 -0.008251 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 4 55899 0.154809 0.332318 -0.3658439 0.0009079 -0.072307 -0.008187 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 5 55900 0.154215 0.331326 -0.3667684 0.0008148 -0.072291 -0.008091 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 6 55901 0.153708 0.330195 -0.3675813 0.0006978 -0.072340 -0.007966 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 7 55902 0.153211 0.329147 -0.3682736 0.0005716 -0.072413 -0.007869 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 8 55903 0.152474 0.328173 -0.3688377 0.0004544 -0.072458 -0.007834 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 9 55904 0.151589 0.327356 -0.3692961 0.0003643 -0.072501 -0.007825 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 10 55905 0.150654 0.326588 -0.3696841 0.0003162 -0.072552 -0.007791 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 11 55906 0.149663 0.325729 -0.3700499 0.0003204 -0.072513 -0.007747 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 12 55907 0.148512 0.324890 -0.3704451 0.0003818 -0.072283 -0.007762 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 13 55908 0.147252 0.324117 -0.3709299 0.0004976 -0.071926 -0.007863 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 +2011 12 14 55909 0.146162 0.323419 -0.3715520 0.0006545 -0.071634 -0.007986 1.000000 1.000000 1.0000000 1.0000000 1.000000 1.000000 diff --git a/src/test/resources/eopc04/eopc04.12 b/src/test/resources/eopc04/eopc04.12 new file mode 100644 index 0000000000..28774320fb --- /dev/null +++ b/src/test/resources/eopc04/eopc04.12 @@ -0,0 +1,380 @@ + + + INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE + EARTH ORIENTATION PARAMETERS + EOP (IERS) 08 C04 + + + FORMAT(3(I4),I7,2(F11.6),2(F12.7),2(F11.6),2(F11.6),2(F11.7),2F12.6) +********************************************************************************** + + Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err + " " s s " " " " s s " " + (0h UTC) + +2012 1 1 55927 0.118700 0.263209 -0.4190307 0.0012643 -0.070168 -0.007486 0.000019 0.000021 0.0000070 0.0000055 0.000337 0.000215 +2012 1 2 55928 0.117478 0.262407 -0.4202660 0.0011668 -0.070304 -0.007394 0.000018 0.000020 0.0000043 0.0000055 0.000268 0.000169 +2012 1 3 55929 0.115947 0.261649 -0.4213914 0.0010732 -0.070405 -0.007323 0.000018 0.000020 0.0000046 0.0000055 0.000202 0.000127 +2012 1 4 55930 0.114546 0.260620 -0.4223894 0.0009431 -0.070443 -0.007319 0.000018 0.000019 0.0000024 0.0000054 0.000140 0.000088 +2012 1 5 55931 0.113261 0.260200 -0.4232747 0.0008274 -0.070463 -0.007396 0.000018 0.000019 0.0000140 0.0000054 0.000191 0.000121 +2012 1 6 55932 0.111438 0.259932 -0.4240546 0.0007439 -0.070523 -0.007490 0.000018 0.000019 0.0000031 0.0000054 0.000274 0.000174 +2012 1 7 55933 0.108883 0.259664 -0.4247816 0.0007242 -0.070657 -0.007541 0.000018 0.000019 0.0000036 0.0000053 0.000273 0.000170 +2012 1 8 55934 0.106429 0.258874 -0.4255338 0.0007866 -0.070776 -0.007617 0.000018 0.000019 0.0000056 0.0000055 0.000242 0.000144 +2012 1 9 55935 0.104225 0.258041 -0.4263648 0.0009020 -0.070701 -0.007760 0.000019 0.000021 0.0000066 0.0000055 0.000209 0.000118 +2012 1 10 55936 0.101787 0.257252 -0.4272859 0.0009794 -0.070382 -0.007919 0.000019 0.000021 0.0000030 0.0000055 0.000178 0.000092 +2012 1 11 55937 0.099783 0.256002 -0.4283517 0.0011310 -0.070010 -0.008015 0.000019 0.000020 0.0000152 0.0000055 0.000205 0.000108 +2012 1 12 55938 0.098374 0.255057 -0.4295936 0.0013295 -0.069835 -0.007966 0.000019 0.000020 0.0000041 0.0000053 0.000245 0.000135 +2012 1 13 55939 0.097330 0.254481 -0.4310036 0.0014854 -0.069937 -0.007806 0.000019 0.000020 0.0000029 0.0000053 0.000284 0.000162 +2012 1 14 55940 0.096133 0.254325 -0.4325201 0.0015230 -0.070228 -0.007595 0.000019 0.000020 0.0000058 0.0000052 0.000274 0.000157 +2012 1 15 55941 0.094902 0.254379 -0.4340233 0.0014555 -0.070510 -0.007408 0.000019 0.000020 0.0000076 0.0000033 0.000246 0.000139 +2012 1 16 55942 0.093576 0.254528 -0.4354233 0.0013370 -0.070708 -0.007246 0.000020 0.000020 0.0000063 0.0000014 0.000077 0.000043 +2012 1 17 55943 0.091710 0.254906 -0.4366664 0.0011580 -0.070863 -0.007136 0.000020 0.000020 0.0000040 0.0000014 0.000073 0.000041 +2012 1 18 55944 0.089569 0.254798 -0.4377220 0.0009552 -0.071047 -0.007143 0.000019 0.000020 0.0000012 0.0000014 0.000070 0.000040 +2012 1 19 55945 0.087373 0.254461 -0.4385853 0.0007743 -0.071073 -0.007370 0.000019 0.000020 0.0000219 0.0000014 0.000100 0.000049 +2012 1 20 55946 0.085428 0.254180 -0.4393168 0.0006897 -0.070970 -0.007728 0.000019 0.000020 0.0000042 0.0000014 0.000140 0.000062 +2012 1 21 55947 0.083249 0.254651 -0.4400245 0.0007410 -0.070854 -0.008012 0.000019 0.000020 0.0000024 0.0000014 0.000158 0.000071 +2012 1 22 55948 0.080627 0.254924 -0.4408023 0.0008370 -0.070685 -0.008050 0.000019 0.000020 0.0000042 0.0000011 0.000164 0.000077 +2012 1 23 55949 0.078598 0.255078 -0.4416951 0.0009573 -0.070483 -0.007842 0.000019 0.000020 0.0000056 0.0000010 0.000171 0.000085 +2012 1 24 55950 0.076541 0.255624 -0.4427049 0.0010539 -0.070365 -0.007568 0.000019 0.000020 0.0000028 0.0000010 0.000176 0.000091 +2012 1 25 55951 0.074227 0.255806 -0.4437962 0.0011148 -0.070405 -0.007403 0.000018 0.000019 0.0000151 0.0000010 0.000198 0.000101 +2012 1 26 55952 0.071900 0.256092 -0.4449204 0.0011189 -0.070589 -0.007455 0.000018 0.000019 0.0000071 0.0000010 0.000222 0.000111 +2012 1 27 55953 0.069358 0.256156 -0.4460334 0.0010883 -0.070862 -0.007640 0.000018 0.000019 0.0000025 0.0000010 0.000246 0.000120 +2012 1 28 55954 0.066770 0.256221 -0.4470732 0.0010003 -0.071038 -0.007816 0.000018 0.000019 0.0000154 0.0000010 0.000223 0.000109 +2012 1 29 55955 0.064294 0.256203 -0.4479982 0.0008641 -0.071126 -0.007879 0.000018 0.000019 0.0000049 0.0000012 0.000183 0.000090 +2012 1 30 55956 0.061824 0.255972 -0.4487993 0.0007511 -0.071117 -0.007868 0.000019 0.000021 0.0000048 0.0000017 0.000142 0.000071 +2012 1 31 55957 0.059292 0.255822 -0.4494656 0.0006002 -0.070972 -0.007908 0.000019 0.000021 0.0000020 0.0000017 0.000102 0.000052 +2012 2 1 55958 0.056906 0.255521 -0.4499897 0.0004580 -0.070793 -0.008039 0.000019 0.000020 0.0000026 0.0000015 0.000090 0.000046 +2012 2 2 55959 0.054725 0.255253 -0.4504155 0.0003937 -0.070618 -0.008208 0.000019 0.000020 0.0000040 0.0000015 0.000088 0.000045 +2012 2 3 55960 0.052998 0.254824 -0.4508189 0.0004269 -0.070535 -0.008318 0.000019 0.000018 0.0000018 0.0000015 0.000085 0.000044 +2012 2 4 55961 0.051408 0.254365 -0.4512567 0.0004943 -0.070586 -0.008381 0.000019 0.000018 0.0000196 0.0000015 0.000095 0.000041 +2012 2 5 55962 0.049333 0.254085 -0.4517747 0.0005607 -0.070709 -0.008487 0.000019 0.000020 0.0000080 0.0000015 0.000109 0.000037 +2012 2 6 55963 0.047368 0.254001 -0.4524402 0.0007546 -0.070832 -0.008724 0.000022 0.000023 0.0000070 0.0000017 0.000075 0.000040 +2012 2 7 55964 0.045706 0.254220 -0.4533091 0.0009995 -0.070689 -0.008950 0.000022 0.000023 0.0000023 0.0000017 0.000071 0.000038 +2012 2 8 55965 0.044328 0.254546 -0.4544131 0.0012229 -0.070179 -0.008960 0.000021 0.000022 0.0000350 0.0000016 0.000085 0.000046 +2012 2 9 55966 0.043192 0.255056 -0.4557227 0.0013892 -0.069728 -0.008708 0.000021 0.000022 0.0000087 0.0000016 0.000105 0.000056 +2012 2 10 55967 0.042172 0.255797 -0.4571640 0.0014757 -0.069623 -0.008355 0.000021 0.000022 0.0000028 0.0000016 0.000125 0.000067 +2012 2 11 55968 0.041276 0.256690 -0.4586395 0.0014643 -0.070131 -0.008143 0.000021 0.000022 0.0000103 0.0000016 0.000124 0.000067 +2012 2 12 55969 0.040311 0.257438 -0.4600560 0.0013481 -0.070807 -0.008168 0.000021 0.000022 0.0000090 0.0000014 0.000116 0.000063 +2012 2 13 55970 0.038861 0.258102 -0.4613256 0.0011742 -0.071300 -0.008335 0.000022 0.000025 0.0000081 0.0000012 0.000109 0.000061 +2012 2 14 55971 0.037082 0.258631 -0.4623763 0.0009157 -0.071577 -0.008509 0.000022 0.000025 0.0000030 0.0000012 0.000103 0.000057 +2012 2 15 55972 0.035700 0.259565 -0.4631795 0.0006923 -0.071479 -0.008660 0.000021 0.000024 0.0000638 0.0000012 0.000101 0.000057 +2012 2 16 55973 0.034328 0.260928 -0.4637969 0.0005548 -0.071388 -0.008834 0.000021 0.000024 0.0000067 0.0000012 0.000100 0.000057 +2012 2 17 55974 0.033052 0.261952 -0.4643422 0.0005356 -0.071338 -0.009063 0.000021 0.000024 0.0000019 0.0000012 0.000100 0.000057 +2012 2 18 55975 0.031627 0.263077 -0.4649159 0.0006173 -0.071389 -0.009269 0.000021 0.000024 0.0000033 0.0000012 0.000130 0.000074 +2012 2 19 55976 0.030595 0.263990 -0.4655836 0.0007289 -0.071323 -0.009340 0.000021 0.000024 0.0000052 0.0000014 0.000172 0.000096 +2012 2 20 55977 0.029688 0.265055 -0.4663655 0.0008446 -0.071203 -0.009216 0.000023 0.000028 0.0000060 0.0000018 0.000213 0.000120 +2012 2 21 55978 0.028345 0.265672 -0.4672390 0.0008971 -0.071159 -0.008985 0.000023 0.000028 0.0000052 0.0000018 0.000254 0.000142 +2012 2 22 55979 0.026675 0.266316 -0.4681516 0.0009304 -0.071247 -0.008806 0.000022 0.000027 0.0000027 0.0000018 0.000291 0.000160 +2012 2 23 55980 0.024689 0.267060 -0.4690540 0.0008475 -0.071124 -0.008826 0.000022 0.000027 0.0000198 0.0000018 0.000229 0.000126 +2012 2 24 55981 0.022814 0.267829 -0.4698906 0.0007513 -0.070922 -0.009030 0.000022 0.000027 0.0000016 0.0000018 0.000138 0.000076 +2012 2 25 55982 0.021568 0.268920 -0.4706253 0.0006815 -0.070943 -0.009327 0.000022 0.000027 0.0000028 0.0000018 0.000123 0.000068 +2012 2 26 55983 0.020597 0.270273 -0.4712585 0.0005631 -0.071081 -0.009570 0.000022 0.000027 0.0000063 0.0000018 0.000139 0.000076 +2012 2 27 55984 0.020119 0.271192 -0.4718084 0.0005016 -0.071209 -0.009738 0.000027 0.000030 0.0000069 0.0000019 0.000154 0.000083 +2012 2 28 55985 0.019720 0.271968 -0.4722734 0.0004154 -0.071207 -0.009928 0.000027 0.000030 0.0000024 0.0000019 0.000169 0.000091 +2012 2 29 55986 0.019069 0.273018 -0.4726379 0.0003138 -0.071063 -0.010135 0.000026 0.000030 0.0000356 0.0000019 0.000213 0.000127 +2012 3 1 55987 0.018798 0.274244 -0.4729194 0.0002463 -0.070954 -0.010328 0.000023 0.000025 0.0000086 0.0000022 0.000284 0.000171 +2012 3 2 55988 0.018937 0.275646 -0.4731884 0.0002932 -0.070932 -0.010371 0.000023 0.000025 0.0000040 0.0000022 0.000354 0.000216 +2012 3 3 55989 0.018563 0.277173 -0.4735285 0.0003928 -0.070970 -0.010250 0.000023 0.000025 0.0000039 0.0000022 0.000343 0.000192 +2012 3 4 55990 0.017782 0.278172 -0.4739946 0.0005434 -0.070983 -0.010112 0.000023 0.000025 0.0000043 0.0000022 0.000301 0.000144 +2012 3 5 55991 0.017387 0.279057 -0.4746191 0.0007031 -0.070883 -0.010243 0.000021 0.000024 0.0000040 0.0000021 0.000286 0.000146 +2012 3 6 55992 0.016938 0.279996 -0.4754440 0.0009276 -0.070723 -0.010455 0.000021 0.000024 0.0000023 0.0000021 0.000260 0.000126 +2012 3 7 55993 0.015713 0.280781 -0.4764778 0.0011170 -0.070413 -0.010529 0.000021 0.000023 0.0000253 0.0000021 0.000223 0.000107 +2012 3 8 55994 0.014414 0.281463 -0.4776973 0.0012909 -0.070091 -0.010305 0.000021 0.000023 0.0000110 0.0000021 0.000182 0.000087 +2012 3 9 55995 0.013219 0.282526 -0.4790665 0.0014230 -0.069995 -0.009941 0.000021 0.000023 0.0000027 0.0000021 0.000141 0.000068 +2012 3 10 55996 0.012112 0.283779 -0.4805270 0.0014563 -0.070203 -0.009770 0.000021 0.000023 0.0000048 0.0000021 0.000135 0.000065 +2012 3 11 55997 0.011081 0.285000 -0.4819953 0.0014503 -0.070524 -0.009939 0.000021 0.000023 0.0000064 0.0000021 0.000142 0.000069 +2012 3 12 55998 0.009693 0.286339 -0.4833724 0.0012916 -0.070703 -0.010287 0.000022 0.000024 0.0000076 0.0000023 0.000150 0.000074 +2012 3 13 55999 0.008716 0.287402 -0.4846160 0.0011775 -0.070680 -0.010585 0.000022 0.000024 0.0000076 0.0000023 0.000157 0.000077 +2012 3 14 56000 0.007787 0.288851 -0.4857658 0.0011305 -0.070593 -0.010746 0.000021 0.000023 0.0000023 0.0000022 0.000165 0.000081 +2012 3 15 56001 0.006396 0.290088 -0.4868843 0.0011365 -0.070551 -0.010853 0.000021 0.000023 0.0000239 0.0000022 0.000129 0.000067 +2012 3 16 56002 0.004961 0.291305 -0.4880525 0.0012366 -0.070625 -0.010941 0.000021 0.000023 0.0000022 0.0000022 0.000082 0.000048 +2012 3 17 56003 0.003408 0.292453 -0.4893520 0.0013827 -0.070713 -0.011022 0.000021 0.000023 0.0000060 0.0000022 0.000081 0.000048 +2012 3 18 56004 0.001664 0.293818 -0.4907959 0.0014974 -0.070676 -0.011058 0.000021 0.000023 0.0000098 0.0000022 0.000098 0.000056 +2012 3 19 56005 -0.000066 0.295068 -0.4923400 0.0015768 -0.070509 -0.011020 0.000021 0.000024 0.0000088 0.0000023 0.000114 0.000064 +2012 3 20 56006 -0.001503 0.295981 -0.4939230 0.0015941 -0.070350 -0.010905 0.000021 0.000024 0.0000033 0.0000023 0.000130 0.000071 +2012 3 21 56007 -0.002628 0.296959 -0.4954831 0.0015507 -0.070283 -0.010736 0.000020 0.000023 0.0000308 0.0000022 0.000119 0.000066 +2012 3 22 56008 -0.003668 0.298210 -0.4969786 0.0014646 -0.070261 -0.010623 0.000020 0.000023 0.0000074 0.0000022 0.000101 0.000057 +2012 3 23 56009 -0.004461 0.299582 -0.4983959 0.0013760 -0.070228 -0.010657 0.000020 0.000023 0.0000032 0.0000022 0.000082 0.000048 +2012 3 24 56010 -0.004601 0.300921 -0.4997354 0.0013061 -0.070034 -0.010870 0.000020 0.000023 0.0000040 0.0000022 0.000077 0.000045 +2012 3 25 56011 -0.005048 0.302625 -0.5009856 0.0012063 -0.069791 -0.011147 0.000020 0.000023 0.0000065 0.0000018 0.000078 0.000045 +2012 3 26 56012 -0.005769 0.303991 -0.5021345 0.0011041 -0.069449 -0.011395 0.000020 0.000024 0.0000060 0.0000015 0.000076 0.000043 +2012 3 27 56013 -0.006669 0.305064 -0.5031977 0.0010361 -0.069090 -0.011610 0.000020 0.000024 0.0000022 0.0000015 0.000075 0.000042 +2012 3 28 56014 -0.007584 0.305823 -0.5042043 0.0010024 -0.069008 -0.011905 0.000019 0.000023 0.0000037 0.0000015 0.000107 0.000058 +2012 3 29 56015 -0.008111 0.307058 -0.5051893 0.0009960 -0.069165 -0.012207 0.000019 0.000023 0.0000051 0.0000015 0.000143 0.000076 +2012 3 30 56016 -0.008824 0.308790 -0.5061999 0.0010312 -0.069447 -0.012356 0.000019 0.000023 0.0000024 0.0000015 0.000180 0.000093 +2012 3 31 56017 -0.009569 0.310658 -0.5072863 0.0011621 -0.069494 -0.012188 0.000019 0.000023 0.0000018 0.0000015 0.000192 0.000103 +2012 4 1 56018 -0.010272 0.312753 -0.5085085 0.0013212 -0.069294 -0.011868 0.000019 0.000023 0.0000044 0.0000018 0.000194 0.000111 +2012 4 2 56019 -0.011351 0.314853 -0.5098935 0.0014645 -0.069044 -0.011671 0.000020 0.000023 0.0000059 0.0000023 0.000170 0.000114 +2012 4 3 56020 -0.012264 0.316855 -0.5114393 0.0016464 -0.068888 -0.011732 0.000020 0.000023 0.0000029 0.0000023 0.000172 0.000121 +2012 4 4 56021 -0.012896 0.318655 -0.5131549 0.0017876 -0.068517 -0.011861 0.000019 0.000022 0.0000241 0.0000023 0.000181 0.000128 +2012 4 5 56022 -0.013364 0.320312 -0.5149928 0.0018599 -0.068109 -0.011757 0.000019 0.000022 0.0000019 0.0000023 0.000191 0.000134 +2012 4 6 56023 -0.013715 0.322168 -0.5168595 0.0018563 -0.068172 -0.011479 0.000019 0.000022 0.0000202 0.0000023 0.000182 0.000129 +2012 4 7 56024 -0.013652 0.324149 -0.5186314 0.0016680 -0.068553 -0.011351 0.000021 0.000022 0.0000088 0.0000023 0.000166 0.000120 +2012 4 8 56025 -0.013097 0.325975 -0.5202021 0.0014264 -0.068998 -0.011551 0.000021 0.000022 0.0000079 0.0000023 0.000150 0.000110 +2012 4 9 56026 -0.012284 0.327886 -0.5215395 0.0012179 -0.069332 -0.011878 0.000020 0.000023 0.0000069 0.0000023 0.000134 0.000102 +2012 4 10 56027 -0.011627 0.330100 -0.5226753 0.0010577 -0.069558 -0.012167 0.000020 0.000023 0.0000046 0.0000023 0.000117 0.000093 +2012 4 11 56028 -0.010886 0.332532 -0.5236983 0.0009927 -0.069704 -0.012337 0.000020 0.000022 0.0000028 0.0000022 0.000095 0.000083 +2012 4 12 56029 -0.009896 0.334821 -0.5247296 0.0010518 -0.069650 -0.012469 0.000020 0.000022 0.0000184 0.0000022 0.000067 0.000058 +2012 4 13 56030 -0.008876 0.337102 -0.5258404 0.0011597 -0.069590 -0.012572 0.000020 0.000022 0.0000026 0.0000022 0.000035 0.000029 +2012 4 14 56031 -0.008011 0.339146 -0.5270311 0.0012156 -0.069688 -0.012588 0.000020 0.000022 0.0000041 0.0000022 0.000036 0.000028 +2012 4 15 56032 -0.007230 0.341115 -0.5282793 0.0012592 -0.069826 -0.012527 0.000020 0.000022 0.0000066 0.0000022 0.000049 0.000037 +2012 4 16 56033 -0.006329 0.342951 -0.5295673 0.0013069 -0.069808 -0.012453 0.000020 0.000023 0.0000048 0.0000024 0.000068 0.000047 +2012 4 17 56034 -0.005199 0.344926 -0.5308783 0.0013224 -0.069582 -0.012394 0.000020 0.000023 0.0000015 0.0000024 0.000082 0.000056 +2012 4 18 56035 -0.004236 0.347145 -0.5321965 0.0013050 -0.069315 -0.012287 0.000022 0.000024 0.0000216 0.0000024 0.000087 0.000059 +2012 4 19 56036 -0.003430 0.349499 -0.5334911 0.0012692 -0.069164 -0.012148 0.000022 0.000024 0.0000034 0.0000024 0.000091 0.000061 +2012 4 20 56037 -0.002859 0.351929 -0.5347248 0.0011976 -0.069155 -0.012089 0.000022 0.000024 0.0000015 0.0000024 0.000095 0.000064 +2012 4 21 56038 -0.002364 0.354154 -0.5358613 0.0010735 -0.069261 -0.012258 0.000022 0.000024 0.0000184 0.0000024 0.000086 0.000058 +2012 4 22 56039 -0.001494 0.356002 -0.5368881 0.0009847 -0.069339 -0.012593 0.000022 0.000024 0.0000048 0.0000024 0.000072 0.000048 +2012 4 23 56040 -0.000272 0.357958 -0.5378074 0.0008768 -0.069290 -0.012913 0.000022 0.000026 0.0000056 0.0000102 0.000060 0.000040 +2012 4 24 56041 0.000653 0.359920 -0.5386339 0.0007777 -0.069134 -0.013105 0.000022 0.000026 0.0000020 0.0000102 0.000046 0.000030 +2012 4 25 56042 0.001379 0.361760 -0.5394047 0.0007599 -0.068805 -0.013171 0.000021 0.000026 0.0000238 0.0000100 0.000129 0.000046 +2012 4 26 56043 0.002156 0.363391 -0.5401645 0.0007562 -0.068635 -0.013204 0.000021 0.000026 0.0000078 0.0000100 0.000238 0.000069 +2012 4 27 56044 0.003382 0.364806 -0.5409514 0.0008113 -0.068636 -0.013182 0.000021 0.000026 0.0000102 0.0000100 0.000347 0.000091 +2012 4 28 56045 0.004355 0.366218 -0.5417935 0.0008890 -0.069335 -0.012819 0.000021 0.000026 0.0000085 0.0000099 0.000219 0.000081 +2012 4 29 56046 0.005295 0.367419 -0.5427026 0.0009543 -0.069078 -0.012569 0.000021 0.000026 0.0000075 0.0000099 0.000163 0.000075 +2012 4 30 56047 0.006321 0.368720 -0.5436860 0.0010777 -0.068707 -0.012625 0.000022 0.000027 0.0000081 0.0000104 0.000086 0.000061 +2012 5 1 56048 0.007701 0.369896 -0.5448627 0.0012836 -0.068608 -0.012605 0.000022 0.000027 0.0000060 0.0000104 0.000086 0.000061 +2012 5 2 56049 0.009214 0.370867 -0.5462259 0.0014206 -0.068826 -0.012694 0.000021 0.000026 0.0000038 0.0000099 0.000099 0.000071 +2012 5 3 56050 0.010546 0.371897 -0.5476981 0.0014781 -0.069229 -0.012627 0.000021 0.000026 0.0000011 0.0000095 0.000106 0.000076 +2012 5 4 56051 0.011863 0.373233 -0.5491911 0.0014524 -0.069485 -0.012563 0.000021 0.000026 0.0000003 0.0000099 0.000073 0.000053 +2012 5 5 56052 0.013328 0.374713 -0.5505806 0.0013560 -0.069863 -0.012485 0.000021 0.000026 0.0000124 0.0000101 0.000072 0.000050 +2012 5 6 56053 0.015115 0.376289 -0.5519143 0.0011972 -0.070142 -0.012602 0.000021 0.000026 0.0000108 0.0000097 0.000087 0.000058 +2012 5 7 56054 0.016308 0.378006 -0.5529813 0.0009654 -0.070198 -0.012803 0.000021 0.000029 0.0000082 0.0000099 0.000098 0.000065 +2012 5 8 56055 0.016994 0.379387 -0.5538805 0.0008582 -0.070119 -0.012891 0.000021 0.000029 0.0000023 0.0000095 0.000112 0.000072 +2012 5 9 56056 0.017569 0.380756 -0.5546775 0.0008585 -0.069907 -0.012879 0.000020 0.000027 0.0000084 0.0000087 0.000099 0.000065 +2012 5 10 56057 0.017690 0.381913 -0.5555654 0.0008893 -0.069596 -0.012912 0.000020 0.000027 0.0000094 0.0000087 0.000080 0.000055 +2012 5 11 56058 0.017780 0.382703 -0.5564987 0.0009321 -0.069307 -0.012993 0.000020 0.000027 0.0000021 0.0000090 0.000061 0.000045 +2012 5 12 56059 0.018358 0.383365 -0.5574634 0.0009890 -0.069260 -0.013039 0.000020 0.000027 0.0000147 0.0000093 0.000055 0.000042 +2012 5 13 56060 0.019698 0.384038 -0.5584999 0.0010488 -0.069517 -0.012986 0.000020 0.000027 0.0000130 0.0000089 0.000054 0.000041 +2012 5 14 56061 0.021579 0.384748 -0.5595554 0.0010784 -0.069769 -0.012921 0.000018 0.000021 0.0000075 0.0000086 0.000053 0.000041 +2012 5 15 56062 0.023673 0.385619 -0.5606062 0.0010969 -0.069732 -0.012913 0.000016 0.000018 0.0000026 0.0000086 0.000052 0.000041 +2012 5 16 56063 0.025895 0.386691 -0.5617379 0.0010618 -0.069716 -0.012863 0.000015 0.000020 0.0000114 0.0000082 0.000074 0.000040 +2012 5 17 56064 0.027811 0.387502 -0.5626890 0.0009536 -0.069895 -0.012739 0.000015 0.000020 0.0000068 0.0000079 0.000104 0.000040 +2012 5 18 56065 0.029234 0.388335 -0.5636053 0.0008258 -0.070364 -0.012625 0.000017 0.000022 0.0000037 0.0000078 0.000134 0.000040 +2012 5 19 56066 0.030346 0.388884 -0.5643392 0.0006682 -0.070752 -0.012671 0.000019 0.000025 0.0000060 0.0000080 0.000132 0.000037 +2012 5 20 56067 0.031434 0.389058 -0.5649455 0.0005017 -0.070930 -0.012894 0.000017 0.000022 0.0000061 0.0000078 0.000118 0.000034 +2012 5 21 56068 0.032275 0.389282 -0.5653980 0.0004082 -0.070839 -0.013130 0.000016 0.000022 0.0000118 0.0000074 0.000105 0.000031 +2012 5 22 56069 0.033210 0.389771 -0.5657928 0.0004042 -0.070531 -0.013206 0.000016 0.000022 0.0000061 0.0000074 0.000092 0.000027 +2012 5 23 56070 0.034221 0.390526 -0.5661696 0.0004037 -0.070335 -0.013163 0.000015 0.000021 0.0000111 0.0000074 0.000088 0.000029 +2012 5 24 56071 0.035455 0.391205 -0.5665821 0.0004431 -0.070346 -0.013110 0.000015 0.000021 0.0000075 0.0000074 0.000101 0.000033 +2012 5 25 56072 0.037074 0.391898 -0.5670903 0.0005170 -0.070569 -0.013122 0.000015 0.000021 0.0000039 0.0000074 0.000114 0.000037 +2012 5 26 56073 0.039082 0.392483 -0.5677077 0.0006670 -0.070800 -0.013142 0.000015 0.000021 0.0000159 0.0000073 0.000115 0.000038 +2012 5 27 56074 0.041109 0.393177 -0.5684351 0.0008057 -0.070791 -0.013087 0.000015 0.000021 0.0000142 0.0000073 0.000112 0.000038 +2012 5 28 56075 0.042920 0.393837 -0.5693419 0.0009406 -0.070485 -0.012943 0.000016 0.000022 0.0000129 0.0000072 0.000137 0.000039 +2012 5 29 56076 0.044706 0.394674 -0.5703691 0.0010727 -0.070225 -0.012826 0.000016 0.000022 0.0000088 0.0000072 0.000132 0.000039 +2012 5 30 56077 0.045635 0.395458 -0.5714640 0.0011683 -0.070276 -0.012702 0.000015 0.000021 0.0000045 0.0000071 0.000126 0.000038 +2012 5 31 56078 0.046295 0.395962 -0.5726044 0.0012286 -0.070618 -0.012609 0.000018 0.000022 0.0000062 0.0000077 0.000074 0.000057 +2012 6 1 56079 0.046904 0.396529 -0.5738579 0.0011900 -0.071257 -0.012369 0.000018 0.000022 0.0000020 0.0000077 0.000059 0.000038 +2012 6 2 56080 0.047752 0.397169 -0.5750136 0.0010731 -0.071831 -0.012258 0.000018 0.000022 0.0000048 0.0000077 0.000057 0.000035 +2012 6 3 56081 0.048883 0.397593 -0.5759777 0.0008980 -0.072129 -0.012381 0.000018 0.000022 0.0000064 0.0000080 0.000061 0.000037 +2012 6 4 56082 0.050164 0.397885 -0.5768206 0.0007145 -0.072142 -0.012567 0.000016 0.000021 0.0000066 0.0000082 0.000065 0.000041 +2012 6 5 56083 0.051748 0.398092 -0.5774835 0.0006022 -0.072030 -0.012599 0.000016 0.000021 0.0000049 0.0000082 0.000069 0.000043 +2012 6 6 56084 0.053303 0.398675 -0.5780250 0.0006101 -0.071878 -0.012479 0.000015 0.000021 0.0000020 0.0000077 0.000071 0.000046 +2012 6 7 56085 0.054801 0.398944 -0.5786822 0.0007000 -0.071568 -0.012441 0.000015 0.000021 0.0000117 0.0000074 0.000071 0.000046 +2012 6 8 56086 0.056559 0.399505 -0.5794676 0.0008186 -0.071242 -0.012537 0.000015 0.000021 0.0000033 0.0000071 0.000071 0.000045 +2012 6 9 56087 0.058236 0.400027 -0.5803155 0.0008893 -0.071307 -0.012653 0.000015 0.000021 0.0000142 0.0000074 0.000071 0.000046 +2012 6 10 56088 0.059975 0.400443 -0.5812728 0.0008809 -0.071842 -0.012688 0.000015 0.000021 0.0000086 0.0000071 0.000071 0.000047 +2012 6 11 56089 0.061732 0.401401 -0.5820705 0.0008119 -0.072477 -0.012672 0.000017 0.000022 0.0000092 0.0000067 0.000073 0.000048 +2012 6 12 56090 0.063526 0.402509 -0.5828366 0.0007430 -0.072772 -0.012667 0.000017 0.000022 0.0000080 0.0000067 0.000074 0.000049 +2012 6 13 56091 0.064962 0.403874 -0.5835287 0.0006265 -0.072702 -0.012641 0.000017 0.000021 0.0000021 0.0000065 0.000077 0.000053 +2012 6 14 56092 0.065967 0.404715 -0.5840927 0.0004785 -0.072646 -0.012506 0.000017 0.000021 0.0000055 0.0000065 0.000081 0.000057 +2012 6 15 56093 0.066912 0.405172 -0.5844996 0.0003328 -0.072889 -0.012344 0.000017 0.000021 0.0000019 0.0000062 0.000086 0.000063 +2012 6 16 56094 0.067613 0.405417 -0.5847645 0.0002061 -0.073420 -0.012398 0.000017 0.000021 0.0000080 0.0000065 0.000109 0.000076 +2012 6 17 56095 0.068671 0.405618 -0.5849684 0.0001149 -0.073902 -0.012639 0.000017 0.000021 0.0000088 0.0000071 0.000140 0.000091 +2012 6 18 56096 0.070078 0.405863 -0.5850269 0.0000151 -0.074157 -0.012914 0.000018 0.000022 0.0000090 0.0000077 0.000163 0.000103 +2012 6 19 56097 0.071947 0.406222 -0.5849518 -0.0000577 -0.074191 -0.013050 0.000018 0.000022 0.0000044 0.0000077 0.000189 0.000115 +2012 6 20 56098 0.073719 0.406799 -0.5848340 -0.0000932 -0.074069 -0.012939 0.000016 0.000022 0.0000025 0.0000079 0.000151 0.000099 +2012 6 21 56099 0.075527 0.407342 -0.5847583 -0.0000777 -0.073980 -0.012750 0.000016 0.000022 0.0000019 0.0000079 0.000118 0.000079 +2012 6 22 56100 0.077422 0.407945 -0.5847443 -0.0000077 -0.074072 -0.012633 0.000016 0.000022 0.0000013 0.0000076 0.000084 0.000059 +2012 6 23 56101 0.078904 0.408461 -0.5848059 0.0001223 -0.074459 -0.012644 0.000016 0.000022 0.0000160 0.0000075 0.000082 0.000057 +2012 6 24 56102 0.080862 0.408502 -0.5850064 0.0002569 -0.074897 -0.012665 0.000016 0.000022 0.0000086 0.0000075 0.000092 0.000061 +2012 6 25 56103 0.083114 0.408517 -0.5853030 0.0003872 -0.075138 -0.012604 0.000017 0.000022 0.0000104 0.0000074 0.000103 0.000065 +2012 6 26 56104 0.085285 0.408564 -0.5857136 0.0004553 -0.075181 -0.012445 0.000015 0.000019 0.0000044 0.0000073 0.000113 0.000068 +2012 6 27 56105 0.087439 0.408651 -0.5861801 0.0004176 -0.075103 -0.012197 0.000014 0.000018 0.0000136 0.0000073 0.000092 0.000055 +2012 6 28 56106 0.089473 0.409002 -0.5865662 0.0003057 -0.075226 -0.011906 0.000014 0.000018 0.0000077 0.0000073 0.000062 0.000038 +2012 6 29 56107 0.091346 0.409225 -0.5867776 0.0001678 -0.075577 -0.011683 0.000014 0.000018 0.0000018 0.0000073 0.000032 0.000021 +2012 6 30 56108 0.092800 0.409392 -0.5868238 0.0000012 -0.076036 -0.011665 0.000014 0.000018 0.0000449 0.0000076 0.000034 0.000021 +2012 7 1 56109 0.094064 0.409151 0.4131816 -0.0001241 -0.076311 -0.011892 0.000014 0.000018 0.0000122 0.0000075 0.000047 0.000029 +2012 7 2 56110 0.095640 0.408939 0.4133963 -0.0002276 -0.076307 -0.012172 0.000019 0.000022 0.0000087 0.0000073 0.000060 0.000037 +2012 7 3 56111 0.097268 0.408650 0.4136688 -0.0002157 -0.076222 -0.012284 0.000019 0.000022 0.0000024 0.0000073 0.000074 0.000044 +2012 7 4 56112 0.098946 0.408274 0.4137809 -0.0001348 -0.076047 -0.012193 0.000018 0.000023 0.0000153 0.0000072 0.000076 0.000046 +2012 7 5 56113 0.100502 0.407981 0.4138739 -0.0000409 -0.075789 -0.012052 0.000018 0.000023 0.0000091 0.0000072 0.000077 0.000046 +2012 7 6 56114 0.102491 0.407531 0.4138922 0.0000899 -0.075538 -0.012024 0.000018 0.000023 0.0000028 0.0000072 0.000077 0.000046 +2012 7 7 56115 0.105059 0.407518 0.4137229 0.0002019 -0.075651 -0.012116 0.000018 0.000023 0.0000161 0.0000072 0.000073 0.000044 +2012 7 8 56116 0.107683 0.407590 0.4134589 0.0002824 -0.076221 -0.012243 0.000018 0.000023 0.0000124 0.0000072 0.000068 0.000042 +2012 7 9 56117 0.110089 0.407731 0.4131662 0.0002979 -0.076980 -0.012337 0.000017 0.000020 0.0000076 0.0000075 0.000065 0.000040 +2012 7 10 56118 0.111864 0.407792 0.4128977 0.0002350 -0.077512 -0.012372 0.000015 0.000018 0.0000019 0.0000074 0.000059 0.000037 +2012 7 11 56119 0.113448 0.407539 0.4127029 0.0001164 -0.077604 -0.012362 0.000018 0.000021 0.0000126 0.0000070 0.000054 0.000034 +2012 7 12 56120 0.114869 0.407288 0.4126306 -0.0000738 -0.077563 -0.012303 0.000018 0.000021 0.0000106 0.0000070 0.000048 0.000031 +2012 7 13 56121 0.116662 0.407013 0.4128536 -0.0002246 -0.077673 -0.012247 0.000018 0.000021 0.0000027 0.0000070 0.000042 0.000028 +2012 7 14 56122 0.118293 0.407111 0.4131009 -0.0002932 -0.078053 -0.012225 0.000022 0.000023 0.0000082 0.0000075 0.000046 0.000031 +2012 7 15 56123 0.119936 0.407332 0.4133776 -0.0003112 -0.078446 -0.012269 0.000022 0.000023 0.0000052 0.0000078 0.000053 0.000035 +2012 7 16 56124 0.121393 0.407270 0.4137799 -0.0003142 -0.078669 -0.012322 0.000020 0.000023 0.0000117 0.0000075 0.000060 0.000040 +2012 7 17 56125 0.122814 0.407047 0.4140680 -0.0002586 -0.078766 -0.012311 0.000020 0.000023 0.0000040 0.0000072 0.000067 0.000044 +2012 7 18 56126 0.124435 0.406840 0.4141897 -0.0001211 -0.078600 -0.012272 0.000019 0.000021 0.0000055 0.0000072 0.000065 0.000042 +2012 7 19 56127 0.126239 0.406643 0.4142080 0.0000848 -0.078325 -0.012221 0.000019 0.000021 0.0000034 0.0000072 0.000061 0.000038 +2012 7 20 56128 0.127745 0.406264 0.4140650 0.0002780 -0.078105 -0.012191 0.000019 0.000021 0.0000013 0.0000072 0.000057 0.000035 +2012 7 21 56129 0.129227 0.405785 0.4137358 0.0003926 -0.078243 -0.012173 0.000019 0.000021 0.0000051 0.0000072 0.000076 0.000035 +2012 7 22 56130 0.130787 0.405125 0.4132710 0.0004882 -0.078689 -0.012137 0.000019 0.000021 0.0000047 0.0000072 0.000103 0.000037 +2012 7 23 56131 0.132387 0.404523 0.4127181 0.0005677 -0.079203 -0.012172 0.000018 0.000021 0.0000041 0.0000069 0.000058 0.000033 +2012 7 24 56132 0.133932 0.404018 0.4121307 0.0005665 -0.079588 -0.012114 0.000018 0.000021 0.0000014 0.0000069 0.000055 0.000033 +2012 7 25 56133 0.135614 0.403272 0.4115946 0.0005071 -0.079822 -0.011923 0.000017 0.000020 0.0000139 0.0000066 0.000046 0.000028 +2012 7 26 56134 0.137280 0.402524 0.4110659 0.0004115 -0.079999 -0.011693 0.000017 0.000020 0.0000084 0.0000066 0.000042 0.000024 +2012 7 27 56135 0.138993 0.401814 0.4107666 0.0002750 -0.080240 -0.011514 0.000017 0.000020 0.0000020 0.0000066 0.000038 0.000019 +2012 7 28 56136 0.140920 0.401178 0.4105485 0.0001827 -0.080545 -0.011551 0.000017 0.000020 0.0000078 0.0000069 0.000039 0.000019 +2012 7 29 56137 0.142506 0.401049 0.4104218 0.0001033 -0.080837 -0.011799 0.000223 0.000101 0.0000051 0.0000270 0.000041 0.000021 +2012 7 30 56138 0.143480 0.400692 0.4103389 0.0000898 -0.080969 -0.012092 0.000020 0.000022 0.0000102 0.0000081 0.000238 0.000050 +2012 7 31 56139 0.144625 0.400215 0.4102122 0.0001944 -0.081101 -0.012275 0.000020 0.000022 0.0000100 0.0000081 0.000239 0.000052 +2012 8 1 56140 0.145946 0.399829 0.4099390 0.0003125 -0.080801 -0.012443 0.000020 0.000023 0.0000101 0.0000079 0.000033 0.000017 +2012 8 2 56141 0.147315 0.399345 0.4095109 0.0004448 -0.080723 -0.012129 0.000020 0.000023 0.0000248 0.0000080 0.000046 0.000023 +2012 8 3 56142 0.148473 0.398802 0.4090457 0.0005349 -0.080594 -0.011832 0.000020 0.000023 0.0000056 0.0000080 0.000060 0.000030 +2012 8 4 56143 0.149249 0.398140 0.4084970 0.0005555 -0.080556 -0.011720 0.000020 0.000023 0.0000047 0.0000079 0.000067 0.000034 +2012 8 5 56144 0.149979 0.397590 0.4078980 0.0005695 -0.080767 -0.011777 0.000020 0.000023 0.0000052 0.0000076 0.000072 0.000037 +2012 8 6 56145 0.150671 0.397024 0.4074006 0.0005046 -0.081464 -0.012002 0.000020 0.000023 0.0000178 0.0000075 0.000071 0.000035 +2012 8 7 56146 0.151467 0.396333 0.4069557 0.0003400 -0.081928 -0.012109 0.000020 0.000023 0.0000060 0.0000075 0.000073 0.000036 +2012 8 8 56147 0.152500 0.395877 0.4066848 0.0001660 -0.082017 -0.012056 0.000019 0.000022 0.0000130 0.0000079 0.000081 0.000041 +2012 8 9 56148 0.153731 0.395218 0.4065725 0.0000398 -0.081876 -0.011947 0.000019 0.000022 0.0000146 0.0000079 0.000090 0.000046 +2012 8 10 56149 0.155332 0.394230 0.4065859 -0.0000282 -0.081683 -0.011883 0.000019 0.000022 0.0000036 0.0000076 0.000099 0.000052 +2012 8 11 56150 0.156948 0.393425 0.4066744 -0.0000903 -0.081690 -0.011985 0.000019 0.000022 0.0000088 0.0000079 0.000096 0.000050 +2012 8 12 56151 0.158540 0.392912 0.4068334 -0.0001849 -0.081824 -0.012153 0.000019 0.000022 0.0000101 0.0000078 0.000089 0.000045 +2012 8 13 56152 0.160221 0.392335 0.4070462 -0.0001930 -0.081966 -0.012282 0.000020 0.000023 0.0000134 0.0000075 0.000084 0.000040 +2012 8 14 56153 0.162134 0.391867 0.4071880 -0.0000865 -0.082120 -0.012361 0.000020 0.000023 0.0000041 0.0000075 0.000076 0.000035 +2012 8 15 56154 0.163362 0.391425 0.4072338 0.0000221 -0.082163 -0.012357 0.000019 0.000022 0.0000178 0.0000073 0.000067 0.000031 +2012 8 16 56155 0.164337 0.390714 0.4071029 0.0001697 -0.082078 -0.012354 0.000019 0.000022 0.0000102 0.0000073 0.000058 0.000028 +2012 8 17 56156 0.165627 0.389979 0.4068344 0.0003544 -0.081944 -0.012322 0.000019 0.000022 0.0000025 0.0000073 0.000049 0.000025 +2012 8 18 56157 0.167118 0.389033 0.4064603 0.0005000 -0.081912 -0.012210 0.000019 0.000022 0.0000222 0.0000073 0.000042 0.000022 +2012 8 19 56158 0.168597 0.387928 0.4058641 0.0006231 -0.082148 -0.011990 0.000024 0.000027 0.0000098 0.0000069 0.000036 0.000019 +2012 8 20 56159 0.169942 0.386763 0.4051827 0.0006640 -0.082630 -0.011755 0.000023 0.000026 0.0000150 0.0000059 0.000030 0.000016 +2012 8 21 56160 0.171298 0.385398 0.4044970 0.0006236 -0.083161 -0.011633 0.000023 0.000026 0.0000044 0.0000059 0.000024 0.000013 +2012 8 22 56161 0.172368 0.384295 0.4039089 0.0005211 -0.083511 -0.011672 0.000023 0.000027 0.0000107 0.0000060 0.000036 0.000019 +2012 8 23 56162 0.173431 0.383057 0.4034457 0.0003728 -0.083553 -0.011788 0.000021 0.000025 0.0000130 0.0000060 0.000053 0.000028 +2012 8 24 56163 0.174105 0.382028 0.4031042 0.0002330 -0.083381 -0.011900 0.000025 0.000025 0.0000031 0.0000066 0.000070 0.000037 +2012 8 25 56164 0.174644 0.380911 0.4029126 0.0001327 -0.083254 -0.011986 0.000027 0.000025 0.0000127 0.0000066 0.000087 0.000047 +2012 8 26 56165 0.175245 0.379956 0.4027897 0.0001302 -0.083296 -0.012112 0.000219 0.000124 0.0000091 0.0000255 0.000104 0.000058 +2012 8 27 56166 0.175637 0.378763 0.4025922 0.0002565 -0.083455 -0.012282 0.000029 0.000030 0.0000075 0.0000065 0.000122 0.000065 +2012 8 28 56167 0.176051 0.377272 0.4022464 0.0004426 -0.083583 -0.012399 0.000029 0.000030 0.0000023 0.0000061 0.000139 0.000075 +2012 8 29 56168 0.176396 0.375751 0.4017502 0.0006084 -0.083503 -0.012376 0.000029 0.000029 0.0000144 0.0000069 0.000118 0.000066 +2012 8 30 56169 0.176738 0.374268 0.4010153 0.0007421 -0.083288 -0.012170 0.000029 0.000029 0.0000074 0.0000069 0.000085 0.000046 +2012 8 31 56170 0.177099 0.373095 0.4002225 0.0008255 -0.083075 -0.011886 0.000027 0.000029 0.0000018 0.0000066 0.000051 0.000025 +2012 9 1 56171 0.177275 0.371957 0.3993713 0.0008531 -0.082944 -0.011662 0.000025 0.000026 0.0000068 0.0000063 0.000045 0.000021 +2012 9 2 56172 0.176992 0.370856 0.3985346 0.0008164 -0.082942 -0.011607 0.000025 0.000026 0.0000062 0.0000060 0.000049 0.000024 +2012 9 3 56173 0.176650 0.369576 0.3977350 0.0007865 -0.083067 -0.011692 0.000022 0.000025 0.0000038 0.0000060 0.000052 0.000027 +2012 9 4 56174 0.176375 0.368337 0.3969768 0.0006557 -0.083244 -0.011805 0.000022 0.000025 0.0000021 0.0000060 0.000056 0.000030 +2012 9 5 56175 0.176227 0.366835 0.3964221 0.0004809 -0.083362 -0.011866 0.000024 0.000024 0.0000010 0.0000068 0.000060 0.000033 +2012 9 6 56176 0.176079 0.365318 0.3960766 0.0003651 -0.083464 -0.011945 0.000024 0.000024 0.0000127 0.0000078 0.000066 0.000038 +2012 9 7 56177 0.175947 0.363610 0.3957532 0.0002873 -0.083427 -0.012071 0.000024 0.000026 0.0000033 0.0000085 0.000072 0.000042 +2012 9 8 56178 0.175877 0.362235 0.3954737 0.0001969 -0.083231 -0.012211 0.000022 0.000024 0.0000111 0.0000071 0.000068 0.000040 +2012 9 9 56179 0.175770 0.360924 0.3953274 0.0002235 -0.083140 -0.012317 0.000020 0.000022 0.0000063 0.0000065 0.000060 0.000036 +2012 9 10 56180 0.175594 0.359634 0.3950609 0.0003344 -0.083212 -0.012313 0.000019 0.000024 0.0000054 0.0000067 0.000052 0.000031 +2012 9 11 56181 0.175696 0.358288 0.3946589 0.0004786 -0.083306 -0.012203 0.000019 0.000024 0.0000015 0.0000070 0.000044 0.000026 +2012 9 12 56182 0.176007 0.357211 0.3940966 0.0006534 -0.083277 -0.012090 0.000017 0.000025 0.0000098 0.0000072 0.000052 0.000031 +2012 9 13 56183 0.176196 0.356139 0.3933510 0.0008391 -0.083139 -0.012078 0.000017 0.000025 0.0000047 0.0000069 0.000063 0.000036 +2012 9 14 56184 0.176741 0.355007 0.3923733 0.0010866 -0.082985 -0.012110 0.000017 0.000023 0.0000017 0.0000065 0.000074 0.000041 +2012 9 15 56185 0.177425 0.353913 0.3911818 0.0013191 -0.082987 -0.012058 0.000017 0.000023 0.0000124 0.0000193 0.000078 0.000042 +2012 9 16 56186 0.177784 0.352812 0.3897334 0.0014194 -0.083129 -0.011825 0.000017 0.000023 0.0000065 0.0000233 0.000079 0.000043 +2012 9 17 56187 0.177802 0.351932 0.3883459 0.0014015 -0.083394 -0.011545 0.000017 0.000023 0.0000066 0.0000234 0.000078 0.000042 +2012 9 18 56188 0.177737 0.350812 0.3869787 0.0012928 -0.083695 -0.011385 0.000017 0.000023 0.0000022 0.0000335 0.000068 0.000036 +2012 9 19 56189 0.177452 0.349704 0.3857794 0.0011324 -0.083806 -0.011393 0.000017 0.000026 0.0000089 0.0000326 0.000067 0.000040 +2012 9 20 56190 0.177149 0.348196 0.3847136 0.0009374 -0.083695 -0.011487 0.000017 0.000026 0.0000054 0.0000272 0.000074 0.000044 +2012 9 21 56191 0.176564 0.346359 0.3838781 0.0007399 -0.083380 -0.011553 0.000017 0.000026 0.0000020 0.0000059 0.000081 0.000049 +2012 9 22 56192 0.176120 0.344589 0.3832650 0.0006562 -0.083110 -0.011586 0.000017 0.000026 0.0000213 0.0000694 0.000075 0.000046 +2012 9 23 56193 0.175882 0.343246 0.3825801 0.0006935 -0.082917 -0.011621 0.000015 0.000026 0.0000082 0.0000825 0.000064 0.000040 +2012 9 24 56194 0.175608 0.342116 0.3818287 0.0007769 -0.082817 -0.011669 0.000016 0.000032 0.0000050 0.0000066 0.000056 0.000035 +2012 9 25 56195 0.174962 0.341530 0.3810195 0.0009283 -0.082783 -0.011690 0.000018 0.000032 0.0000013 0.0000072 0.000044 0.000029 +2012 9 26 56196 0.173929 0.341108 0.3799844 0.0011293 -0.082638 -0.011609 0.000018 0.000029 0.0000145 0.0000070 0.000046 0.000031 +2012 9 27 56197 0.173095 0.340541 0.3787985 0.0012887 -0.082490 -0.011420 0.000018 0.000029 0.0000066 0.0000064 0.000053 0.000034 +2012 9 28 56198 0.172110 0.339831 0.3774630 0.0013459 -0.082451 -0.011170 0.000018 0.000027 0.0000018 0.0000057 0.000060 0.000037 +2012 9 29 56199 0.170690 0.338621 0.3761286 0.0013303 -0.082564 -0.010986 0.000018 0.000029 0.0000067 0.0000061 0.000064 0.000041 +2012 9 30 56200 0.169484 0.336658 0.3748316 0.0012338 -0.082731 -0.010927 0.000018 0.000029 0.0000106 0.0000061 0.000066 0.000045 +2012 10 1 56201 0.168893 0.334488 0.3736748 0.0010666 -0.082877 -0.011015 0.000020 0.000029 0.0000062 0.0000061 0.000071 0.000049 +2012 10 2 56202 0.168556 0.332869 0.3726886 0.0008843 -0.083009 -0.011197 0.000017 0.000027 0.0000021 0.0000064 0.000074 0.000054 +2012 10 3 56203 0.168261 0.331527 0.3719108 0.0007204 -0.083116 -0.011340 0.000019 0.000027 0.0000085 0.0000059 0.000072 0.000050 +2012 10 4 56204 0.168218 0.330668 0.3712561 0.0006217 -0.083033 -0.011430 0.000019 0.000027 0.0000187 0.0000056 0.000067 0.000044 +2012 10 5 56205 0.167775 0.329688 0.3706736 0.0005530 -0.082700 -0.011502 0.000019 0.000027 0.0000043 0.0000053 0.000063 0.000038 +2012 10 6 56206 0.166829 0.328457 0.3701593 0.0005139 -0.082229 -0.011617 0.000019 0.000027 0.0000253 0.0000053 0.000071 0.000043 +2012 10 7 56207 0.165776 0.327262 0.3696645 0.0005181 -0.081924 -0.011720 0.000019 0.000027 0.0000081 0.0000059 0.000085 0.000053 +2012 10 8 56208 0.164811 0.326195 0.3691114 0.0006293 -0.081837 -0.011720 0.000019 0.000033 0.0000027 0.0000060 0.000096 0.000064 +2012 10 9 56209 0.163850 0.325275 0.3684334 0.0007981 -0.081782 -0.011561 0.000016 0.000030 0.0000205 0.0000053 0.000113 0.000075 +2012 10 10 56210 0.162665 0.324057 0.3675127 0.0009569 -0.081579 -0.011371 0.000015 0.000030 0.0000296 0.0000053 0.000127 0.000085 +2012 10 11 56211 0.161680 0.322876 0.3664814 0.0011412 -0.081328 -0.011319 0.000017 0.000030 0.0000053 0.0000059 0.000140 0.000095 +2012 10 12 56212 0.160551 0.322152 0.3652754 0.0013041 -0.081345 -0.011451 0.000019 0.000030 0.0000001 0.0000059 0.000057 0.000038 +2012 10 13 56213 0.159093 0.321446 0.3638545 0.0014383 -0.081515 -0.011514 0.000017 0.000030 0.0000110 0.0000056 0.000026 0.000017 +2012 10 14 56214 0.157601 0.320568 0.3623838 0.0015090 -0.081653 -0.011362 0.000017 0.000030 0.0000069 0.0000060 0.000025 0.000016 +2012 10 15 56215 0.156054 0.319968 0.3608963 0.0014698 -0.081632 -0.011090 0.000022 0.000032 0.0000087 0.0000066 0.000023 0.000015 +2012 10 16 56216 0.154180 0.319360 0.3594887 0.0013351 -0.081463 -0.010877 0.000020 0.000032 0.0000017 0.0000063 0.000021 0.000014 +2012 10 17 56217 0.152154 0.318297 0.3582199 0.0011582 -0.081128 -0.010796 0.000018 0.000032 0.0000113 0.0000060 0.000020 0.000014 +2012 10 18 56218 0.150678 0.317253 0.3571501 0.0009695 -0.080927 -0.010744 0.000016 0.000030 0.0000201 0.0000058 0.000019 0.000013 +2012 10 19 56219 0.149569 0.316662 0.3562671 0.0008218 -0.080929 -0.010654 0.000018 0.000033 0.0000044 0.0000061 0.000019 0.000013 +2012 10 20 56220 0.148043 0.316131 0.3554928 0.0007675 -0.081019 -0.010633 0.000020 0.000033 0.0000085 0.0000061 0.000033 0.000024 +2012 10 21 56221 0.146608 0.315322 0.3547440 0.0007928 -0.080875 -0.010726 0.000020 0.000033 0.0000061 0.0000067 0.000054 0.000039 +2012 10 22 56222 0.145543 0.314380 0.3538588 0.0008641 -0.080466 -0.010836 0.000021 0.000033 0.0000060 0.0000069 0.000074 0.000055 +2012 10 23 56223 0.145007 0.313268 0.3530058 0.0009556 -0.080038 -0.010828 0.000021 0.000030 0.0000018 0.0000059 0.000094 0.000071 +2012 10 24 56224 0.145213 0.312435 0.3519872 0.0010597 -0.079882 -0.010681 0.000022 0.000029 0.0000066 0.0000057 0.000109 0.000080 +2012 10 25 56225 0.145615 0.311852 0.3509033 0.0011278 -0.079954 -0.010492 0.000020 0.000030 0.0000041 0.0000054 0.000123 0.000087 +2012 10 26 56226 0.145955 0.311525 0.3497695 0.0011597 -0.080162 -0.010337 0.000017 0.000030 0.0000016 0.0000051 0.000137 0.000095 +2012 10 27 56227 0.146089 0.311350 0.3485935 0.0011237 -0.080289 -0.010192 0.000017 0.000027 0.0000118 0.0000051 0.000130 0.000092 +2012 10 28 56228 0.145902 0.311464 0.3474828 0.0010661 -0.080347 -0.010066 0.000017 0.000027 0.0000142 0.0000051 0.000115 0.000084 +2012 10 29 56229 0.144985 0.311623 0.3464489 0.0009639 -0.080330 -0.010012 0.000018 0.000028 0.0000077 0.0000058 0.000101 0.000075 +2012 10 30 56230 0.144143 0.311322 0.3455521 0.0008863 -0.080227 -0.010069 0.000018 0.000028 0.0000012 0.0000058 0.000086 0.000068 +2012 10 31 56231 0.143952 0.311344 0.3446854 0.0008094 -0.080110 -0.010203 0.000018 0.000027 0.0000110 0.0000060 0.000074 0.000057 +2012 11 1 56232 0.143335 0.311322 0.3439558 0.0007282 -0.079845 -0.010309 0.000018 0.000027 0.0000078 0.0000060 0.000063 0.000045 +2012 11 2 56233 0.142496 0.311202 0.3432281 0.0007022 -0.079396 -0.010347 0.000018 0.000027 0.0000018 0.0000056 0.000051 0.000033 +2012 11 3 56234 0.141501 0.311090 0.3425405 0.0007021 -0.078835 -0.010344 0.000018 0.000027 0.0000043 0.0000056 0.000077 0.000053 +2012 11 4 56235 0.140655 0.310338 0.3417878 0.0008071 -0.078448 -0.010351 0.000018 0.000024 0.0000070 0.0000056 0.000117 0.000086 +2012 11 5 56236 0.139661 0.309499 0.3409540 0.0008889 -0.078331 -0.010337 0.000018 0.000023 0.0000056 0.0000058 0.000158 0.000121 +2012 11 6 56237 0.138701 0.308399 0.3400027 0.0010174 -0.078223 -0.010183 0.000018 0.000026 0.0000016 0.0000058 0.000199 0.000154 +2012 11 7 56238 0.138146 0.307641 0.3389408 0.0011536 -0.078078 -0.009984 0.000017 0.000026 0.0000081 0.0000061 0.000166 0.000128 +2012 11 8 56239 0.137681 0.306960 0.3377096 0.0012756 -0.077951 -0.009889 0.000017 0.000026 0.0000151 0.0000061 0.000115 0.000087 +2012 11 9 56240 0.137470 0.306697 0.3363673 0.0013616 -0.078036 -0.009969 0.000019 0.000026 0.0000035 0.0000065 0.000064 0.000047 +2012 11 10 56241 0.136415 0.306862 0.3349822 0.0014158 -0.078263 -0.010036 0.000019 0.000026 0.0000117 0.0000065 0.000054 0.000038 +2012 11 11 56242 0.135140 0.306608 0.3335328 0.0014946 -0.078443 -0.009956 0.000017 0.000026 0.0000114 0.0000057 0.000059 0.000042 +2012 11 12 56243 0.134281 0.306265 0.3320174 0.0015146 -0.078354 -0.009773 0.000018 0.000027 0.0000078 0.0000060 0.000066 0.000045 +2012 11 13 56244 0.133366 0.305787 0.3305276 0.0014486 -0.077924 -0.009650 0.000018 0.000027 0.0000017 0.0000060 0.000071 0.000049 +2012 11 14 56245 0.132333 0.305606 0.3291377 0.0013427 -0.077283 -0.009701 0.000017 0.000026 0.0000061 0.0000060 0.000079 0.000054 +2012 11 15 56246 0.131284 0.305387 0.3278387 0.0012898 -0.076871 -0.009727 0.000017 0.000026 0.0000094 0.0000060 0.000088 0.000060 +2012 11 16 56247 0.130376 0.305026 0.3265250 0.0012852 -0.076965 -0.009628 0.000017 0.000026 0.0000022 0.0000060 0.000097 0.000066 +2012 11 17 56248 0.129972 0.304514 0.3252155 0.0013455 -0.077475 -0.009546 0.000017 0.000026 0.0000050 0.0000060 0.000087 0.000059 +2012 11 18 56249 0.129616 0.304629 0.3237804 0.0015122 -0.077747 -0.009663 0.000017 0.000026 0.0000057 0.0000060 0.000071 0.000048 +2012 11 19 56250 0.128850 0.304488 0.3221868 0.0016714 -0.077499 -0.009864 0.000020 0.000036 0.0000111 0.0000063 0.000055 0.000037 +2012 11 20 56251 0.127719 0.304116 0.3204734 0.0017408 -0.077080 -0.009872 0.000020 0.000036 0.0000033 0.0000063 0.000039 0.000025 +2012 11 21 56252 0.126270 0.303725 0.3187318 0.0017465 -0.076641 -0.009673 0.000017 0.000030 0.0000002 0.0000058 0.000063 0.000045 +2012 11 22 56253 0.124830 0.303039 0.3170010 0.0017007 -0.076630 -0.009377 0.000017 0.000030 0.0000085 0.0000058 0.000066 0.000048 +2012 11 23 56254 0.123682 0.302270 0.3153139 0.0016361 -0.076771 -0.009188 0.000017 0.000030 0.0000077 0.0000058 0.000058 0.000041 +2012 11 24 56255 0.122815 0.301758 0.3137377 0.0015098 -0.076867 -0.009113 0.000017 0.000030 0.0000068 0.0000061 0.000050 0.000035 +2012 11 25 56256 0.121446 0.301305 0.3123183 0.0013572 -0.076951 -0.009048 0.000017 0.000030 0.0000058 0.0000061 0.000042 0.000028 +2012 11 26 56257 0.120330 0.300698 0.3110089 0.0012209 -0.077074 -0.008989 0.000018 0.000035 0.0000068 0.0000059 0.000034 0.000022 +2012 11 27 56258 0.120111 0.300372 0.3098756 0.0010843 -0.077105 -0.008958 0.000018 0.000035 0.0000017 0.0000059 0.000026 0.000016 +2012 11 28 56259 0.119773 0.300632 0.3088455 0.0009915 -0.077032 -0.009021 0.000017 0.000036 0.0000170 0.0000060 0.000024 0.000014 +2012 11 29 56260 0.119249 0.300583 0.3079206 0.0009233 -0.076850 -0.009084 0.000017 0.000036 0.0000051 0.0000060 0.000023 0.000013 +2012 11 30 56261 0.118883 0.300539 0.3070117 0.0008703 -0.076574 -0.009076 0.000017 0.000036 0.0000011 0.0000060 0.000022 0.000013 +2012 12 1 56262 0.118435 0.300220 0.3061411 0.0009115 -0.076253 -0.009042 0.000017 0.000036 0.0000048 0.0000060 0.000031 0.000018 +2012 12 2 56263 0.117747 0.299784 0.3051720 0.0010053 -0.076024 -0.009077 0.000017 0.000032 0.0000059 0.0000060 0.000043 0.000025 +2012 12 3 56264 0.116649 0.299475 0.3041420 0.0010707 -0.075946 -0.009175 0.000019 0.000031 0.0000051 0.0000062 0.000055 0.000031 +2012 12 4 56265 0.115800 0.299221 0.3030426 0.0011420 -0.075893 -0.009208 0.000019 0.000035 0.0000013 0.0000062 0.000068 0.000038 +2012 12 5 56266 0.115161 0.299515 0.3018303 0.0012547 -0.075773 -0.009071 0.000018 0.000034 0.0000154 0.0000060 0.000060 0.000039 +2012 12 6 56267 0.114070 0.299438 0.3005523 0.0013252 -0.075593 -0.008851 0.000018 0.000034 0.0000087 0.0000060 0.000051 0.000039 +2012 12 7 56268 0.112562 0.299270 0.2991894 0.0013649 -0.075563 -0.008680 0.000020 0.000034 0.0000020 0.0000064 0.000042 0.000038 +2012 12 8 56269 0.110757 0.299082 0.2977818 0.0013447 -0.075753 -0.008565 0.000020 0.000034 0.0000138 0.0000064 0.000037 0.000034 +2012 12 9 56270 0.109587 0.298335 0.2964589 0.0013415 -0.076048 -0.008423 0.000018 0.000034 0.0000101 0.0000056 0.000032 0.000028 +2012 12 10 56271 0.108678 0.297733 0.2951313 0.0013011 -0.076225 -0.008277 0.000018 0.000034 0.0000070 0.0000055 0.000030 0.000022 +2012 12 11 56272 0.107369 0.297098 0.2938971 0.0011538 -0.076133 -0.008291 0.000018 0.000034 0.0000013 0.0000059 0.000025 0.000016 +2012 12 12 56273 0.106168 0.296689 0.2928453 0.0010051 -0.075738 -0.008464 0.000018 0.000031 0.0000203 0.0000059 0.000030 0.000024 +2012 12 13 56274 0.104749 0.296358 0.2919197 0.0009091 -0.075317 -0.008612 0.000018 0.000031 0.0000348 0.0000062 0.000040 0.000035 +2012 12 14 56275 0.103055 0.295597 0.2909741 0.0009133 -0.075182 -0.008537 0.000020 0.000033 0.0000075 0.0000069 0.000050 0.000045 +2012 12 15 56276 0.101061 0.294850 0.2899782 0.0009629 -0.075330 -0.008370 0.000020 0.000033 0.0000119 0.0000066 0.000059 0.000054 +2012 12 16 56277 0.098998 0.293924 0.2890035 0.0010471 -0.075492 -0.008428 0.000020 0.000033 0.0000090 0.0000062 0.000057 0.000048 +2012 12 17 56278 0.096862 0.293381 0.2879406 0.0010959 -0.075236 -0.008636 0.000021 0.000035 0.0000086 0.0000073 0.000059 0.000046 +2012 12 18 56279 0.094647 0.292659 0.2868441 0.0011422 -0.074898 -0.008768 0.000023 0.000035 0.0000050 0.0000076 0.000061 0.000044 +2012 12 19 56280 0.093111 0.291768 0.2857065 0.0011148 -0.074889 -0.008699 0.000019 0.000032 0.0000014 0.0000062 0.000063 0.000045 +2012 12 20 56281 0.091472 0.291349 0.2846565 0.0009436 -0.075128 -0.008482 0.000019 0.000032 0.0000084 0.0000059 0.000068 0.000046 +2012 12 21 56282 0.090028 0.291117 0.2837893 0.0007936 -0.075305 -0.008364 0.000022 0.000032 0.0000020 0.0000062 0.000085 0.000059 +2012 12 22 56283 0.088588 0.291159 0.2830603 0.0007287 -0.075316 -0.008401 0.000019 0.000032 0.0000111 0.0000062 0.000098 0.000072 +2012 12 23 56284 0.087501 0.290712 0.2823224 0.0006926 -0.075265 -0.008461 0.000019 0.000032 0.0000088 0.0000062 0.000110 0.000084 +2012 12 24 56285 0.086882 0.290363 0.2816902 0.0006156 -0.075321 -0.008468 0.000021 0.000030 0.0000068 0.0000064 0.000126 0.000098 +2012 12 25 56286 0.086339 0.290236 0.2811304 0.0005151 -0.075336 -0.008452 0.000018 0.000030 0.0000050 0.0000060 0.000137 0.000111 +2012 12 26 56287 0.085694 0.290416 0.2806345 0.0004547 -0.075266 -0.008463 0.000018 0.000029 0.0000032 0.0000059 0.000145 0.000124 +2012 12 27 56288 0.084727 0.290487 0.2801731 0.0004607 -0.075144 -0.008467 0.000020 0.000032 0.0000015 0.0000063 0.000157 0.000136 +2012 12 28 56289 0.082947 0.290475 0.2797111 0.0004567 -0.075116 -0.008405 0.000020 0.000032 0.0000003 0.0000063 0.000053 0.000044 +2012 12 29 56290 0.080756 0.290205 0.2792361 0.0005094 -0.074953 -0.008337 0.000018 0.000029 0.0000070 0.0000059 0.000026 0.000016 +2012 12 30 56291 0.079007 0.289959 0.2786180 0.0006577 -0.074710 -0.008360 0.000018 0.000029 0.0000093 0.0000059 0.000037 0.000023 +2012 12 31 56292 0.077238 0.289933 0.2778751 0.0007788 -0.074509 -0.008494 0.000018 0.000028 0.0000082 0.0000061 0.000049 0.000029 diff --git a/src/test/resources/eopc04/eopc04.13 b/src/test/resources/eopc04/eopc04.13 new file mode 100644 index 0000000000..a35bc0f7c9 --- /dev/null +++ b/src/test/resources/eopc04/eopc04.13 @@ -0,0 +1,379 @@ + EARTH ORIENTATION PARAMETER (EOP) PRODUCT CENTER CENTER (PARIS OBSERVATORY) + INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE + EOP (IERS) 14 C04 TIME SERIES + Description: https://hpiers.obspm.fr/eoppc/eop/eopc04/C04.guide.pdf + contact: christian.bizouard@obspm.fr + + + FORMAT(3(I4),I7,2(F11.6),2(F12.7),2(F11.6),2(F11.6),2(F11.7),2(F12.6)) +################################################################################## + + Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err + " " s s " " " " s s " " + (0h UTC) + +2013 1 1 56293 0.075311 0.289933 0.2770733 0.0008908 -0.074368 -0.008664 0.000065 0.000062 0.0000074 0.0000084 0.000113 0.000041 +2013 1 2 56294 0.073263 0.289986 0.2761073 0.0010380 -0.074256 -0.008651 0.000065 0.000062 0.0000069 0.0000084 0.000113 0.000041 +2013 1 3 56295 0.071343 0.289771 0.2750342 0.0011409 -0.074141 -0.008472 0.000065 0.000063 0.0000064 0.0000085 0.000113 0.000040 +2013 1 4 56296 0.070023 0.289588 0.2738599 0.0012019 -0.074463 -0.008261 0.000065 0.000063 0.0000041 0.0000085 0.000113 0.000040 +2013 1 5 56297 0.069000 0.289604 0.2726428 0.0012066 -0.074754 -0.008045 0.000065 0.000063 0.0000152 0.0000085 0.000113 0.000040 +2013 1 6 56298 0.068165 0.289674 0.2714446 0.0011391 -0.075099 -0.007859 0.000065 0.000063 0.0000163 0.0000085 0.000113 0.000040 +2013 1 7 56299 0.067223 0.290116 0.2703909 0.0009850 -0.075496 -0.007775 0.000066 0.000063 0.0000099 0.0000085 0.000113 0.000040 +2013 1 8 56300 0.066251 0.290552 0.2694696 0.0008888 -0.075803 -0.007926 0.000066 0.000063 0.0000036 0.0000085 0.000113 0.000040 +2013 1 9 56301 0.065348 0.291376 0.2686090 0.0008315 -0.075778 -0.008151 0.000066 0.000063 0.0000020 0.0000085 0.000113 0.000041 +2013 1 10 56302 0.064418 0.292248 0.2677610 0.0008859 -0.075524 -0.008472 0.000066 0.000063 0.0000016 0.0000085 0.000113 0.000040 +2013 1 11 56303 0.063242 0.293086 0.2668054 0.0010276 -0.075305 -0.008709 0.000066 0.000063 0.0000034 0.0000085 0.000113 0.000040 +2013 1 12 56304 0.061991 0.293682 0.2657105 0.0011763 -0.075243 -0.008524 0.000066 0.000063 0.0000089 0.0000085 0.000113 0.000040 +2013 1 13 56305 0.060508 0.294356 0.2644765 0.0013109 -0.075147 -0.008230 0.000066 0.000063 0.0000092 0.0000085 0.000113 0.000041 +2013 1 14 56306 0.059252 0.295183 0.2630607 0.0014629 -0.074931 -0.008111 0.000066 0.000063 0.0000111 0.0000085 0.000113 0.000041 +2013 1 15 56307 0.058334 0.296001 0.2615521 0.0015359 -0.074779 -0.008159 0.000066 0.000063 0.0000120 0.0000085 0.000113 0.000041 +2013 1 16 56308 0.057061 0.296556 0.2600806 0.0014533 -0.074835 -0.008310 0.000066 0.000063 0.0000040 0.0000085 0.000113 0.000040 +2013 1 17 56309 0.055424 0.297495 0.2586844 0.0013488 -0.075242 -0.008361 0.000066 0.000063 0.0000024 0.0000085 0.000113 0.000040 +2013 1 18 56310 0.053896 0.298480 0.2573767 0.0012530 -0.075596 -0.008416 0.000066 0.000063 0.0000025 0.0000085 0.000113 0.000040 +2013 1 19 56311 0.052957 0.299381 0.2561106 0.0012012 -0.075711 -0.008597 0.000066 0.000063 0.0000087 0.0000085 0.000113 0.000040 +2013 1 20 56312 0.051718 0.300807 0.2549491 0.0011213 -0.075733 -0.008823 0.000066 0.000063 0.0000171 0.0000085 0.000113 0.000040 +2013 1 21 56313 0.050256 0.301917 0.2538439 0.0010740 -0.075805 -0.009026 0.000066 0.000062 0.0000146 0.0000085 0.000113 0.000040 +2013 1 22 56314 0.049362 0.302844 0.2527727 0.0010638 -0.075787 -0.009157 0.000066 0.000062 0.0000087 0.0000085 0.000116 0.000040 +2013 1 23 56315 0.048655 0.304129 0.2517270 0.0010841 -0.075406 -0.008895 0.000066 0.000063 0.0000037 0.0000085 0.000113 0.000039 +2013 1 24 56316 0.047844 0.305256 0.2506537 0.0011081 -0.075453 -0.008912 0.000066 0.000063 0.0000035 0.0000086 0.000113 0.000039 +2013 1 25 56317 0.047080 0.305956 0.2495288 0.0011205 -0.075681 -0.008975 0.000066 0.000063 0.0000046 0.0000086 0.000113 0.000039 +2013 1 26 56318 0.046328 0.306582 0.2483738 0.0011736 -0.075602 -0.009004 0.000066 0.000063 0.0000082 0.0000086 0.000113 0.000039 +2013 1 27 56319 0.045576 0.306920 0.2471277 0.0013019 -0.075308 -0.009079 0.000066 0.000063 0.0000101 0.0000086 0.000113 0.000039 +2013 1 28 56320 0.044920 0.307242 0.2457648 0.0014110 -0.074911 -0.009214 0.000066 0.000063 0.0000076 0.0000086 0.000113 0.000039 +2013 1 29 56321 0.043974 0.307636 0.2443026 0.0014984 -0.074569 -0.009321 0.000066 0.000063 0.0000036 0.0000086 0.000113 0.000038 +2013 1 30 56322 0.043052 0.308184 0.2427638 0.0015462 -0.074562 -0.009301 0.000066 0.000063 0.0000127 0.0000086 0.000113 0.000038 +2013 1 31 56323 0.042733 0.309243 0.2412122 0.0015434 -0.074634 -0.009192 0.000066 0.000063 0.0000199 0.0000086 0.000113 0.000039 +2013 2 1 56324 0.042307 0.310564 0.2397387 0.0014218 -0.074706 -0.009034 0.000066 0.000063 0.0000069 0.0000087 0.000113 0.000039 +2013 2 2 56325 0.042565 0.311910 0.2383410 0.0012953 -0.074901 -0.008885 0.000066 0.000063 0.0000089 0.0000087 0.000116 0.000039 +2013 2 3 56326 0.042556 0.313623 0.2371329 0.0011535 -0.075174 -0.008798 0.000066 0.000063 0.0000107 0.0000087 0.000116 0.000040 +2013 2 4 56327 0.041961 0.314752 0.2360361 0.0010689 -0.075512 -0.008839 0.000066 0.000063 0.0000078 0.0000087 0.000118 0.000040 +2013 2 5 56328 0.040987 0.315647 0.2350318 0.0009328 -0.075865 -0.009081 0.000066 0.000063 0.0000039 0.0000087 0.000118 0.000040 +2013 2 6 56329 0.039917 0.316854 0.2340878 0.0009198 -0.076103 -0.009456 0.000066 0.000063 0.0000035 0.0000087 0.000121 0.000041 +2013 2 7 56330 0.038911 0.318577 0.2331423 0.0009909 -0.076210 -0.009822 0.000066 0.000063 0.0000040 0.0000087 0.000121 0.000042 +2013 2 8 56331 0.037403 0.319854 0.2321261 0.0011237 -0.076213 -0.009926 0.000066 0.000063 0.0000045 0.0000088 0.000123 0.000043 +2013 2 9 56332 0.036088 0.320567 0.2309282 0.0012763 -0.076172 -0.009661 0.000066 0.000063 0.0000078 0.0000088 0.000126 0.000043 +2013 2 10 56333 0.035434 0.321256 0.2296031 0.0013759 -0.076083 -0.009226 0.000066 0.000063 0.0000095 0.0000088 0.000126 0.000044 +2013 2 11 56334 0.035162 0.322147 0.2281975 0.0014362 -0.075976 -0.008927 0.000066 0.000063 0.0000344 0.0000088 0.000128 0.000045 +2013 2 12 56335 0.034953 0.323255 0.2267689 0.0013880 -0.075952 -0.008915 0.000066 0.000063 0.0000716 0.0000088 0.000131 0.000046 +2013 2 13 56336 0.034408 0.324163 0.2254444 0.0012134 -0.075804 -0.009304 0.000066 0.000063 0.0000184 0.0000089 0.000131 0.000047 +2013 2 14 56337 0.033748 0.324632 0.2242842 0.0010509 -0.075738 -0.009683 0.000066 0.000064 0.0000024 0.0000089 0.000133 0.000048 +2013 2 15 56338 0.033854 0.325158 0.2232784 0.0009396 -0.075839 -0.009771 0.000066 0.000064 0.0000017 0.0000089 0.000133 0.000048 +2013 2 16 56339 0.033944 0.325995 0.2223751 0.0008369 -0.075913 -0.009863 0.000066 0.000064 0.0000075 0.0000090 0.000133 0.000048 +2013 2 17 56340 0.033659 0.326959 0.2215763 0.0007366 -0.075950 -0.009985 0.000066 0.000064 0.0000105 0.0000090 0.000136 0.000049 +2013 2 18 56341 0.033288 0.327869 0.2208975 0.0006448 -0.075972 -0.010132 0.000066 0.000064 0.0000088 0.0000091 0.000136 0.000050 +2013 2 19 56342 0.032983 0.329206 0.2202815 0.0005863 -0.075890 -0.010299 0.000066 0.000064 0.0000059 0.0000091 0.000138 0.000050 +2013 2 20 56343 0.032922 0.330362 0.2196575 0.0006594 -0.075819 -0.010479 0.000066 0.000064 0.0000081 0.0000092 0.000138 0.000051 +2013 2 21 56344 0.032768 0.331461 0.2189314 0.0007652 -0.075556 -0.010539 0.000066 0.000064 0.0000078 0.0000092 0.000138 0.000051 +2013 2 22 56345 0.032541 0.332466 0.2180783 0.0009435 -0.075217 -0.010495 0.000066 0.000064 0.0000064 0.0000093 0.000138 0.000051 +2013 2 23 56346 0.032011 0.333495 0.2170539 0.0011225 -0.075113 -0.010528 0.000066 0.000064 0.0000074 0.0000094 0.000138 0.000051 +2013 2 24 56347 0.031551 0.334442 0.2158510 0.0012904 -0.075050 -0.010652 0.000066 0.000064 0.0000077 0.0000095 0.000138 0.000050 +2013 2 25 56348 0.030716 0.335000 0.2144735 0.0014588 -0.074866 -0.010789 0.000066 0.000064 0.0000066 0.0000096 0.000138 0.000050 +2013 2 26 56349 0.029613 0.335127 0.2129265 0.0016370 -0.074657 -0.010835 0.000066 0.000064 0.0000052 0.0000097 0.000136 0.000050 +2013 2 27 56350 0.029181 0.335809 0.2112230 0.0017356 -0.074585 -0.010736 0.000066 0.000064 0.0000147 0.0000098 0.000136 0.000049 +2013 2 28 56351 0.029538 0.336818 0.2094548 0.0017356 -0.074721 -0.010590 0.000066 0.000064 0.0000220 0.0000099 0.000136 0.000049 +2013 3 1 56352 0.030228 0.338183 0.2077328 0.0017292 -0.074958 -0.010520 0.000066 0.000064 0.0000105 0.0000100 0.000133 0.000049 +2013 3 2 56353 0.030508 0.339800 0.2060765 0.0015897 -0.075127 -0.010574 0.000066 0.000064 0.0000078 0.0000102 0.000133 0.000048 +2013 3 3 56354 0.030810 0.341317 0.2045314 0.0014927 -0.075180 -0.010701 0.000066 0.000064 0.0000078 0.0000104 0.000131 0.000047 +2013 3 4 56355 0.031141 0.342877 0.2030446 0.0014688 -0.075193 -0.010852 0.000066 0.000064 0.0000060 0.0000106 0.000128 0.000047 +2013 3 5 56356 0.031568 0.344071 0.2015923 0.0014735 -0.075251 -0.011049 0.000066 0.000064 0.0000033 0.0000108 0.000128 0.000046 +2013 3 6 56357 0.031980 0.345080 0.2001178 0.0015099 -0.075383 -0.011271 0.000067 0.000064 0.0000029 0.0000109 0.000126 0.000046 +2013 3 7 56358 0.032861 0.345739 0.1985736 0.0016168 -0.075485 -0.011490 0.000067 0.000064 0.0000030 0.0000111 0.000126 0.000045 +2013 3 8 56359 0.034204 0.346436 0.1969145 0.0017040 -0.075480 -0.011560 0.000067 0.000064 0.0000031 0.0000113 0.000123 0.000045 +2013 3 9 56360 0.035541 0.347380 0.1951483 0.0018194 -0.075354 -0.011394 0.000067 0.000064 0.0000064 0.0000115 0.000123 0.000045 +2013 3 10 56361 0.036619 0.348529 0.1932676 0.0019040 -0.075219 -0.011063 0.000067 0.000064 0.0000122 0.0000117 0.000123 0.000044 +2013 3 11 56362 0.037638 0.349952 0.1913735 0.0018998 -0.075158 -0.010791 0.000067 0.000064 0.0000105 0.0000118 0.000123 0.000044 +2013 3 12 56363 0.038793 0.351874 0.1894884 0.0018711 -0.075166 -0.010777 0.000067 0.000064 0.0000067 0.0000120 0.000121 0.000044 +2013 3 13 56364 0.039907 0.354162 0.1876533 0.0017781 -0.075169 -0.011029 0.000067 0.000064 0.0000048 0.0000121 0.000121 0.000044 +2013 3 14 56365 0.040538 0.356068 0.1859071 0.0016723 -0.075129 -0.011425 0.000067 0.000064 0.0000045 0.0000123 0.000121 0.000043 +2013 3 15 56366 0.040952 0.357486 0.1843035 0.0015482 -0.075090 -0.011732 0.000067 0.000064 0.0000045 0.0000124 0.000118 0.000043 +2013 3 16 56367 0.040887 0.358706 0.1828133 0.0014157 -0.075076 -0.011870 0.000067 0.000065 0.0000090 0.0000126 0.000118 0.000042 +2013 3 17 56368 0.040975 0.359641 0.1814599 0.0013046 -0.075090 -0.011930 0.000067 0.000065 0.0000124 0.0000127 0.000116 0.000042 +2013 3 18 56369 0.041530 0.360311 0.1802067 0.0012321 -0.075045 -0.012036 0.000067 0.000065 0.0000101 0.0000129 0.000116 0.000041 +2013 3 19 56370 0.042428 0.360712 0.1790100 0.0011480 -0.074849 -0.012204 0.000067 0.000065 0.0000055 0.0000130 0.000116 0.000041 +2013 3 20 56371 0.043572 0.361292 0.1778473 0.0011545 -0.074571 -0.012187 0.000067 0.000065 0.0000082 0.0000131 0.000113 0.000040 +2013 3 21 56372 0.044690 0.362195 0.1766711 0.0011914 -0.074252 -0.012015 0.000067 0.000065 0.0000103 0.0000133 0.000113 0.000040 +2013 3 22 56373 0.045148 0.363217 0.1754811 0.0012585 -0.074174 -0.012047 0.000067 0.000065 0.0000035 0.0000134 0.000111 0.000039 +2013 3 23 56374 0.045636 0.364449 0.1741399 0.0014257 -0.074103 -0.012061 0.000067 0.000065 0.0000111 0.0000135 0.000108 0.000038 +2013 3 24 56375 0.046494 0.365575 0.1726491 0.0015786 -0.074070 -0.012147 0.000067 0.000065 0.0000157 0.0000136 0.000108 0.000037 +2013 3 25 56376 0.047552 0.366953 0.1710111 0.0017030 -0.074043 -0.012275 0.000067 0.000065 0.0000109 0.0000137 0.000106 0.000037 +2013 3 26 56377 0.048035 0.368228 0.1692365 0.0018518 -0.073959 -0.012278 0.000067 0.000065 0.0000033 0.0000138 0.000106 0.000036 +2013 3 27 56378 0.048589 0.369493 0.1673192 0.0019554 -0.073996 -0.012121 0.000067 0.000065 0.0000014 0.0000139 0.000106 0.000036 +2013 3 28 56379 0.049531 0.370939 0.1653403 0.0019499 -0.074046 -0.011950 0.000067 0.000065 0.0000118 0.0000140 0.000106 0.000036 +2013 3 29 56380 0.050469 0.372139 0.1633911 0.0018911 -0.074174 -0.011964 0.000067 0.000065 0.0000224 0.0000141 0.000106 0.000036 +2013 3 30 56381 0.051097 0.373351 0.1615324 0.0017897 -0.074300 -0.012214 0.000067 0.000065 0.0000204 0.0000142 0.000106 0.000036 +2013 3 31 56382 0.051465 0.374470 0.1598065 0.0016856 -0.074289 -0.012544 0.000067 0.000065 0.0000167 0.0000143 0.000106 0.000036 +2013 4 1 56383 0.051243 0.375362 0.1581798 0.0016203 -0.074160 -0.012772 0.000068 0.000065 0.0000113 0.0000143 0.000106 0.000036 +2013 4 2 56384 0.050849 0.376099 0.1566140 0.0015651 -0.074059 -0.012866 0.000068 0.000065 0.0000069 0.0000144 0.000106 0.000036 +2013 4 3 56385 0.050910 0.376790 0.1550287 0.0015671 -0.074076 -0.012925 0.000068 0.000065 0.0000025 0.0000145 0.000106 0.000036 +2013 4 4 56386 0.051450 0.377420 0.1534221 0.0016253 -0.074312 -0.012922 0.000068 0.000065 0.0000019 0.0000145 0.000106 0.000037 +2013 4 5 56387 0.052286 0.378138 0.1517310 0.0017599 -0.074472 -0.012888 0.000068 0.000065 0.0000022 0.0000146 0.000106 0.000037 +2013 4 6 56388 0.052841 0.378806 0.1498885 0.0018642 -0.074228 -0.012828 0.000068 0.000065 0.0000116 0.0000146 0.000106 0.000037 +2013 4 7 56389 0.053079 0.379354 0.1479962 0.0018990 -0.073876 -0.012675 0.000068 0.000065 0.0000176 0.0000147 0.000106 0.000037 +2013 4 8 56390 0.053489 0.380033 0.1461143 0.0018727 -0.073685 -0.012499 0.000068 0.000065 0.0000131 0.0000147 0.000106 0.000037 +2013 4 9 56391 0.054072 0.381235 0.1442868 0.0018038 -0.073643 -0.012440 0.000068 0.000065 0.0000050 0.0000147 0.000106 0.000038 +2013 4 10 56392 0.054962 0.382432 0.1425417 0.0016741 -0.073924 -0.012553 0.000068 0.000065 0.0000026 0.0000147 0.000106 0.000038 +2013 4 11 56393 0.055508 0.383574 0.1409411 0.0015045 -0.073889 -0.012868 0.000068 0.000065 0.0000057 0.0000148 0.000108 0.000039 +2013 4 12 56394 0.055501 0.384312 0.1395086 0.0013633 -0.073637 -0.013136 0.000068 0.000065 0.0000049 0.0000148 0.000108 0.000039 +2013 4 13 56395 0.055436 0.385129 0.1382103 0.0012303 -0.073591 -0.013245 0.000068 0.000065 0.0000119 0.0000148 0.000108 0.000040 +2013 4 14 56396 0.055457 0.385712 0.1370216 0.0011527 -0.073659 -0.013256 0.000068 0.000065 0.0000135 0.0000147 0.000108 0.000040 +2013 4 15 56397 0.055562 0.386062 0.1359141 0.0010929 -0.073681 -0.013273 0.000068 0.000065 0.0000092 0.0000147 0.000111 0.000041 +2013 4 16 56398 0.055865 0.386438 0.1348138 0.0011176 -0.073620 -0.013345 0.000068 0.000065 0.0000064 0.0000147 0.000111 0.000042 +2013 4 17 56399 0.056437 0.386990 0.1336824 0.0011613 -0.073515 -0.013413 0.000068 0.000065 0.0000048 0.0000147 0.000111 0.000042 +2013 4 18 56400 0.057487 0.387546 0.1325258 0.0011959 -0.074003 -0.013416 0.000068 0.000065 0.0000034 0.0000146 0.000113 0.000043 +2013 4 19 56401 0.058643 0.388716 0.1312766 0.0013074 -0.073236 -0.013267 0.000068 0.000065 0.0000021 0.0000146 0.000113 0.000043 +2013 4 20 56402 0.059379 0.390006 0.1298811 0.0014754 -0.072780 -0.013161 0.000068 0.000065 0.0000094 0.0000146 0.000113 0.000043 +2013 4 21 56403 0.059982 0.391007 0.1283435 0.0015923 -0.072768 -0.013230 0.000068 0.000065 0.0000128 0.0000145 0.000113 0.000044 +2013 4 22 56404 0.060976 0.392025 0.1266947 0.0016977 -0.072982 -0.013426 0.000067 0.000065 0.0000094 0.0000144 0.000116 0.000044 +2013 4 23 56405 0.061979 0.392889 0.1249550 0.0017958 -0.073278 -0.013532 0.000067 0.000065 0.0000044 0.0000144 0.000116 0.000044 +2013 4 24 56406 0.062952 0.393425 0.1231493 0.0018230 -0.073973 -0.013131 0.000067 0.000065 0.0000050 0.0000143 0.000118 0.000045 +2013 4 25 56407 0.063869 0.393925 0.1213230 0.0017928 -0.074088 -0.012917 0.000067 0.000065 0.0000054 0.0000143 0.000118 0.000046 +2013 4 26 56408 0.064803 0.394246 0.1195762 0.0017021 -0.074034 -0.013006 0.000067 0.000065 0.0000053 0.0000142 0.000121 0.000046 +2013 4 27 56409 0.065665 0.394813 0.1179651 0.0015426 -0.074105 -0.013287 0.000067 0.000065 0.0000132 0.0000141 0.000121 0.000047 +2013 4 28 56410 0.066063 0.395283 0.1165016 0.0013950 -0.074131 -0.013637 0.000067 0.000065 0.0000141 0.0000141 0.000121 0.000048 +2013 4 29 56411 0.066852 0.395523 0.1151293 0.0013614 -0.074052 -0.013829 0.000067 0.000065 0.0000107 0.0000140 0.000123 0.000048 +2013 4 30 56412 0.067865 0.396036 0.1137648 0.0013765 -0.073982 -0.013799 0.000067 0.000065 0.0000075 0.0000140 0.000123 0.000049 +2013 5 1 56413 0.069025 0.396180 0.1123639 0.0014226 -0.074058 -0.013661 0.000067 0.000065 0.0000062 0.0000139 0.000123 0.000050 +2013 5 2 56414 0.070527 0.396898 0.1108750 0.0015343 -0.074238 -0.013553 0.000067 0.000065 0.0000054 0.0000138 0.000126 0.000050 +2013 5 3 56415 0.072161 0.397682 0.1092913 0.0016588 -0.074390 -0.013500 0.000067 0.000065 0.0000046 0.0000138 0.000126 0.000051 +2013 5 4 56416 0.073905 0.398179 0.1076238 0.0016978 -0.074211 -0.013485 0.000067 0.000065 0.0000061 0.0000138 0.000126 0.000051 +2013 5 5 56417 0.075392 0.398755 0.1059290 0.0016712 -0.073852 -0.013459 0.000067 0.000065 0.0000083 0.0000137 0.000128 0.000052 +2013 5 6 56418 0.076739 0.399258 0.1042659 0.0016513 -0.073552 -0.013406 0.000067 0.000065 0.0000106 0.0000137 0.000128 0.000052 +2013 5 7 56419 0.077687 0.399573 0.1026670 0.0015733 -0.073440 -0.013348 0.000067 0.000065 0.0000118 0.0000136 0.000128 0.000052 +2013 5 8 56420 0.078725 0.399726 0.1011230 0.0014590 -0.073988 -0.013148 0.000067 0.000065 0.0000088 0.0000136 0.000128 0.000052 +2013 5 9 56421 0.080078 0.400193 0.0997635 0.0012814 -0.074527 -0.013190 0.000067 0.000065 0.0000086 0.0000136 0.000131 0.000053 +2013 5 10 56422 0.081684 0.400536 0.0985567 0.0011249 -0.074871 -0.013467 0.000067 0.000065 0.0000091 0.0000135 0.000131 0.000052 +2013 5 11 56423 0.084152 0.400866 0.0974615 0.0010251 -0.074842 -0.013680 0.000067 0.000065 0.0000138 0.0000135 0.000131 0.000051 +2013 5 12 56424 0.086383 0.401465 0.0964513 0.0009404 -0.074645 -0.013779 0.000067 0.000065 0.0000201 0.0000135 0.000128 0.000050 +2013 5 13 56425 0.088340 0.402205 0.0955436 0.0009047 -0.074405 -0.013803 0.000067 0.000065 0.0000148 0.0000134 0.000128 0.000050 +2013 5 14 56426 0.089529 0.403293 0.0946587 0.0009003 -0.074207 -0.013817 0.000067 0.000065 0.0000053 0.0000134 0.000128 0.000049 +2013 5 15 56427 0.090703 0.404054 0.0937165 0.0009594 -0.074397 -0.013858 0.000067 0.000065 0.0000044 0.0000134 0.000128 0.000049 +2013 5 16 56428 0.091108 0.404666 0.0927077 0.0010320 -0.074659 -0.013943 0.000067 0.000065 0.0000044 0.0000134 0.000128 0.000050 +2013 5 17 56429 0.091561 0.404812 0.0915998 0.0011958 -0.074742 -0.013945 0.000067 0.000064 0.0000039 0.0000134 0.000131 0.000051 +2013 5 18 56430 0.092183 0.404978 0.0903395 0.0013148 -0.074391 -0.013698 0.000067 0.000064 0.0000092 0.0000134 0.000133 0.000052 +2013 5 19 56431 0.093244 0.404859 0.0889514 0.0014225 -0.074025 -0.013472 0.000067 0.000064 0.0000166 0.0000133 0.000133 0.000053 +2013 5 20 56432 0.094778 0.404863 0.0874681 0.0014982 -0.074030 -0.013389 0.000067 0.000064 0.0000353 0.0000133 0.000136 0.000055 +2013 5 21 56433 0.096646 0.405104 0.0859344 0.0015116 -0.074367 -0.013292 0.000067 0.000064 0.0000583 0.0000133 0.000138 0.000056 +2013 5 22 56434 0.098087 0.405815 0.0844135 0.0014402 -0.074715 -0.012996 0.000067 0.000064 0.0000812 0.0000133 0.000141 0.000058 +2013 5 23 56435 0.099424 0.406665 0.0829814 0.0013784 -0.075184 -0.012887 0.000067 0.000064 0.0000864 0.0000133 0.000141 0.000059 +2013 5 24 56436 0.100672 0.407207 0.0817119 0.0012580 -0.075549 -0.013018 0.000067 0.000064 0.0000258 0.0000133 0.000143 0.000060 +2013 5 25 56437 0.101496 0.407265 0.0805181 0.0011409 -0.075662 -0.013354 0.000067 0.000064 0.0000132 0.0000133 0.000143 0.000061 +2013 5 26 56438 0.102284 0.407252 0.0794325 0.0010312 -0.075630 -0.013781 0.000067 0.000064 0.0000184 0.0000133 0.000146 0.000062 +2013 5 27 56439 0.102837 0.407320 0.0784968 0.0009175 -0.075568 -0.014047 0.000067 0.000064 0.0000180 0.0000133 0.000146 0.000063 +2013 5 28 56440 0.103468 0.407198 0.0776036 0.0009108 -0.075607 -0.013983 0.000067 0.000064 0.0000156 0.0000133 0.000148 0.000063 +2013 5 29 56441 0.104057 0.407260 0.0766822 0.0009119 -0.075803 -0.013719 0.000067 0.000064 0.0000132 0.0000133 0.000148 0.000063 +2013 5 30 56442 0.104772 0.407130 0.0757515 0.0009371 -0.075992 -0.013548 0.000067 0.000064 0.0000114 0.0000133 0.000146 0.000063 +2013 5 31 56443 0.106250 0.406961 0.0748157 0.0009083 -0.076287 -0.013409 0.000067 0.000064 0.0000097 0.0000133 0.000146 0.000062 +2013 6 1 56444 0.108260 0.406678 0.0739657 0.0008125 -0.076510 -0.013314 0.000067 0.000064 0.0000125 0.0000133 0.000146 0.000061 +2013 6 2 56445 0.110238 0.406535 0.0732150 0.0006963 -0.076539 -0.013291 0.000067 0.000064 0.0000170 0.0000133 0.000143 0.000060 +2013 6 3 56446 0.111880 0.406443 0.0725063 0.0006465 -0.076377 -0.013329 0.000067 0.000064 0.0000139 0.0000132 0.000143 0.000059 +2013 6 4 56447 0.113287 0.406036 0.0719077 0.0005541 -0.076234 -0.013356 0.000068 0.000064 0.0000079 0.0000132 0.000141 0.000058 +2013 6 5 56448 0.114222 0.405478 0.0714164 0.0004204 -0.076576 -0.013365 0.000068 0.000064 0.0000096 0.0000132 0.000141 0.000058 +2013 6 6 56449 0.114604 0.404655 0.0710478 0.0003183 -0.077172 -0.013391 0.000068 0.000065 0.0000116 0.0000132 0.000138 0.000057 +2013 6 7 56450 0.114997 0.403825 0.0707807 0.0002378 -0.077735 -0.013518 0.000068 0.000065 0.0000070 0.0000131 0.000138 0.000056 +2013 6 8 56451 0.115635 0.402946 0.0706302 0.0001403 -0.077860 -0.013714 0.000068 0.000065 0.0000057 0.0000131 0.000136 0.000055 +2013 6 9 56452 0.116268 0.402131 0.0705272 0.0000820 -0.077672 -0.013856 0.000068 0.000065 0.0000057 0.0000131 0.000136 0.000054 +2013 6 10 56453 0.117307 0.401368 0.0704108 0.0001016 -0.077376 -0.013854 0.000068 0.000065 0.0000056 0.0000131 0.000133 0.000053 +2013 6 11 56454 0.118210 0.401117 0.0702382 0.0002011 -0.077190 -0.013770 0.000068 0.000065 0.0000055 0.0000130 0.000133 0.000052 +2013 6 12 56455 0.119114 0.400692 0.0699931 0.0002879 -0.077442 -0.013681 0.000068 0.000065 0.0000057 0.0000130 0.000131 0.000051 +2013 6 13 56456 0.120642 0.400080 0.0696368 0.0004226 -0.077895 -0.013653 0.000068 0.000065 0.0000060 0.0000130 0.000131 0.000050 +2013 6 14 56457 0.122357 0.399538 0.0691516 0.0005604 -0.078200 -0.013598 0.000068 0.000065 0.0000062 0.0000130 0.000128 0.000049 +2013 6 15 56458 0.123862 0.398951 0.0684908 0.0006894 -0.078129 -0.013535 0.000068 0.000065 0.0000229 0.0000129 0.000128 0.000048 +2013 6 16 56459 0.125637 0.398579 0.0676997 0.0007732 -0.077921 -0.013459 0.000068 0.000065 0.0000456 0.0000129 0.000126 0.000047 +2013 6 17 56460 0.127327 0.398307 0.0669085 0.0008433 -0.078026 -0.013431 0.000068 0.000065 0.0000345 0.0000129 0.000126 0.000046 +2013 6 18 56461 0.129251 0.397879 0.0661099 0.0008712 -0.078605 -0.013427 0.000068 0.000065 0.0000109 0.0000129 0.000126 0.000046 +2013 6 19 56462 0.131165 0.397933 0.0652706 0.0008211 -0.079223 -0.013232 0.000069 0.000066 0.0000055 0.0000129 0.000123 0.000045 +2013 6 20 56463 0.132622 0.397806 0.0644819 0.0007709 -0.079636 -0.012950 0.000069 0.000066 0.0000048 0.0000128 0.000121 0.000044 +2013 6 21 56464 0.134144 0.397070 0.0637749 0.0006593 -0.079856 -0.012895 0.000069 0.000066 0.0000028 0.0000128 0.000121 0.000043 +2013 6 22 56465 0.135348 0.396517 0.0631586 0.0005808 -0.079835 -0.013098 0.000069 0.000066 0.0000025 0.0000128 0.000121 0.000043 +2013 6 23 56466 0.136112 0.395972 0.0625930 0.0005616 -0.079682 -0.013368 0.000069 0.000066 0.0000031 0.0000128 0.000123 0.000043 +2013 6 24 56467 0.136395 0.395663 0.0620134 0.0006036 -0.079570 -0.013452 0.000069 0.000066 0.0000036 0.0000128 0.000123 0.000044 +2013 6 25 56468 0.136903 0.395025 0.0613647 0.0007057 -0.079576 -0.013343 0.000069 0.000066 0.0000042 0.0000128 0.000123 0.000044 +2013 6 26 56469 0.137507 0.394414 0.0606340 0.0007645 -0.079635 -0.013177 0.000069 0.000066 0.0000074 0.0000128 0.000126 0.000044 +2013 6 27 56470 0.137909 0.393820 0.0598371 0.0007990 -0.079677 -0.013174 0.000069 0.000066 0.0000052 0.0000128 0.000126 0.000044 +2013 6 28 56471 0.138669 0.392917 0.0590462 0.0007593 -0.080118 -0.013182 0.000069 0.000066 0.0000022 0.0000128 0.000126 0.000044 +2013 6 29 56472 0.140149 0.392020 0.0583264 0.0006512 -0.080708 -0.013095 0.000069 0.000066 0.0000089 0.0000128 0.000126 0.000044 +2013 6 30 56473 0.141473 0.391466 0.0577434 0.0004923 -0.081168 -0.013039 0.000069 0.000066 0.0000191 0.0000128 0.000126 0.000045 +2013 7 1 56474 0.142539 0.390858 0.0573555 0.0002903 -0.081250 -0.013110 0.000069 0.000065 0.0000147 0.0000128 0.000128 0.000045 +2013 7 2 56475 0.144023 0.390190 0.0571643 0.0001182 -0.081088 -0.013235 0.000069 0.000065 0.0000050 0.0000128 0.000128 0.000045 +2013 7 3 56476 0.145661 0.389620 0.0571541 -0.0000571 -0.081233 -0.013379 0.000069 0.000065 0.0000053 0.0000128 0.000131 0.000046 +2013 7 4 56477 0.146693 0.388854 0.0573059 -0.0002287 -0.081685 -0.013475 0.000069 0.000065 0.0000091 0.0000128 0.000131 0.000047 +2013 7 5 56478 0.147627 0.387628 0.0575760 -0.0002912 -0.082214 -0.013580 0.000068 0.000065 0.0000132 0.0000128 0.000133 0.000048 +2013 7 6 56479 0.148912 0.386223 0.0579190 -0.0003477 -0.082472 -0.013653 0.000068 0.000065 0.0000191 0.0000129 0.000133 0.000048 +2013 7 7 56480 0.150303 0.384908 0.0582998 -0.0003593 -0.082419 -0.013689 0.000068 0.000065 0.0000316 0.0000129 0.000136 0.000049 +2013 7 8 56481 0.151516 0.383569 0.0585873 -0.0002522 -0.082201 -0.013581 0.000068 0.000065 0.0000241 0.0000129 0.000136 0.000050 +2013 7 9 56482 0.152960 0.382258 0.0587691 -0.0001149 -0.082046 -0.013370 0.000068 0.000065 0.0000092 0.0000130 0.000138 0.000050 +2013 7 10 56483 0.154497 0.381112 0.0588261 0.0000077 -0.081815 -0.012956 0.000068 0.000065 0.0000071 0.0000130 0.000138 0.000051 +2013 7 11 56484 0.156047 0.380758 0.0587462 0.0001672 -0.082117 -0.013023 0.000068 0.000065 0.0000082 0.0000130 0.000138 0.000051 +2013 7 12 56485 0.156842 0.380698 0.0585307 0.0002560 -0.082549 -0.013291 0.000068 0.000065 0.0000093 0.0000131 0.000141 0.000051 +2013 7 13 56486 0.157999 0.380151 0.0582181 0.0003605 -0.082671 -0.013395 0.000068 0.000065 0.0000124 0.0000131 0.000141 0.000051 +2013 7 14 56487 0.159031 0.379509 0.0578244 0.0004030 -0.082548 -0.013334 0.000068 0.000065 0.0000162 0.0000132 0.000141 0.000051 +2013 7 15 56488 0.159869 0.378293 0.0574029 0.0004258 -0.082501 -0.013199 0.000068 0.000065 0.0000192 0.0000132 0.000141 0.000051 +2013 7 16 56489 0.160867 0.377205 0.0569745 0.0004286 -0.082807 -0.013062 0.000068 0.000065 0.0000189 0.0000133 0.000141 0.000051 +2013 7 17 56490 0.161806 0.376293 0.0565401 0.0003925 -0.083425 -0.012948 0.000068 0.000065 0.0000079 0.0000133 0.000141 0.000051 +2013 7 18 56491 0.162510 0.375481 0.0561690 0.0003277 -0.084012 -0.012835 0.000068 0.000065 0.0000045 0.0000134 0.000141 0.000051 +2013 7 19 56492 0.162870 0.374748 0.0558747 0.0002735 -0.084318 -0.012878 0.000068 0.000065 0.0000030 0.0000134 0.000141 0.000051 +2013 7 20 56493 0.163780 0.373917 0.0556450 0.0002354 -0.084408 -0.013156 0.000068 0.000065 0.0000063 0.0000135 0.000143 0.000051 +2013 7 21 56494 0.164543 0.373202 0.0554100 0.0002105 -0.084396 -0.013429 0.000068 0.000065 0.0000112 0.0000135 0.000143 0.000052 +2013 7 22 56495 0.164730 0.371897 0.0551500 0.0002807 -0.084475 -0.013448 0.000068 0.000065 0.0000100 0.0000136 0.000146 0.000052 +2013 7 23 56496 0.165231 0.370380 0.0547788 0.0004278 -0.084687 -0.013250 0.000068 0.000065 0.0000066 0.0000136 0.000146 0.000053 +2013 7 24 56497 0.165974 0.369025 0.0542736 0.0005583 -0.084802 -0.013095 0.000068 0.000065 0.0000192 0.0000137 0.000148 0.000054 +2013 7 25 56498 0.166323 0.367973 0.0536557 0.0006296 -0.084722 -0.013109 0.000069 0.000065 0.0000114 0.0000137 0.000148 0.000054 +2013 7 26 56499 0.166728 0.366709 0.0530040 0.0006564 -0.084557 -0.013128 0.000069 0.000065 0.0000052 0.0000138 0.000148 0.000054 +2013 7 27 56500 0.167633 0.365562 0.0523629 0.0006092 -0.084884 -0.013106 0.000069 0.000065 0.0000080 0.0000138 0.000151 0.000054 +2013 7 28 56501 0.168716 0.364800 0.0518137 0.0004846 -0.085392 -0.013064 0.000069 0.000065 0.0000132 0.0000139 0.000151 0.000054 +2013 7 29 56502 0.169699 0.364067 0.0513906 0.0003568 -0.085576 -0.013096 0.000069 0.000065 0.0000181 0.0000139 0.000151 0.000055 +2013 7 30 56503 0.170573 0.363004 0.0511034 0.0002257 -0.085394 -0.013212 0.000069 0.000065 0.0000230 0.0000140 0.000151 0.000055 +2013 7 31 56504 0.171200 0.361756 0.0509457 0.0001170 -0.085203 -0.013395 0.000069 0.000065 0.0000376 0.0000140 0.000151 0.000055 +2013 8 1 56505 0.171584 0.360841 0.0508848 0.0000434 -0.085660 -0.013389 0.000069 0.000065 0.0000444 0.0000141 0.000151 0.000054 +2013 8 2 56506 0.171430 0.359964 0.0508783 -0.0000191 -0.086392 -0.013308 0.000069 0.000065 0.0000120 0.0000141 0.000151 0.000053 +2013 8 3 56507 0.171752 0.358845 0.0508340 -0.0000291 -0.086633 -0.013316 0.000069 0.000065 0.0000187 0.0000142 0.000151 0.000052 +2013 8 4 56508 0.172180 0.357545 0.0507491 -0.0000111 -0.086495 -0.013329 0.000069 0.000065 0.0000399 0.0000142 0.000151 0.000052 +2013 8 5 56509 0.172376 0.356300 0.0507629 0.0000697 -0.086226 -0.013255 0.000069 0.000065 0.0000301 0.0000142 0.000148 0.000052 +2013 8 6 56510 0.172494 0.355281 0.0507483 0.0001832 -0.085979 -0.013115 0.000069 0.000065 0.0000088 0.0000143 0.000148 0.000052 +2013 8 7 56511 0.172699 0.354291 0.0505603 0.0002741 -0.086312 -0.012968 0.000069 0.000065 0.0000043 0.0000143 0.000148 0.000052 +2013 8 8 56512 0.173404 0.353367 0.0502464 0.0003302 -0.086775 -0.012950 0.000069 0.000065 0.0000043 0.0000144 0.000148 0.000052 +2013 8 9 56513 0.173693 0.352980 0.0498354 0.0004365 -0.086878 -0.013070 0.000069 0.000065 0.0000043 0.0000144 0.000148 0.000051 +2013 8 10 56514 0.173874 0.352127 0.0493279 0.0005279 -0.087069 -0.013175 0.000069 0.000066 0.0000117 0.0000144 0.000148 0.000051 +2013 8 11 56515 0.173973 0.351349 0.0487762 0.0005378 -0.087153 -0.013183 0.000069 0.000066 0.0000217 0.0000145 0.000148 0.000050 +2013 8 12 56516 0.173727 0.350269 0.0482574 0.0004871 -0.087098 -0.013105 0.000069 0.000066 0.0000186 0.0000145 0.000148 0.000050 +2013 8 13 56517 0.173596 0.348967 0.0477935 0.0004243 -0.087074 -0.013005 0.000069 0.000066 0.0000107 0.0000145 0.000148 0.000050 +2013 8 14 56518 0.173664 0.347824 0.0473992 0.0003647 -0.087068 -0.012851 0.000069 0.000066 0.0000065 0.0000145 0.000146 0.000049 +2013 8 15 56519 0.173730 0.346434 0.0470576 0.0003132 -0.087310 -0.012889 0.000069 0.000066 0.0000108 0.0000146 0.000146 0.000049 +2013 8 16 56520 0.173632 0.345207 0.0467511 0.0002967 -0.087558 -0.013138 0.000069 0.000066 0.0000180 0.0000146 0.000146 0.000049 +2013 8 17 56521 0.173307 0.343951 0.0464336 0.0003818 -0.087645 -0.013503 0.000069 0.000066 0.0000289 0.0000146 0.000146 0.000049 +2013 8 18 56522 0.173225 0.342947 0.0460411 0.0005532 -0.087655 -0.013780 0.000069 0.000066 0.0000535 0.0000146 0.000146 0.000049 +2013 8 19 56523 0.173145 0.341957 0.0454580 0.0006305 -0.087741 -0.013773 0.000069 0.000066 0.0000401 0.0000146 0.000146 0.000049 +2013 8 20 56524 0.173084 0.340806 0.0446897 0.0007825 -0.087917 -0.013517 0.000069 0.000066 0.0000125 0.0000146 0.000146 0.000049 +2013 8 21 56525 0.172378 0.339452 0.0438471 0.0008623 -0.088195 -0.013061 0.000070 0.000066 0.0000073 0.0000146 0.000146 0.000048 +2013 8 22 56526 0.171662 0.337878 0.0429590 0.0008898 -0.088274 -0.012991 0.000070 0.000066 0.0000072 0.0000146 0.000146 0.000049 +2013 8 23 56527 0.170580 0.336508 0.0420853 0.0008374 -0.088050 -0.013085 0.000070 0.000066 0.0000057 0.0000146 0.000146 0.000048 +2013 8 24 56528 0.169716 0.334993 0.0412694 0.0007660 -0.088204 -0.013143 0.000070 0.000066 0.0000108 0.0000146 0.000146 0.000048 +2013 8 25 56529 0.169112 0.333637 0.0405769 0.0006468 -0.088651 -0.013106 0.000070 0.000066 0.0000183 0.0000146 0.000146 0.000048 +2013 8 26 56530 0.168864 0.332253 0.0399921 0.0005439 -0.088997 -0.013027 0.000070 0.000066 0.0000336 0.0000146 0.000143 0.000049 +2013 8 27 56531 0.168452 0.330771 0.0395351 0.0003996 -0.089061 -0.012984 0.000070 0.000066 0.0000516 0.0000146 0.000143 0.000049 +2013 8 28 56532 0.168403 0.329438 0.0391671 0.0002964 -0.088895 -0.013023 0.000070 0.000067 0.0000438 0.0000146 0.000143 0.000048 +2013 8 29 56533 0.168158 0.328306 0.0388828 0.0002559 -0.088764 -0.013097 0.000070 0.000067 0.0000280 0.0000146 0.000141 0.000048 +2013 8 30 56534 0.167666 0.327165 0.0386808 0.0002515 -0.088581 -0.013201 0.000070 0.000067 0.0000075 0.0000146 0.000141 0.000047 +2013 8 31 56535 0.166524 0.325783 0.0384740 0.0002250 -0.088502 -0.013264 0.000070 0.000067 0.0000083 0.0000146 0.000138 0.000046 +2013 9 1 56536 0.165762 0.324121 0.0382110 0.0003020 -0.088428 -0.013282 0.000070 0.000067 0.0000170 0.0000146 0.000138 0.000046 +2013 9 2 56537 0.165522 0.322697 0.0378712 0.0004263 -0.088366 -0.013267 0.000070 0.000067 0.0000240 0.0000146 0.000138 0.000046 +2013 9 3 56538 0.165319 0.321433 0.0374043 0.0005082 -0.088363 -0.013241 0.000070 0.000067 0.0000183 0.0000146 0.000136 0.000046 +2013 9 4 56539 0.164795 0.320516 0.0367986 0.0006425 -0.088286 -0.013077 0.000070 0.000067 0.0000072 0.0000146 0.000136 0.000045 +2013 9 5 56540 0.164319 0.319550 0.0360415 0.0008064 -0.088480 -0.013089 0.000070 0.000067 0.0000040 0.0000146 0.000133 0.000044 +2013 9 6 56541 0.164302 0.318390 0.0351753 0.0009107 -0.088850 -0.013119 0.000070 0.000067 0.0000028 0.0000145 0.000131 0.000043 +2013 9 7 56542 0.163914 0.317379 0.0342211 0.0009740 -0.089101 -0.013013 0.000070 0.000067 0.0000072 0.0000145 0.000131 0.000043 +2013 9 8 56543 0.163487 0.316433 0.0332465 0.0009391 -0.089263 -0.012865 0.000070 0.000067 0.0000136 0.0000145 0.000131 0.000043 +2013 9 9 56544 0.162930 0.315370 0.0323482 0.0008822 -0.089248 -0.012753 0.000070 0.000067 0.0000164 0.0000145 0.000128 0.000043 +2013 9 10 56545 0.162046 0.314234 0.0315186 0.0007755 -0.089023 -0.012689 0.000070 0.000067 0.0000192 0.0000145 0.000128 0.000043 +2013 9 11 56546 0.161079 0.312849 0.0307669 0.0007056 -0.088711 -0.012667 0.000070 0.000067 0.0000199 0.0000145 0.000128 0.000043 +2013 9 12 56547 0.159894 0.311536 0.0301115 0.0006663 -0.088484 -0.012693 0.000070 0.000067 0.0000082 0.0000144 0.000128 0.000043 +2013 9 13 56548 0.158658 0.310005 0.0294810 0.0006449 -0.088746 -0.012663 0.000070 0.000067 0.0000028 0.0000144 0.000128 0.000042 +2013 9 14 56549 0.157686 0.308473 0.0288014 0.0007250 -0.088802 -0.012816 0.000070 0.000067 0.0000083 0.0000144 0.000128 0.000043 +2013 9 15 56550 0.157285 0.306927 0.0280076 0.0008702 -0.088717 -0.012980 0.000070 0.000067 0.0000170 0.0000144 0.000128 0.000043 +2013 9 16 56551 0.157114 0.305952 0.0270598 0.0010222 -0.088646 -0.012945 0.000070 0.000067 0.0000142 0.0000144 0.000128 0.000043 +2013 9 17 56552 0.156544 0.305543 0.0259398 0.0012024 -0.088694 -0.012730 0.000070 0.000067 0.0000090 0.0000143 0.000128 0.000043 +2013 9 18 56553 0.155359 0.305444 0.0246687 0.0013377 -0.089127 -0.012603 0.000070 0.000067 0.0000083 0.0000143 0.000128 0.000043 +2013 9 19 56554 0.153916 0.304869 0.0233110 0.0013853 -0.089011 -0.012418 0.000071 0.000067 0.0000057 0.0000143 0.000126 0.000042 +2013 9 20 56555 0.152835 0.304021 0.0219582 0.0013010 -0.088748 -0.012381 0.000071 0.000068 0.0000026 0.0000142 0.000126 0.000042 +2013 9 21 56556 0.151468 0.303256 0.0207271 0.0011423 -0.088727 -0.012513 0.000071 0.000068 0.0000082 0.0000142 0.000126 0.000042 +2013 9 22 56557 0.149277 0.302433 0.0196382 0.0010214 -0.088911 -0.012610 0.000071 0.000068 0.0000170 0.0000141 0.000126 0.000042 +2013 9 23 56558 0.146634 0.301494 0.0186897 0.0008944 -0.089140 -0.012594 0.000071 0.000068 0.0000171 0.0000141 0.000126 0.000042 +2013 9 24 56559 0.144771 0.300370 0.0178128 0.0008450 -0.089259 -0.012536 0.000071 0.000068 0.0000124 0.0000140 0.000126 0.000042 +2013 9 25 56560 0.143504 0.299644 0.0169732 0.0008191 -0.089181 -0.012543 0.000071 0.000068 0.0000056 0.0000140 0.000126 0.000042 +2013 9 26 56561 0.142035 0.298888 0.0161882 0.0007756 -0.088675 -0.012539 0.000071 0.000068 0.0000099 0.0000140 0.000126 0.000043 +2013 9 27 56562 0.140873 0.297982 0.0154469 0.0007198 -0.088056 -0.012668 0.000071 0.000068 0.0000050 0.0000141 0.000126 0.000043 +2013 9 28 56563 0.139511 0.297333 0.0147489 0.0007003 -0.087690 -0.012753 0.000071 0.000068 0.0000105 0.0000141 0.000126 0.000043 +2013 9 29 56564 0.137665 0.296309 0.0140103 0.0007465 -0.087520 -0.012751 0.000071 0.000067 0.0000214 0.0000142 0.000126 0.000044 +2013 9 30 56565 0.135471 0.295206 0.0131784 0.0008861 -0.087510 -0.012718 0.000070 0.000067 0.0000173 0.0000142 0.000126 0.000044 +2013 10 1 56566 0.132996 0.294189 0.0122339 0.0009851 -0.087613 -0.012709 0.000070 0.000067 0.0000076 0.0000143 0.000126 0.000045 +2013 10 2 56567 0.130959 0.292934 0.0111900 0.0011245 -0.088121 -0.012695 0.000070 0.000067 0.0000063 0.0000143 0.000128 0.000046 +2013 10 3 56568 0.129438 0.292066 0.0100165 0.0011812 -0.088181 -0.012649 0.000070 0.000067 0.0000089 0.0000144 0.000128 0.000046 +2013 10 4 56569 0.128473 0.291082 0.0087955 0.0012572 -0.087904 -0.012574 0.000070 0.000067 0.0000054 0.0000144 0.000128 0.000046 +2013 10 5 56570 0.127962 0.290177 0.0075035 0.0012910 -0.087977 -0.012304 0.000070 0.000067 0.0000056 0.0000144 0.000128 0.000046 +2013 10 6 56571 0.126768 0.289609 0.0062059 0.0012733 -0.088211 -0.012036 0.000070 0.000067 0.0000078 0.0000144 0.000128 0.000046 +2013 10 7 56572 0.124795 0.289183 0.0049334 0.0012192 -0.088367 -0.011946 0.000070 0.000067 0.0000069 0.0000144 0.000128 0.000046 +2013 10 8 56573 0.123114 0.288197 0.0037325 0.0011707 -0.088361 -0.012020 0.000070 0.000067 0.0000048 0.0000144 0.000128 0.000047 +2013 10 9 56574 0.121820 0.287321 0.0025652 0.0011339 -0.087324 -0.012235 0.000070 0.000067 0.0000140 0.0000144 0.000128 0.000047 +2013 10 10 56575 0.120522 0.286790 0.0014458 0.0011061 -0.087070 -0.012258 0.000070 0.000067 0.0000206 0.0000144 0.000126 0.000047 +2013 10 11 56576 0.118673 0.286134 0.0003789 0.0011101 -0.087221 -0.012176 0.000069 0.000066 0.0000066 0.0000144 0.000126 0.000047 +2013 10 12 56577 0.116765 0.285305 -0.0007375 0.0011577 -0.087226 -0.012153 0.000069 0.000066 0.0000062 0.0000144 0.000126 0.000046 +2013 10 13 56578 0.115030 0.284844 -0.0019565 0.0012431 -0.087174 -0.012167 0.000069 0.000066 0.0000108 0.0000144 0.000126 0.000046 +2013 10 14 56579 0.113802 0.284597 -0.0032686 0.0013816 -0.087076 -0.012130 0.000069 0.000066 0.0000126 0.0000143 0.000123 0.000046 +2013 10 15 56580 0.112811 0.284080 -0.0047081 0.0014959 -0.086958 -0.011950 0.000069 0.000066 0.0000148 0.0000143 0.000123 0.000046 +2013 10 16 56581 0.111762 0.283825 -0.0062269 0.0015403 -0.086974 -0.011588 0.000069 0.000066 0.0000161 0.0000142 0.000123 0.000046 +2013 10 17 56582 0.110237 0.283660 -0.0077680 0.0015102 -0.086996 -0.011466 0.000069 0.000066 0.0000113 0.0000142 0.000121 0.000046 +2013 10 18 56583 0.108365 0.283441 -0.0092482 0.0014385 -0.087031 -0.011533 0.000069 0.000066 0.0000048 0.0000141 0.000121 0.000045 +2013 10 19 56584 0.105949 0.283204 -0.0105862 0.0012853 -0.086933 -0.011593 0.000069 0.000066 0.0000052 0.0000141 0.000121 0.000045 +2013 10 20 56585 0.103699 0.282693 -0.0117699 0.0011053 -0.086764 -0.011605 0.000069 0.000066 0.0000081 0.0000140 0.000121 0.000046 +2013 10 21 56586 0.101715 0.282555 -0.0127666 0.0009064 -0.086603 -0.011524 0.000069 0.000066 0.0000076 0.0000140 0.000121 0.000046 +2013 10 22 56587 0.099901 0.282351 -0.0136356 0.0007936 -0.086437 -0.011370 0.000069 0.000066 0.0000058 0.0000139 0.000121 0.000046 +2013 10 23 56588 0.098499 0.282289 -0.0144408 0.0007584 -0.086432 -0.011340 0.000069 0.000066 0.0000054 0.0000139 0.000118 0.000046 +2013 10 24 56589 0.096899 0.282593 -0.0152294 0.0008042 -0.086207 -0.011437 0.000069 0.000066 0.0000054 0.0000138 0.000118 0.000046 +2013 10 25 56590 0.094999 0.282712 -0.0160484 0.0009433 -0.085552 -0.011597 0.000069 0.000066 0.0000054 0.0000138 0.000118 0.000046 +2013 10 26 56591 0.093349 0.282561 -0.0169963 0.0010364 -0.085079 -0.011702 0.000069 0.000066 0.0000089 0.0000137 0.000118 0.000045 +2013 10 27 56592 0.092330 0.282369 -0.0180690 0.0011019 -0.084850 -0.011695 0.000069 0.000066 0.0000137 0.0000137 0.000116 0.000045 +2013 10 28 56593 0.091798 0.282949 -0.0192767 0.0012716 -0.084794 -0.011617 0.000069 0.000067 0.0000103 0.0000137 0.000116 0.000045 +2013 10 29 56594 0.091203 0.283739 -0.0206140 0.0014087 -0.084818 -0.011561 0.000069 0.000067 0.0000039 0.0000136 0.000116 0.000045 +2013 10 30 56595 0.090463 0.284185 -0.0220581 0.0015023 -0.084963 -0.011643 0.000069 0.000067 0.0000036 0.0000136 0.000116 0.000045 +2013 10 31 56596 0.089562 0.284274 -0.0235858 0.0015320 -0.085169 -0.011712 0.000070 0.000067 0.0000049 0.0000136 0.000113 0.000045 +2013 11 1 56597 0.088054 0.284270 -0.0250817 0.0014698 -0.085198 -0.011482 0.000070 0.000067 0.0000104 0.0000135 0.000113 0.000044 +2013 11 2 56598 0.086765 0.284387 -0.0265327 0.0013811 -0.085063 -0.011038 0.000070 0.000067 0.0000169 0.0000135 0.000113 0.000044 +2013 11 3 56599 0.085485 0.284789 -0.0278886 0.0013043 -0.084799 -0.010621 0.000070 0.000067 0.0000214 0.0000135 0.000113 0.000044 +2013 11 4 56600 0.084398 0.284933 -0.0292512 0.0013043 -0.084484 -0.010437 0.000070 0.000067 0.0000176 0.0000134 0.000113 0.000044 +2013 11 5 56601 0.083118 0.285518 -0.0305387 0.0012651 -0.084270 -0.010550 0.000070 0.000067 0.0000107 0.0000134 0.000111 0.000044 +2013 11 6 56602 0.081773 0.285857 -0.0317349 0.0012149 -0.084400 -0.010934 0.000070 0.000067 0.0000038 0.0000134 0.000111 0.000043 +2013 11 7 56603 0.081100 0.286200 -0.0328871 0.0011385 -0.084378 -0.011060 0.000070 0.000068 0.0000017 0.0000134 0.000111 0.000043 +2013 11 8 56604 0.079915 0.286770 -0.0340353 0.0011564 -0.084440 -0.010940 0.000070 0.000068 0.0000026 0.0000133 0.000111 0.000043 +2013 11 9 56605 0.077910 0.286728 -0.0352053 0.0012022 -0.084477 -0.010801 0.000070 0.000068 0.0000049 0.0000133 0.000111 0.000043 +2013 11 10 56606 0.076178 0.286187 -0.0364604 0.0013002 -0.084437 -0.010719 0.000070 0.000068 0.0000075 0.0000133 0.000111 0.000044 +2013 11 11 56607 0.074778 0.285825 -0.0378139 0.0013773 -0.084334 -0.010655 0.000070 0.000068 0.0000172 0.0000133 0.000113 0.000044 +2013 11 12 56608 0.073613 0.285258 -0.0392264 0.0014437 -0.084219 -0.010574 0.000070 0.000068 0.0000295 0.0000133 0.000113 0.000044 +2013 11 13 56609 0.073066 0.284979 -0.0407182 0.0015010 -0.084203 -0.010460 0.000070 0.000068 0.0000083 0.0000132 0.000113 0.000045 +2013 11 14 56610 0.072519 0.284963 -0.0421982 0.0014418 -0.084374 -0.010366 0.000070 0.000068 0.0000347 0.0000132 0.000116 0.000045 +2013 11 15 56611 0.071889 0.285170 -0.0435577 0.0012787 -0.084640 -0.010314 0.000070 0.000068 0.0000131 0.0000132 0.000116 0.000045 +2013 11 16 56612 0.070307 0.285503 -0.0447834 0.0011450 -0.084530 -0.010292 0.000070 0.000068 0.0000067 0.0000132 0.000116 0.000045 +2013 11 17 56613 0.068331 0.285991 -0.0458832 0.0010649 -0.084147 -0.010261 0.000070 0.000068 0.0000128 0.0000132 0.000116 0.000046 +2013 11 18 56614 0.066609 0.286858 -0.0468825 0.0009562 -0.083680 -0.010197 0.000070 0.000068 0.0000102 0.0000132 0.000118 0.000046 +2013 11 19 56615 0.065136 0.287651 -0.0478077 0.0009092 -0.083229 -0.010100 0.000070 0.000068 0.0000045 0.0000132 0.000118 0.000046 +2013 11 20 56616 0.063662 0.288233 -0.0486815 0.0008690 -0.083316 -0.010086 0.000070 0.000068 0.0000070 0.0000132 0.000121 0.000047 +2013 11 21 56617 0.062281 0.288815 -0.0495622 0.0008779 -0.083180 -0.010120 0.000070 0.000068 0.0000152 0.0000132 0.000121 0.000048 +2013 11 22 56618 0.061361 0.289229 -0.0504540 0.0009145 -0.082762 -0.010226 0.000070 0.000068 0.0000085 0.0000132 0.000121 0.000048 +2013 11 23 56619 0.060916 0.289535 -0.0514536 0.0010387 -0.082409 -0.010314 0.000070 0.000068 0.0000104 0.0000132 0.000121 0.000048 +2013 11 24 56620 0.060132 0.290238 -0.0525633 0.0011639 -0.082247 -0.010313 0.000070 0.000068 0.0000176 0.0000131 0.000123 0.000048 +2013 11 25 56621 0.059463 0.290747 -0.0537420 0.0012337 -0.082145 -0.010207 0.000070 0.000068 0.0000138 0.0000131 0.000123 0.000049 +2013 11 26 56622 0.059054 0.291623 -0.0550095 0.0013016 -0.081994 -0.010073 0.000070 0.000068 0.0000084 0.0000131 0.000123 0.000049 +2013 11 27 56623 0.058488 0.292239 -0.0563689 0.0013907 -0.082081 -0.009818 0.000070 0.000068 0.0000112 0.0000131 0.000123 0.000049 +2013 11 28 56624 0.057838 0.292856 -0.0577935 0.0014582 -0.082346 -0.009743 0.000071 0.000068 0.0000121 0.0000131 0.000123 0.000049 +2013 11 29 56625 0.057425 0.293396 -0.0592413 0.0014041 -0.082560 -0.009744 0.000071 0.000068 0.0000284 0.0000131 0.000123 0.000049 +2013 11 30 56626 0.057543 0.293803 -0.0606331 0.0013588 -0.082557 -0.009621 0.000071 0.000068 0.0000503 0.0000130 0.000123 0.000049 +2013 12 1 56627 0.057914 0.294200 -0.0619990 0.0013435 -0.082253 -0.009472 0.000071 0.000068 0.0000479 0.0000130 0.000123 0.000049 +2013 12 2 56628 0.057839 0.294924 -0.0632772 0.0012458 -0.081883 -0.009495 0.000071 0.000068 0.0000424 0.0000130 0.000123 0.000048 +2013 12 3 56629 0.057194 0.295668 -0.0644756 0.0011527 -0.081789 -0.009683 0.000071 0.000068 0.0000104 0.0000129 0.000121 0.000047 +2013 12 4 56630 0.056918 0.296444 -0.0656209 0.0011485 -0.081891 -0.009789 0.000071 0.000068 0.0000123 0.0000129 0.000121 0.000047 +2013 12 5 56631 0.057140 0.297418 -0.0667769 0.0011580 -0.081902 -0.009859 0.000071 0.000069 0.0000252 0.0000129 0.000118 0.000046 +2013 12 6 56632 0.057071 0.298691 -0.0679634 0.0012450 -0.081909 -0.009803 0.000072 0.000069 0.0000088 0.0000129 0.000118 0.000046 +2013 12 7 56633 0.056992 0.300509 -0.0692558 0.0013655 -0.081950 -0.009577 0.000072 0.000069 0.0000129 0.0000128 0.000118 0.000045 +2013 12 8 56634 0.056938 0.301651 -0.0706430 0.0014145 -0.081941 -0.009454 0.000072 0.000069 0.0000242 0.0000128 0.000116 0.000045 +2013 12 9 56635 0.057219 0.302570 -0.0720747 0.0014267 -0.081993 -0.009422 0.000072 0.000069 0.0000253 0.0000128 0.000116 0.000044 +2013 12 10 56636 0.057085 0.303912 -0.0734639 0.0013498 -0.082147 -0.009381 0.000072 0.000069 0.0000078 0.0000127 0.000113 0.000043 +2013 12 11 56637 0.056894 0.305244 -0.0747629 0.0012567 -0.082263 -0.009375 0.000072 0.000069 0.0000229 0.0000127 0.000113 0.000042 +2013 12 12 56638 0.056884 0.306338 -0.0759614 0.0011469 -0.082376 -0.009463 0.000072 0.000070 0.0000084 0.0000127 0.000111 0.000041 +2013 12 13 56639 0.056551 0.307369 -0.0770606 0.0010580 -0.082278 -0.009496 0.000072 0.000070 0.0000022 0.0000126 0.000111 0.000041 +2013 12 14 56640 0.055765 0.308392 -0.0780468 0.0009192 -0.082174 -0.009470 0.000072 0.000070 0.0000180 0.0000126 0.000108 0.000040 +2013 12 15 56641 0.054607 0.308780 -0.0788703 0.0007803 -0.082023 -0.009378 0.000072 0.000070 0.0000236 0.0000126 0.000108 0.000040 +2013 12 16 56642 0.053207 0.308627 -0.0796080 0.0006877 -0.081824 -0.009265 0.000073 0.000070 0.0000169 0.0000126 0.000108 0.000039 +2013 12 17 56643 0.051747 0.308124 -0.0802390 0.0006188 -0.081998 -0.009194 0.000073 0.000070 0.0000105 0.0000126 0.000106 0.000039 +2013 12 18 56644 0.049948 0.308089 -0.0808494 0.0006070 -0.081891 -0.009193 0.000073 0.000070 0.0000046 0.0000126 0.000106 0.000038 +2013 12 19 56645 0.048273 0.308191 -0.0814842 0.0006785 -0.081553 -0.009199 0.000073 0.000070 0.0000017 0.0000126 0.000103 0.000038 +2013 12 20 56646 0.047064 0.308707 -0.0821956 0.0007629 -0.081179 -0.009200 0.000073 0.000070 0.0000016 0.0000126 0.000103 0.000037 +2013 12 21 56647 0.046035 0.309134 -0.0830511 0.0009297 -0.081022 -0.009303 0.000073 0.000070 0.0000046 0.0000125 0.000103 0.000037 +2013 12 22 56648 0.045171 0.309786 -0.0840450 0.0010592 -0.081018 -0.009429 0.000073 0.000071 0.0000087 0.0000125 0.000101 0.000037 +2013 12 23 56649 0.044273 0.310487 -0.0851666 0.0011598 -0.080979 -0.009461 0.000073 0.000071 0.0000098 0.0000125 0.000101 0.000036 +2013 12 24 56650 0.043536 0.311211 -0.0864052 0.0012651 -0.080817 -0.009389 0.000073 0.000071 0.0000100 0.0000125 0.000101 0.000036 +2013 12 25 56651 0.042681 0.312389 -0.0877381 0.0013238 -0.080690 -0.009291 0.000073 0.000071 0.0000077 0.0000125 0.000101 0.000036 +2013 12 26 56652 0.041817 0.313465 -0.0891304 0.0013841 -0.080838 -0.009281 0.000073 0.000071 0.0000048 0.0000125 0.000101 0.000036 +2013 12 27 56653 0.041024 0.314925 -0.0905397 0.0014322 -0.081235 -0.009303 0.000073 0.000071 0.0000019 0.0000125 0.000101 0.000036 +2013 12 28 56654 0.040472 0.316045 -0.0919746 0.0014002 -0.081562 -0.009203 0.000073 0.000071 0.0000072 0.0000125 0.000101 0.000036 +2013 12 29 56655 0.040086 0.316906 -0.0933653 0.0013948 -0.081619 -0.009028 0.000073 0.000071 0.0000156 0.0000125 0.000101 0.000036 +2013 12 30 56656 0.039580 0.317719 -0.0946525 0.0012934 -0.081484 -0.009003 0.000073 0.000071 0.0000116 0.0000125 0.000101 0.000036 +2013 12 31 56657 0.038942 0.318303 -0.0958694 0.0012211 -0.081327 -0.009224 0.000073 0.000071 0.0000031 0.0000125 0.000101 0.000036 diff --git a/src/test/resources/eopc04/eopc04.14 b/src/test/resources/eopc04/eopc04.14 new file mode 100644 index 0000000000..c77a71d921 --- /dev/null +++ b/src/test/resources/eopc04/eopc04.14 @@ -0,0 +1,371 @@ +# EARTH ORIENTATION PARAMETER (EOP) PRODUCT CENTER CENTER (PARIS OBSERVATORY) - INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE +# EOP (IERS) 20 C04 TIME SERIES consistent with ITRF 2020 - sampled at 0h UTC +# Contact: christian.bizouard@obspm.fr +# Reference Precession-Nutation Model: IAU 2000 +# format(4(i4),f10.2,2(f12.6),f12.7,2(f12.6),2(f12.6),f12.7,2(f12.6),f12.7,2(f12.6),2(f12.6),f12.7) +# YR MM DD HH MJD x(") y(") UT1-UTC(s) dX(") dY(") xrt(") yrt(") LOD(s) x Er y Er UT1-UTC Er dX Er dY Er xrt Er yrt Er LOD Er +2014 1 1 0 56658.00 0.038614 0.318914 -0.0970662 0.000062 0.000116 -0.000184 0.000610 0.0011775 0.000072 0.000053 0.0000163 0.000109 0.000110 0.000087 0.000124 0.0000529 +2014 1 2 0 56659.00 0.038361 0.319638 -0.0982610 0.000043 0.000155 -0.000313 0.000838 0.0012187 0.000072 0.000053 0.0000168 0.000156 0.000151 0.000088 0.000125 0.0000526 +2014 1 3 0 56660.00 0.037786 0.320372 -0.0995164 0.000026 0.000193 -0.000640 0.000628 0.0012910 0.000073 0.000054 0.0000181 0.000187 0.000179 0.000088 0.000125 0.0000533 +2014 1 4 0 56661.00 0.037126 0.320830 -0.1008464 0.000029 0.000213 -0.000496 0.000481 0.0013672 0.000073 0.000054 0.0000208 0.000184 0.000176 0.000088 0.000125 0.0000525 +2014 1 5 0 56662.00 0.036653 0.321267 -0.1022390 0.000043 0.000222 -0.000385 0.000543 0.0014142 0.000072 0.000053 0.0000236 0.000149 0.000144 0.000088 0.000125 0.0000520 +2014 1 6 0 56663.00 0.036090 0.321934 -0.1036473 0.000060 0.000226 -0.000650 0.000747 0.0013911 0.000073 0.000054 0.0000193 0.000099 0.000099 0.000088 0.000124 0.0000506 +2014 1 7 0 56664.00 0.035229 0.322864 -0.1049914 0.000076 0.000230 -0.000921 0.001066 0.0012802 0.000072 0.000053 0.0000141 0.000082 0.000083 0.000087 0.000122 0.0000490 +2014 1 8 0 56665.00 0.034347 0.323939 -0.1061938 0.000079 0.000243 -0.000729 0.001120 0.0011181 0.000071 0.000053 0.0000124 0.000081 0.000082 0.000087 0.000121 0.0000486 +2014 1 9 0 56666.00 0.033902 0.325031 -0.1072271 0.000075 0.000258 -0.000193 0.001334 0.0009514 0.000071 0.000053 0.0000121 0.000080 0.000081 0.000087 0.000121 0.0000486 +2014 1 10 0 56667.00 0.033520 0.326443 -0.1081017 0.000068 0.000274 -0.000435 0.001473 0.0008041 0.000071 0.000053 0.0000126 0.000080 0.000080 0.000086 0.000122 0.0000489 +2014 1 11 0 56668.00 0.032766 0.327836 -0.1088416 0.000055 0.000292 -0.000911 0.001329 0.0006787 0.000072 0.000053 0.0000158 0.000080 0.000080 0.000086 0.000123 0.0000491 +2014 1 12 0 56669.00 0.031605 0.328987 -0.1094720 0.000040 0.000308 -0.001184 0.001087 0.0005892 0.000072 0.000053 0.0000208 0.000082 0.000081 0.000086 0.000124 0.0000497 +2014 1 13 0 56670.00 0.030322 0.329962 -0.1100363 0.000031 0.000314 -0.001137 0.000834 0.0005479 0.000072 0.000052 0.0000188 0.000084 0.000083 0.000086 0.000124 0.0000507 +2014 1 14 0 56671.00 0.029399 0.330759 -0.1105856 0.000032 0.000305 -0.000744 0.000883 0.0005613 0.000071 0.000052 0.0000142 0.000084 0.000084 0.000085 0.000123 0.0000511 +2014 1 15 0 56672.00 0.028506 0.331735 -0.1111732 0.000064 0.000260 -0.001026 0.001016 0.0006206 0.000071 0.000052 0.0000130 0.000084 0.000084 0.000085 0.000123 0.0000500 +2014 1 16 0 56673.00 0.027443 0.332700 -0.1118382 0.000108 0.000200 -0.001024 0.001053 0.0007137 0.000072 0.000052 0.0000134 0.000083 0.000084 0.000086 0.000123 0.0000494 +2014 1 17 0 56674.00 0.026582 0.333777 -0.1126094 0.000152 0.000138 -0.000650 0.001361 0.0008305 0.000072 0.000053 0.0000146 0.000082 0.000083 0.000086 0.000124 0.0000500 +2014 1 18 0 56675.00 0.025817 0.335112 -0.1135067 0.000152 0.000102 -0.000788 0.001467 0.0009690 0.000073 0.000053 0.0000189 0.000082 0.000083 0.000086 0.000126 0.0000506 +2014 1 19 0 56676.00 0.024785 0.336378 -0.1145461 0.000128 0.000087 -0.001086 0.001265 0.0011134 0.000073 0.000053 0.0000263 0.000082 0.000083 0.000086 0.000126 0.0000506 +2014 1 20 0 56677.00 0.023858 0.337427 -0.1157173 0.000102 0.000084 -0.000638 0.001082 0.0012170 0.000072 0.000052 0.0000307 0.000081 0.000081 0.000086 0.000125 0.0000508 +2014 1 21 0 56678.00 0.023703 0.338255 -0.1169657 0.000085 0.000090 0.000265 0.000886 0.0012695 0.000072 0.000052 0.0000243 0.000080 0.000080 0.000085 0.000125 0.0000512 +2014 1 22 0 56679.00 0.024048 0.339054 -0.1182436 0.000088 0.000104 0.000422 0.001003 0.0012804 0.000072 0.000052 0.0000171 0.000080 0.000080 0.000086 0.000125 0.0000515 +2014 1 23 0 56680.00 0.024334 0.339950 -0.1195115 0.000144 0.000126 0.000219 0.001039 0.0012476 0.000073 0.000052 0.0000149 0.000079 0.000079 0.000086 0.000126 0.0000513 +2014 1 24 0 56681.00 0.024456 0.340753 -0.1207262 0.000210 0.000148 0.000070 0.000763 0.0011736 0.000073 0.000052 0.0000155 0.000078 0.000078 0.000086 0.000127 0.0000515 +2014 1 25 0 56682.00 0.024330 0.341375 -0.1218559 0.000239 0.000144 -0.000111 0.000611 0.0010861 0.000073 0.000053 0.0000208 0.000078 0.000078 0.000086 0.000127 0.0000516 +2014 1 26 0 56683.00 0.024140 0.341872 -0.1229027 0.000231 0.000121 -0.000058 0.000538 0.0010202 0.000073 0.000052 0.0000310 0.000080 0.000080 0.000086 0.000127 0.0000523 +2014 1 27 0 56684.00 0.024194 0.342639 -0.1238939 0.000205 0.000090 0.000140 0.001055 0.0009527 0.000072 0.000052 0.0000342 0.000086 0.000086 0.000086 0.000126 0.0000522 +2014 1 28 0 56685.00 0.024370 0.343960 -0.1248363 0.000168 0.000060 0.000159 0.001540 0.0009421 0.000072 0.000052 0.0000250 0.000090 0.000089 0.000085 0.000125 0.0000511 +2014 1 29 0 56686.00 0.024487 0.345535 -0.1258215 0.000127 0.000039 0.000076 0.001583 0.0010686 0.000072 0.000052 0.0000167 0.000090 0.000089 0.000085 0.000124 0.0000510 +2014 1 30 0 56687.00 0.024494 0.346925 -0.1269732 0.000074 0.000033 -0.000061 0.001298 0.0012308 0.000072 0.000052 0.0000137 0.000085 0.000084 0.000085 0.000124 0.0000522 +2014 1 31 0 56688.00 0.024217 0.347974 -0.1282708 0.000198 0.000058 -0.000388 0.000874 0.0013440 0.000073 0.000053 0.0000140 0.000086 0.000086 0.000086 0.000125 0.0000542 +2014 2 1 0 56689.00 0.023772 0.348812 -0.1296507 0.000286 0.000085 -0.000343 0.000783 0.0014016 0.000073 0.000053 0.0000182 0.000087 0.000086 0.000087 0.000126 0.0000554 +2014 2 2 0 56690.00 0.023546 0.349500 -0.1310603 0.000236 0.000099 -0.000048 0.000630 0.0014142 0.000073 0.000053 0.0000256 0.000085 0.000084 0.000087 0.000125 0.0000541 +2014 2 3 0 56691.00 0.023593 0.350135 -0.1324509 0.000130 0.000108 0.000093 0.000701 0.0013582 0.000072 0.000053 0.0000228 0.000081 0.000080 0.000087 0.000124 0.0000530 +2014 2 4 0 56692.00 0.023711 0.351059 -0.1337460 0.000017 0.000115 0.000149 0.001142 0.0012167 0.000072 0.000053 0.0000158 0.000079 0.000078 0.000086 0.000123 0.0000529 +2014 2 5 0 56693.00 0.023914 0.352336 -0.1348702 0.000050 0.000125 0.000414 0.001426 0.0010238 0.000071 0.000052 0.0000130 0.000080 0.000079 0.000086 0.000122 0.0000521 +2014 2 6 0 56694.00 0.024300 0.353786 -0.1357987 0.000135 0.000137 0.000409 0.001532 0.0008369 0.000071 0.000052 0.0000112 0.000082 0.000082 0.000086 0.000122 0.0000518 +2014 2 7 0 56695.00 0.024498 0.355301 -0.1365600 0.000219 0.000152 0.000113 0.001390 0.0006972 0.000072 0.000053 0.0000102 0.000084 0.000084 0.000087 0.000123 0.0000524 +2014 2 8 0 56696.00 0.024458 0.356735 -0.1372096 0.000228 0.000192 0.000019 0.001299 0.0006131 0.000072 0.000053 0.0000099 0.000084 0.000084 0.000087 0.000124 0.0000528 +2014 2 9 0 56697.00 0.024346 0.358208 -0.1378014 0.000194 0.000237 -0.000132 0.001494 0.0005793 0.000072 0.000053 0.0000101 0.000085 0.000084 0.000088 0.000125 0.0000530 +2014 2 10 0 56698.00 0.024177 0.359854 -0.1383833 0.000152 0.000269 -0.000107 0.001792 0.0005929 0.000072 0.000053 0.0000105 0.000085 0.000084 0.000087 0.000124 0.0000522 +2014 2 11 0 56699.00 0.023969 0.361804 -0.1390006 0.000120 0.000273 -0.000265 0.002047 0.0006482 0.000071 0.000053 0.0000106 0.000085 0.000084 0.000087 0.000124 0.0000511 +2014 2 12 0 56700.00 0.023496 0.363745 -0.1396913 0.000111 0.000212 -0.000553 0.001780 0.0007376 0.000071 0.000053 0.0000108 0.000083 0.000083 0.000087 0.000124 0.0000515 +2014 2 13 0 56701.00 0.022771 0.365459 -0.1404853 0.000152 0.000137 -0.000716 0.001610 0.0008539 0.000072 0.000052 0.0000113 0.000081 0.000082 0.000087 0.000124 0.0000521 +2014 2 14 0 56702.00 0.022059 0.367126 -0.1414047 0.000278 0.000080 -0.000679 0.001554 0.0009861 0.000071 0.000052 0.0000132 0.000087 0.000088 0.000086 0.000124 0.0000520 +2014 2 15 0 56703.00 0.021458 0.368716 -0.1424593 0.000290 0.000041 -0.000388 0.001488 0.0011241 0.000071 0.000052 0.0000181 0.000092 0.000092 0.000086 0.000124 0.0000518 +2014 2 16 0 56704.00 0.020986 0.370441 -0.1436475 0.000158 0.000019 -0.000441 0.001659 0.0012492 0.000072 0.000052 0.0000258 0.000105 0.000100 0.000087 0.000124 0.0000521 +2014 2 17 0 56705.00 0.020409 0.372114 -0.1449460 0.000016 0.000009 -0.000706 0.001604 0.0013427 0.000072 0.000053 0.0000238 0.000123 0.000110 0.000087 0.000125 0.0000530 +2014 2 18 0 56706.00 0.019853 0.373480 -0.1463149 -0.000026 0.000011 -0.000262 0.001375 0.0013788 0.000072 0.000053 0.0000173 0.000129 0.000114 0.000087 0.000125 0.0000527 +2014 2 19 0 56707.00 0.019839 0.374834 -0.1476971 0.000182 0.000007 0.000256 0.001367 0.0013964 0.000072 0.000053 0.0000148 0.000115 0.000108 0.000087 0.000124 0.0000520 +2014 2 20 0 56708.00 0.020081 0.376166 -0.1490744 0.000208 0.000195 0.000282 0.001305 0.0013291 0.000071 0.000052 0.0000150 0.000126 0.000124 0.000087 0.000123 0.0000521 +2014 2 21 0 56709.00 0.020293 0.377381 -0.1503533 0.000201 0.000095 0.000252 0.001114 0.0012249 0.000071 0.000052 0.0000165 0.000125 0.000124 0.000086 0.000122 0.0000534 +2014 2 22 0 56710.00 0.020516 0.378597 -0.1515379 0.000224 -0.000062 0.000320 0.001192 0.0011582 0.000071 0.000052 0.0000205 0.000124 0.000123 0.000087 0.000122 0.0000546 +2014 2 23 0 56711.00 0.020645 0.379985 -0.1526818 0.000252 -0.000073 0.000138 0.001282 0.0011400 0.000071 0.000053 0.0000269 0.000133 0.000132 0.000087 0.000122 0.0000539 +2014 2 24 0 56712.00 0.020379 0.381427 -0.1538343 0.000276 -0.000018 -0.000559 0.001294 0.0011756 0.000071 0.000053 0.0000259 0.000145 0.000144 0.000087 0.000122 0.0000526 +2014 2 25 0 56713.00 0.019564 0.382733 -0.1550541 0.000287 0.000062 -0.000920 0.001117 0.0012698 0.000072 0.000053 0.0000216 0.000150 0.000148 0.000087 0.000123 0.0000525 +2014 2 26 0 56714.00 0.018810 0.383609 -0.1564049 0.000286 0.000121 -0.000345 0.000590 0.0014521 0.000072 0.000053 0.0000224 0.000202 0.000197 0.000088 0.000124 0.0000520 +2014 2 27 0 56715.00 0.018797 0.384252 -0.1579680 0.000131 0.000219 0.000345 0.000781 0.0016810 0.000073 0.000053 0.0000264 0.000306 0.000291 0.000088 0.000125 0.0000506 +2014 2 28 0 56716.00 0.019258 0.385284 -0.1597506 0.000175 0.000086 0.000610 0.001255 0.0018672 0.000073 0.000053 0.0000259 0.000232 0.000220 0.000088 0.000125 0.0000479 +2014 3 1 0 56717.00 0.019794 0.386584 -0.1616786 0.000267 -0.000055 0.000592 0.001323 0.0019663 0.000073 0.000053 0.0000265 0.000187 0.000178 0.000088 0.000126 0.0000449 +2014 3 2 0 56718.00 0.020195 0.387934 -0.1636627 0.000271 -0.000060 0.000362 0.001311 0.0019945 0.000074 0.000053 0.0000298 0.000160 0.000153 0.000089 0.000128 0.0000484 +2014 3 3 0 56719.00 0.020410 0.389423 -0.1656371 0.000240 -0.000001 0.000078 0.001432 0.0019461 0.000074 0.000054 0.0000242 0.000109 0.000106 0.000090 0.000130 0.0000520 +2014 3 4 0 56720.00 0.020461 0.390888 -0.1675219 0.000199 0.000081 -0.000046 0.001317 0.0018070 0.000075 0.000054 0.0000171 0.000086 0.000085 0.000090 0.000131 0.0000523 +2014 3 5 0 56721.00 0.020394 0.392037 -0.1692375 0.000220 0.000085 0.000011 0.000857 0.0016175 0.000075 0.000054 0.0000150 0.000084 0.000084 0.000089 0.000131 0.0000517 +2014 3 6 0 56722.00 0.020390 0.393012 -0.1707614 0.000262 0.000062 0.000027 0.000779 0.0014352 0.000075 0.000053 0.0000149 0.000083 0.000083 0.000089 0.000132 0.0000524 +2014 3 7 0 56723.00 0.020308 0.394030 -0.1721199 0.000303 0.000033 -0.000039 0.000908 0.0012929 0.000075 0.000054 0.0000161 0.000082 0.000083 0.000090 0.000133 0.0000542 +2014 3 8 0 56724.00 0.020118 0.395128 -0.1733554 0.000312 -0.000002 -0.000133 0.000970 0.0011845 0.000076 0.000054 0.0000215 0.000083 0.000084 0.000090 0.000134 0.0000542 +2014 3 9 0 56725.00 0.019854 0.396140 -0.1744956 0.000300 -0.000037 -0.000303 0.000818 0.0010932 0.000074 0.000053 0.0000316 0.000086 0.000086 0.000088 0.000133 0.0000533 +2014 3 10 0 56726.00 0.019505 0.397006 -0.1755611 0.000282 -0.000061 -0.000309 0.000857 0.0010505 0.000074 0.000052 0.0000362 0.000093 0.000094 0.000087 0.000132 0.0000542 +2014 3 11 0 56727.00 0.019274 0.398020 -0.1766089 0.000267 -0.000069 0.000013 0.001100 0.0010478 0.000073 0.000052 0.0000290 0.000098 0.000099 0.000086 0.000131 0.0000542 +2014 3 12 0 56728.00 0.019501 0.399147 -0.1776726 0.000260 -0.000054 0.000582 0.001083 0.0010883 0.000073 0.000052 0.0000211 0.000100 0.000100 0.000086 0.000131 0.0000527 +2014 3 13 0 56729.00 0.020300 0.400223 -0.1787933 0.000285 0.000012 0.001095 0.000959 0.0011585 0.000074 0.000052 0.0000179 0.000100 0.000100 0.000086 0.000131 0.0000526 +2014 3 14 0 56730.00 0.021421 0.401282 -0.1799871 0.000309 0.000087 0.001221 0.000980 0.0012275 0.000074 0.000052 0.0000176 0.000100 0.000101 0.000086 0.000132 0.0000530 +2014 3 15 0 56731.00 0.022513 0.402441 -0.1812391 0.000271 0.000131 0.001138 0.001094 0.0012673 0.000074 0.000052 0.0000219 0.000102 0.000103 0.000086 0.000132 0.0000540 +2014 3 16 0 56732.00 0.023584 0.403730 -0.1825145 0.000194 0.000147 0.001065 0.001256 0.0012746 0.000075 0.000053 0.0000303 0.000114 0.000115 0.000087 0.000134 0.0000550 +2014 3 17 0 56733.00 0.024397 0.404926 -0.1837854 0.000121 0.000144 0.000581 0.000918 0.0012630 0.000076 0.000053 0.0000317 0.000132 0.000134 0.000087 0.000134 0.0000539 +2014 3 18 0 56734.00 0.024852 0.405613 -0.1850383 0.000079 0.000125 0.000408 0.000481 0.0012432 0.000076 0.000053 0.0000272 0.000139 0.000140 0.000087 0.000134 0.0000524 +2014 3 19 0 56735.00 0.025335 0.406026 -0.1862654 0.000181 0.000096 0.000687 0.000345 0.0012105 0.000076 0.000053 0.0000255 0.000145 0.000148 0.000087 0.000134 0.0000517 +2014 3 20 0 56736.00 0.026184 0.406481 -0.1874520 0.000335 0.000060 0.001045 0.000432 0.0011606 0.000076 0.000053 0.0000253 0.000162 0.000169 0.000088 0.000135 0.0000519 +2014 3 21 0 56737.00 0.027328 0.407193 -0.1885870 0.000483 0.000023 0.001130 0.000920 0.0011099 0.000076 0.000053 0.0000260 0.000174 0.000183 0.000087 0.000135 0.0000524 +2014 3 22 0 56738.00 0.028320 0.408422 -0.1896852 0.000525 -0.000010 0.000931 0.001262 0.0010959 0.000075 0.000052 0.0000288 0.000169 0.000178 0.000086 0.000133 0.0000527 +2014 3 23 0 56739.00 0.029441 0.409865 -0.1907998 0.000497 -0.000036 0.001305 0.001380 0.0011530 0.000075 0.000052 0.0000312 0.000137 0.000142 0.000086 0.000132 0.0000540 +2014 3 24 0 56740.00 0.030844 0.411299 -0.1920033 0.000442 -0.000049 0.001491 0.001344 0.0012633 0.000074 0.000052 0.0000235 0.000093 0.000095 0.000085 0.000131 0.0000558 +2014 3 25 0 56741.00 0.032259 0.412727 -0.1933343 0.000377 -0.000045 0.001397 0.001401 0.0014045 0.000074 0.000051 0.0000157 0.000079 0.000080 0.000085 0.000130 0.0000564 +2014 3 26 0 56742.00 0.033601 0.414132 -0.1948100 0.000336 0.000003 0.001440 0.001186 0.0015385 0.000074 0.000052 0.0000128 0.000078 0.000079 0.000085 0.000130 0.0000550 +2014 3 27 0 56743.00 0.035040 0.415189 -0.1964090 0.000300 0.000041 0.001626 0.000759 0.0016456 0.000074 0.000052 0.0000119 0.000077 0.000078 0.000085 0.000131 0.0000542 +2014 3 28 0 56744.00 0.036720 0.416002 -0.1981035 0.000257 0.000026 0.001771 0.000737 0.0017430 0.000074 0.000052 0.0000132 0.000078 0.000080 0.000085 0.000131 0.0000553 +2014 3 29 0 56745.00 0.038489 0.416913 -0.1998799 0.000229 0.000023 0.001834 0.000940 0.0018036 0.000074 0.000052 0.0000185 0.000078 0.000081 0.000085 0.000131 0.0000572 +2014 3 30 0 56746.00 0.040398 0.418078 -0.2016801 0.000223 0.000052 0.001959 0.001253 0.0017805 0.000074 0.000052 0.0000283 0.000079 0.000081 0.000085 0.000131 0.0000615 +2014 3 31 0 56747.00 0.042187 0.419472 -0.2034127 0.000229 0.000095 0.001667 0.001399 0.0016656 0.000074 0.000052 0.0000254 0.000081 0.000080 0.000085 0.000130 0.0000654 +2014 4 1 0 56748.00 0.043582 0.420987 -0.2050058 0.000242 0.000136 0.001241 0.001429 0.0015235 0.000074 0.000052 0.0000174 0.000081 0.000080 0.000085 0.000129 0.0000614 +2014 4 2 0 56749.00 0.044677 0.422526 -0.2064623 0.000238 0.000136 0.000941 0.001320 0.0013967 0.000074 0.000052 0.0000148 0.000082 0.000081 0.000085 0.000129 0.0000580 +2014 4 3 0 56750.00 0.045452 0.423884 -0.2078074 0.000234 0.000119 0.000568 0.001013 0.0013031 0.000073 0.000052 0.0000141 0.000084 0.000083 0.000085 0.000128 0.0000583 +2014 4 4 0 56751.00 0.045941 0.424931 -0.2090767 0.000239 0.000096 0.000375 0.000763 0.0012429 0.000072 0.000051 0.0000148 0.000085 0.000085 0.000084 0.000126 0.0000594 +2014 4 5 0 56752.00 0.046329 0.425737 -0.2102994 0.000318 0.000092 0.000403 0.000592 0.0012063 0.000072 0.000051 0.0000193 0.000088 0.000089 0.000083 0.000124 0.0000580 +2014 4 6 0 56753.00 0.046828 0.426259 -0.2114948 0.000419 0.000092 0.000638 0.000178 0.0011872 0.000071 0.000051 0.0000271 0.000104 0.000106 0.000083 0.000123 0.0000529 +2014 4 7 0 56754.00 0.047749 0.426524 -0.2126785 0.000480 0.000080 0.001203 0.000276 0.0011785 0.000071 0.000051 0.0000259 0.000123 0.000125 0.000083 0.000122 0.0000544 +2014 4 8 0 56755.00 0.049133 0.427228 -0.2138627 0.000462 0.000048 0.001582 0.000817 0.0011915 0.000070 0.000051 0.0000215 0.000128 0.000131 0.000083 0.000121 0.0000550 +2014 4 9 0 56756.00 0.050689 0.428249 -0.2150750 0.000311 -0.000019 0.001503 0.000760 0.0012421 0.000070 0.000051 0.0000226 0.000194 0.000197 0.000083 0.000120 0.0000535 +2014 4 10 0 56757.00 0.052114 0.429095 -0.2163492 0.000251 -0.000046 0.001294 0.000687 0.0013087 0.000070 0.000051 0.0000211 0.000179 0.000181 0.000083 0.000118 0.0000531 +2014 4 11 0 56758.00 0.053262 0.429857 -0.2176844 0.000300 -0.000029 0.001110 0.000616 0.0013456 0.000069 0.000050 0.0000185 0.000112 0.000113 0.000082 0.000117 0.0000543 +2014 4 12 0 56759.00 0.054194 0.430514 -0.2190493 0.000331 -0.000016 0.000810 0.000377 0.0013868 0.000068 0.000050 0.0000217 0.000098 0.000100 0.000082 0.000116 0.0000551 +2014 4 13 0 56760.00 0.054958 0.430848 -0.2204643 0.000331 -0.000014 0.000767 0.000157 0.0014601 0.000069 0.000050 0.0000287 0.000092 0.000093 0.000082 0.000117 0.0000544 +2014 4 14 0 56761.00 0.055799 0.431246 -0.2219428 0.000309 -0.000021 0.000942 0.000512 0.0014874 0.000069 0.000051 0.0000246 0.000083 0.000084 0.000083 0.000117 0.0000544 +2014 4 15 0 56762.00 0.056655 0.432047 -0.2234015 0.000258 -0.000039 0.000735 0.000800 0.0013857 0.000070 0.000051 0.0000178 0.000080 0.000081 0.000083 0.000118 0.0000561 +2014 4 16 0 56763.00 0.057350 0.432762 -0.2247280 0.000168 -0.000074 0.000731 0.000536 0.0012814 0.000070 0.000052 0.0000172 0.000082 0.000085 0.000084 0.000119 0.0000609 +2014 4 17 0 56764.00 0.058325 0.433393 -0.2259715 0.000105 -0.000095 0.001218 0.000777 0.0012211 0.000071 0.000052 0.0000230 0.000083 0.000087 0.000085 0.000121 0.0000703 +2014 4 18 0 56765.00 0.059504 0.434504 -0.2271675 0.000094 -0.000092 0.001048 0.001285 0.0011631 0.000072 0.000053 0.0000361 0.000084 0.000087 0.000086 0.000123 0.0000789 +2014 4 19 0 56766.00 0.060295 0.435868 -0.2283232 0.000116 -0.000080 0.000602 0.001186 0.0011675 0.000072 0.000052 0.0000467 0.000085 0.000090 0.000085 0.000123 0.0000811 +2014 4 20 0 56767.00 0.061038 0.436968 -0.2295260 0.000163 -0.000065 0.000932 0.000879 0.0012600 0.000071 0.000052 0.0000456 0.000090 0.000097 0.000085 0.000123 0.0000890 +2014 4 21 0 56768.00 0.062252 0.437922 -0.2308495 0.000223 -0.000058 0.001376 0.000754 0.0013873 0.000071 0.000052 0.0000367 0.000093 0.000102 0.000085 0.000123 0.0000794 +2014 4 22 0 56769.00 0.063779 0.438720 -0.2323077 0.000288 -0.000066 0.001587 0.000650 0.0015308 0.000071 0.000052 0.0000242 0.000093 0.000103 0.000085 0.000122 0.0000665 +2014 4 23 0 56770.00 0.065320 0.439383 -0.2339101 0.000346 -0.000100 0.001644 0.000486 0.0016633 0.000071 0.000052 0.0000180 0.000093 0.000103 0.000084 0.000121 0.0000605 +2014 4 24 0 56771.00 0.066906 0.439933 -0.2356379 0.000403 -0.000191 0.001778 0.000366 0.0017869 0.000071 0.000052 0.0000175 0.000105 0.000127 0.000085 0.000121 0.0000590 +2014 4 25 0 56772.00 0.068598 0.440443 -0.2374850 0.000273 -0.000070 0.001832 0.000405 0.0019144 0.000071 0.000052 0.0000178 0.000101 0.000115 0.000084 0.000121 0.0000596 +2014 4 26 0 56773.00 0.070290 0.441056 -0.2394423 0.000148 0.000037 0.001610 0.000526 0.0019960 0.000070 0.000051 0.0000211 0.000097 0.000107 0.000084 0.000121 0.0000592 +2014 4 27 0 56774.00 0.071771 0.441760 -0.2414315 0.000126 0.000021 0.001311 0.000720 0.0019547 0.000070 0.000051 0.0000274 0.000092 0.000099 0.000084 0.000120 0.0000586 +2014 4 28 0 56775.00 0.072992 0.442362 -0.2433211 0.000155 -0.000034 0.001242 0.000480 0.0018072 0.000069 0.000051 0.0000234 0.000083 0.000087 0.000084 0.000119 0.0000662 +2014 4 29 0 56776.00 0.074368 0.442670 -0.2450329 0.000206 -0.000076 0.001564 0.000140 0.0016081 0.000069 0.000052 0.0000168 0.000080 0.000083 0.000085 0.000118 0.0000697 +2014 4 30 0 56777.00 0.076125 0.442853 -0.2465446 0.000249 -0.000042 0.001914 0.000212 0.0014299 0.000070 0.000052 0.0000161 0.000082 0.000084 0.000085 0.000119 0.0000666 +2014 5 1 0 56778.00 0.077988 0.443185 -0.2478928 0.000293 -0.000018 0.001801 0.000353 0.0012687 0.000070 0.000052 0.0000214 0.000082 0.000085 0.000085 0.000120 0.0000630 +2014 5 2 0 56779.00 0.079781 0.443597 -0.2490862 0.000337 -0.000024 0.001750 0.000443 0.0010990 0.000069 0.000051 0.0000339 0.000082 0.000084 0.000084 0.000118 0.0000631 +2014 5 3 0 56780.00 0.081752 0.444037 -0.2501396 0.000371 -0.000047 0.002141 0.000616 0.0010426 0.000069 0.000051 0.0000435 0.000079 0.000082 0.000084 0.000118 0.0000624 +2014 5 4 0 56781.00 0.083766 0.444562 -0.2511984 0.000388 -0.000077 0.001914 0.000566 0.0011056 0.000069 0.000051 0.0000394 0.000076 0.000077 0.000084 0.000120 0.0000601 +2014 5 5 0 56782.00 0.085231 0.444871 -0.2523407 0.000380 -0.000103 0.001257 0.000248 0.0011641 0.000069 0.000051 0.0000268 0.000074 0.000076 0.000084 0.000120 0.0000594 +2014 5 6 0 56783.00 0.086238 0.445064 -0.2535331 0.000338 -0.000115 0.001117 0.000305 0.0012217 0.000069 0.000051 0.0000163 0.000074 0.000075 0.000083 0.000120 0.0000582 +2014 5 7 0 56784.00 0.087438 0.445524 -0.2547902 0.000241 -0.000097 0.001415 0.000598 0.0012950 0.000069 0.000051 0.0000117 0.000074 0.000075 0.000083 0.000120 0.0000572 +2014 5 8 0 56785.00 0.088973 0.446186 -0.2561236 0.000179 -0.000091 0.001595 0.000701 0.0013737 0.000069 0.000051 0.0000101 0.000074 0.000075 0.000083 0.000120 0.0000533 +2014 5 9 0 56786.00 0.090386 0.446745 -0.2575276 0.000153 -0.000114 0.001285 0.000448 0.0014259 0.000069 0.000051 0.0000095 0.000074 0.000075 0.000083 0.000120 0.0000492 +2014 5 10 0 56787.00 0.091550 0.446890 -0.2589662 0.000137 -0.000151 0.001321 -0.000018 0.0014452 0.000069 0.000051 0.0000093 0.000075 0.000075 0.000083 0.000121 0.0000515 +2014 5 11 0 56788.00 0.093153 0.446753 -0.2604063 0.000192 -0.000229 0.002099 -0.000044 0.0014300 0.000070 0.000051 0.0000092 0.000074 0.000075 0.000083 0.000121 0.0000521 +2014 5 12 0 56789.00 0.095410 0.446730 -0.2618087 0.000220 -0.000204 0.002540 0.000188 0.0013616 0.000070 0.000051 0.0000092 0.000074 0.000075 0.000084 0.000122 0.0000513 +2014 5 13 0 56790.00 0.097765 0.447052 -0.2631193 0.000214 -0.000150 0.002244 0.000529 0.0012485 0.000070 0.000051 0.0000091 0.000074 0.000075 0.000084 0.000122 0.0000504 +2014 5 14 0 56791.00 0.099776 0.447554 -0.2643110 0.000194 -0.000182 0.001820 0.000584 0.0011440 0.000070 0.000051 0.0000091 0.000074 0.000075 0.000084 0.000123 0.0000488 +2014 5 15 0 56792.00 0.101382 0.447919 -0.2654104 0.000173 -0.000164 0.001479 0.000330 0.0010616 0.000070 0.000051 0.0000091 0.000074 0.000075 0.000084 0.000123 0.0000485 +2014 5 16 0 56793.00 0.102764 0.448066 -0.2664418 0.000159 -0.000217 0.001304 0.000222 0.0010056 0.000070 0.000051 0.0000091 0.000074 0.000075 0.000083 0.000122 0.0000491 +2014 5 17 0 56794.00 0.103960 0.448086 -0.2674423 0.000149 -0.000281 0.001130 0.000076 0.0010084 0.000069 0.000050 0.0000091 0.000074 0.000075 0.000083 0.000122 0.0000499 +2014 5 18 0 56795.00 0.104900 0.448034 -0.2684806 0.000178 -0.000237 0.000923 -0.000095 0.0010779 0.000069 0.000050 0.0000091 0.000074 0.000075 0.000083 0.000122 0.0000514 +2014 5 19 0 56796.00 0.105875 0.447844 -0.2696186 0.000181 -0.000220 0.001175 -0.000176 0.0012132 0.000070 0.000050 0.0000092 0.000074 0.000075 0.000083 0.000122 0.0000529 +2014 5 20 0 56797.00 0.107252 0.447646 -0.2709051 0.000156 -0.000211 0.001710 -0.000061 0.0013599 0.000070 0.000050 0.0000094 0.000075 0.000076 0.000083 0.000122 0.0000534 +2014 5 21 0 56798.00 0.109024 0.447710 -0.2723177 0.000135 -0.000185 0.001945 0.000235 0.0014459 0.000070 0.000051 0.0000102 0.000076 0.000077 0.000083 0.000123 0.0000523 +2014 5 22 0 56799.00 0.110804 0.448053 -0.2737793 0.000145 -0.000186 0.001581 0.000347 0.0014624 0.000070 0.000051 0.0000122 0.000082 0.000083 0.000084 0.000123 0.0000514 +2014 5 23 0 56800.00 0.112231 0.448253 -0.2752272 0.000170 -0.000200 0.001385 0.000082 0.0014259 0.000070 0.000051 0.0000154 0.000089 0.000091 0.000084 0.000124 0.0000517 +2014 5 24 0 56801.00 0.113630 0.448123 -0.2766126 0.000175 -0.000189 0.001543 -0.000193 0.0013360 0.000070 0.000051 0.0000219 0.000089 0.000092 0.000084 0.000123 0.0000510 +2014 5 25 0 56802.00 0.115165 0.447689 -0.2778829 0.000168 -0.000162 0.001640 -0.000510 0.0011965 0.000070 0.000051 0.0000323 0.000088 0.000090 0.000084 0.000123 0.0000497 +2014 5 26 0 56803.00 0.116638 0.447039 -0.2789977 0.000164 -0.000137 0.001587 -0.000666 0.0010335 0.000070 0.000051 0.0000358 0.000083 0.000085 0.000084 0.000123 0.0000555 +2014 5 27 0 56804.00 0.118125 0.446290 -0.2799460 0.000168 -0.000120 0.001746 -0.000798 0.0008667 0.000070 0.000051 0.0000265 0.000080 0.000082 0.000084 0.000122 0.0000592 +2014 5 28 0 56805.00 0.120149 0.445514 -0.2807293 0.000185 -0.000117 0.002459 -0.000686 0.0007003 0.000070 0.000051 0.0000186 0.000079 0.000081 0.000084 0.000122 0.0000559 +2014 5 29 0 56806.00 0.122747 0.444922 -0.2813526 0.000239 -0.000151 0.002717 -0.000619 0.0005478 0.000070 0.000051 0.0000168 0.000088 0.000090 0.000085 0.000122 0.0000535 +2014 5 30 0 56807.00 0.125413 0.444229 -0.2818396 0.000295 -0.000192 0.002576 -0.000692 0.0004296 0.000071 0.000052 0.0000181 0.000103 0.000105 0.000086 0.000122 0.0000523 +2014 5 31 0 56808.00 0.127716 0.443571 -0.2822349 0.000317 -0.000204 0.002072 -0.000581 0.0003726 0.000070 0.000052 0.0000229 0.000104 0.000106 0.000085 0.000121 0.0000521 +2014 6 1 0 56809.00 0.129419 0.442906 -0.2826056 0.000310 -0.000196 0.001490 -0.000754 0.0003833 0.000069 0.000051 0.0000298 0.000095 0.000097 0.000085 0.000120 0.0000519 +2014 6 2 0 56810.00 0.130652 0.441996 -0.2830103 0.000285 -0.000181 0.001252 -0.001027 0.0004311 0.000069 0.000052 0.0000246 0.000082 0.000084 0.000085 0.000120 0.0000513 +2014 6 3 0 56811.00 0.131712 0.440926 -0.2834711 0.000246 -0.000169 0.001231 -0.001227 0.0004895 0.000069 0.000052 0.0000169 0.000077 0.000079 0.000085 0.000120 0.0000508 +2014 6 4 0 56812.00 0.132995 0.439794 -0.2839924 0.000201 -0.000189 0.001441 -0.001014 0.0005513 0.000069 0.000052 0.0000148 0.000078 0.000080 0.000085 0.000119 0.0000497 +2014 6 5 0 56813.00 0.134517 0.438832 -0.2845768 0.000155 -0.000221 0.001707 -0.000886 0.0006173 0.000069 0.000052 0.0000147 0.000083 0.000086 0.000085 0.000120 0.0000489 +2014 6 6 0 56814.00 0.136379 0.437985 -0.2852269 0.000112 -0.000253 0.002053 -0.000747 0.0006853 0.000069 0.000052 0.0000158 0.000086 0.000089 0.000085 0.000119 0.0000495 +2014 6 7 0 56815.00 0.138430 0.437355 -0.2859380 0.000131 -0.000246 0.001996 -0.000607 0.0007317 0.000070 0.000052 0.0000208 0.000087 0.000090 0.000085 0.000120 0.0000497 +2014 6 8 0 56816.00 0.140316 0.436649 -0.2866787 0.000180 -0.000219 0.001920 -0.000767 0.0007423 0.000070 0.000052 0.0000296 0.000090 0.000091 0.000086 0.000121 0.0000493 +2014 6 9 0 56817.00 0.142155 0.435910 -0.2874125 0.000218 -0.000198 0.001896 -0.000661 0.0007165 0.000070 0.000052 0.0000290 0.000097 0.000096 0.000085 0.000122 0.0000493 +2014 6 10 0 56818.00 0.143943 0.435314 -0.2881100 0.000225 -0.000194 0.001895 -0.000530 0.0006796 0.000069 0.000051 0.0000204 0.000101 0.000098 0.000084 0.000121 0.0000498 +2014 6 11 0 56819.00 0.145737 0.434799 -0.2887720 0.000178 -0.000221 0.001888 -0.000467 0.0006505 0.000069 0.000051 0.0000161 0.000101 0.000098 0.000084 0.000120 0.0000500 +2014 6 12 0 56820.00 0.147486 0.434261 -0.2894118 0.000024 -0.000309 0.001806 -0.000603 0.0006364 0.000069 0.000051 0.0000164 0.000127 0.000118 0.000084 0.000120 0.0000495 +2014 6 13 0 56821.00 0.149109 0.433556 -0.2900473 0.000105 -0.000277 0.001692 -0.000782 0.0006342 0.000069 0.000051 0.0000178 0.000121 0.000112 0.000084 0.000121 0.0000493 +2014 6 14 0 56822.00 0.150760 0.432682 -0.2906978 0.000220 -0.000209 0.001714 -0.000914 0.0006747 0.000069 0.000051 0.0000224 0.000115 0.000107 0.000085 0.000122 0.0000504 +2014 6 15 0 56823.00 0.152501 0.431623 -0.2914174 0.000233 -0.000172 0.001898 -0.001029 0.0007712 0.000070 0.000051 0.0000311 0.000111 0.000104 0.000085 0.000123 0.0000518 +2014 6 16 0 56824.00 0.154238 0.430704 -0.2922570 0.000198 -0.000152 0.001789 -0.000872 0.0009165 0.000069 0.000051 0.0000347 0.000098 0.000094 0.000085 0.000123 0.0000534 +2014 6 17 0 56825.00 0.155897 0.430035 -0.2932474 0.000140 -0.000149 0.001643 -0.000616 0.0010606 0.000069 0.000051 0.0000267 0.000087 0.000086 0.000085 0.000123 0.0000541 +2014 6 18 0 56826.00 0.157281 0.429496 -0.2943603 0.000084 -0.000162 0.001404 -0.000603 0.0011538 0.000069 0.000051 0.0000188 0.000084 0.000084 0.000085 0.000123 0.0000522 +2014 6 19 0 56827.00 0.158530 0.428916 -0.2955298 0.000079 -0.000199 0.001427 -0.000772 0.0011741 0.000070 0.000051 0.0000160 0.000082 0.000083 0.000085 0.000123 0.0000508 +2014 6 20 0 56828.00 0.159850 0.428175 -0.2966848 0.000086 -0.000241 0.001376 -0.000892 0.0011233 0.000070 0.000052 0.0000158 0.000080 0.000083 0.000085 0.000123 0.0000503 +2014 6 21 0 56829.00 0.160924 0.427293 -0.2977558 0.000084 -0.000254 0.001044 -0.001011 0.0010058 0.000069 0.000051 0.0000199 0.000079 0.000083 0.000085 0.000122 0.0000499 +2014 6 22 0 56830.00 0.161906 0.426265 -0.2986786 0.000075 -0.000246 0.001195 -0.001081 0.0008347 0.000069 0.000051 0.0000270 0.000078 0.000081 0.000084 0.000121 0.0000496 +2014 6 23 0 56831.00 0.163231 0.425131 -0.2994168 0.000068 -0.000233 0.001558 -0.001208 0.0006495 0.000068 0.000051 0.0000228 0.000077 0.000079 0.000084 0.000120 0.0000499 +2014 6 24 0 56832.00 0.164725 0.423898 -0.2999866 0.000067 -0.000227 0.001570 -0.001344 0.0004966 0.000068 0.000051 0.0000153 0.000077 0.000078 0.000084 0.000119 0.0000501 +2014 6 25 0 56833.00 0.165991 0.422640 -0.3004255 0.000106 -0.000206 0.001157 -0.001325 0.0003678 0.000069 0.000051 0.0000123 0.000078 0.000080 0.000084 0.000119 0.0000499 +2014 6 26 0 56834.00 0.166832 0.421445 -0.3007193 0.000118 -0.000258 0.000745 -0.001270 0.0002238 0.000069 0.000051 0.0000108 0.000081 0.000083 0.000085 0.000120 0.0000495 +2014 6 27 0 56835.00 0.167452 0.420238 -0.3008763 0.000053 -0.000462 0.000766 -0.001296 0.0001252 0.000069 0.000051 0.0000101 0.000086 0.000090 0.000085 0.000120 0.0000503 +2014 6 28 0 56836.00 0.168131 0.419075 -0.3010052 0.000052 -0.000578 0.000794 -0.001295 0.0001480 0.000069 0.000051 0.0000100 0.000088 0.000093 0.000084 0.000120 0.0000516 +2014 6 29 0 56837.00 0.168882 0.417862 -0.3012032 0.000146 -0.000532 0.000794 -0.001332 0.0002407 0.000069 0.000051 0.0000101 0.000092 0.000096 0.000084 0.000119 0.0000517 +2014 6 30 0 56838.00 0.169646 0.416465 -0.3014922 0.000263 -0.000413 0.000850 -0.001588 0.0003284 0.000068 0.000051 0.0000104 0.000097 0.000102 0.000084 0.000118 0.0000522 +2014 7 1 0 56839.00 0.170608 0.415009 -0.3018510 0.000345 -0.000287 0.001169 -0.001473 0.0003860 0.000069 0.000051 0.0000105 0.000099 0.000104 0.000084 0.000118 0.0000524 +2014 7 2 0 56840.00 0.171875 0.413802 -0.3022537 0.000268 -0.000277 0.001432 -0.001243 0.0004282 0.000069 0.000051 0.0000107 0.000093 0.000097 0.000084 0.000118 0.0000511 +2014 7 3 0 56841.00 0.173246 0.412751 -0.3027081 0.000137 -0.000305 0.001400 -0.001118 0.0004813 0.000069 0.000052 0.0000108 0.000083 0.000085 0.000085 0.000119 0.0000510 +2014 7 4 0 56842.00 0.174542 0.411772 -0.3032152 0.000050 -0.000328 0.001293 -0.000823 0.0005235 0.000070 0.000052 0.0000108 0.000082 0.000083 0.000085 0.000119 0.0000528 +2014 7 5 0 56843.00 0.175556 0.410902 -0.3037436 0.000010 -0.000342 0.001029 -0.000978 0.0005225 0.000069 0.000051 0.0000107 0.000081 0.000083 0.000084 0.000119 0.0000556 +2014 7 6 0 56844.00 0.176348 0.409875 -0.3042433 -0.000002 -0.000350 0.000823 -0.001129 0.0004742 0.000069 0.000051 0.0000104 0.000080 0.000081 0.000084 0.000119 0.0000570 +2014 7 7 0 56845.00 0.176987 0.408762 -0.3046817 0.000007 -0.000352 0.000714 -0.001120 0.0004048 0.000069 0.000051 0.0000102 0.000080 0.000079 0.000084 0.000120 0.0000572 +2014 7 8 0 56846.00 0.177616 0.407715 -0.3050522 0.000031 -0.000347 0.000764 -0.001052 0.0003394 0.000070 0.000051 0.0000101 0.000079 0.000079 0.000084 0.000121 0.0000570 +2014 7 9 0 56847.00 0.178342 0.406784 -0.3053680 0.000066 -0.000327 0.000824 -0.000890 0.0002966 0.000070 0.000051 0.0000101 0.000081 0.000080 0.000084 0.000121 0.0000565 +2014 7 10 0 56848.00 0.179088 0.405857 -0.3056603 0.000104 -0.000303 0.000915 -0.001035 0.0002945 0.000070 0.000051 0.0000101 0.000085 0.000083 0.000084 0.000121 0.0000567 +2014 7 11 0 56849.00 0.180049 0.404777 -0.3059773 0.000138 -0.000282 0.001196 -0.001047 0.0003491 0.000070 0.000051 0.0000101 0.000088 0.000085 0.000083 0.000120 0.0000582 +2014 7 12 0 56850.00 0.181102 0.403712 -0.3063807 0.000137 -0.000286 0.001166 -0.000941 0.0004673 0.000070 0.000051 0.0000101 0.000088 0.000086 0.000084 0.000120 0.0000603 +2014 7 13 0 56851.00 0.181992 0.402729 -0.3069288 0.000118 -0.000303 0.000914 -0.000982 0.0006321 0.000070 0.000051 0.0000101 0.000089 0.000087 0.000084 0.000120 0.0000628 +2014 7 14 0 56852.00 0.182698 0.401749 -0.3076467 0.000097 -0.000320 0.000654 -0.000972 0.0007976 0.000070 0.000051 0.0000102 0.000090 0.000090 0.000084 0.000121 0.0000643 +2014 7 15 0 56853.00 0.183168 0.400657 -0.3085067 0.000081 -0.000331 0.000458 -0.001160 0.0009096 0.000070 0.000051 0.0000103 0.000090 0.000091 0.000084 0.000122 0.0000629 +2014 7 16 0 56854.00 0.183619 0.399296 -0.3094354 0.000105 -0.000306 0.000575 -0.001394 0.0009350 0.000070 0.000051 0.0000106 0.000091 0.000091 0.000084 0.000122 0.0000594 +2014 7 17 0 56855.00 0.184021 0.397871 -0.3103459 0.000141 -0.000269 0.000400 -0.001575 0.0008769 0.000071 0.000051 0.0000113 0.000093 0.000091 0.000084 0.000123 0.0000555 +2014 7 18 0 56856.00 0.184463 0.396254 -0.3111684 0.000173 -0.000236 0.000707 -0.001701 0.0007614 0.000071 0.000052 0.0000119 0.000094 0.000092 0.000085 0.000123 0.0000530 +2014 7 19 0 56857.00 0.185345 0.394603 -0.3118591 0.000172 -0.000233 0.001195 -0.001462 0.0006150 0.000071 0.000052 0.0000118 0.000093 0.000091 0.000085 0.000123 0.0000514 +2014 7 20 0 56858.00 0.186504 0.393155 -0.3123964 0.000149 -0.000249 0.001340 -0.001308 0.0004556 0.000071 0.000051 0.0000112 0.000088 0.000087 0.000084 0.000123 0.0000509 +2014 7 21 0 56859.00 0.187745 0.391947 -0.3127712 0.000120 -0.000271 0.001290 -0.000978 0.0002961 0.000071 0.000051 0.0000104 0.000081 0.000082 0.000084 0.000122 0.0000512 +2014 7 22 0 56860.00 0.188753 0.391017 -0.3129953 0.000091 -0.000295 0.000793 -0.000760 0.0001596 0.000071 0.000052 0.0000101 0.000078 0.000080 0.000084 0.000122 0.0000503 +2014 7 23 0 56861.00 0.189324 0.390102 -0.3131036 0.000082 -0.000303 0.000511 -0.000766 0.0000657 0.000071 0.000052 0.0000100 0.000082 0.000085 0.000084 0.000122 0.0000495 +2014 7 24 0 56862.00 0.189662 0.389216 -0.3131430 0.000080 -0.000304 0.000379 -0.000811 0.0000194 0.000071 0.000052 0.0000101 0.000096 0.000100 0.000084 0.000122 0.0000499 +2014 7 25 0 56863.00 0.190004 0.388182 -0.3131564 0.000083 -0.000303 0.000398 -0.000957 0.0000122 0.000071 0.000052 0.0000102 0.000105 0.000110 0.000085 0.000123 0.0000513 +2014 7 26 0 56864.00 0.190384 0.387068 -0.3131774 0.000092 -0.000299 0.000522 -0.000924 0.0000383 0.000071 0.000052 0.0000101 0.000105 0.000109 0.000085 0.000123 0.0000530 +2014 7 27 0 56865.00 0.190992 0.385970 -0.3132440 0.000102 -0.000295 0.000880 -0.000871 0.0001038 0.000071 0.000052 0.0000100 0.000098 0.000101 0.000085 0.000122 0.0000547 +2014 7 28 0 56866.00 0.191985 0.384969 -0.3133963 0.000108 -0.000292 0.001244 -0.000841 0.0002050 0.000071 0.000052 0.0000099 0.000089 0.000090 0.000084 0.000121 0.0000561 +2014 7 29 0 56867.00 0.193140 0.383999 -0.3136607 0.000107 -0.000293 0.001189 -0.000978 0.0003198 0.000070 0.000051 0.0000098 0.000086 0.000086 0.000083 0.000120 0.0000562 +2014 7 30 0 56868.00 0.194094 0.382949 -0.3140328 0.000081 -0.000304 0.000745 -0.001058 0.0004135 0.000069 0.000051 0.0000099 0.000087 0.000086 0.000083 0.000119 0.0000545 +2014 7 31 0 56869.00 0.194746 0.381719 -0.3144727 0.000046 -0.000321 0.000661 -0.001284 0.0004546 0.000069 0.000051 0.0000101 0.000092 0.000087 0.000083 0.000118 0.0000537 +2014 8 1 0 56870.00 0.195453 0.380311 -0.3149212 0.000014 -0.000341 0.000740 -0.001311 0.0004366 0.000070 0.000051 0.0000103 0.000095 0.000087 0.000084 0.000119 0.0000540 +2014 8 2 0 56871.00 0.196067 0.379030 -0.3153290 0.000013 -0.000366 0.000598 -0.001156 0.0003777 0.000070 0.000052 0.0000104 0.000096 0.000088 0.000085 0.000120 0.0000547 +2014 8 3 0 56872.00 0.196688 0.377670 -0.3156678 0.000029 -0.000390 0.000944 -0.001467 0.0003006 0.000071 0.000052 0.0000106 0.000099 0.000094 0.000086 0.000121 0.0000551 +2014 8 4 0 56873.00 0.197586 0.376126 -0.3159289 0.000048 -0.000407 0.001073 -0.001696 0.0002209 0.000071 0.000052 0.0000109 0.000103 0.000103 0.000086 0.000121 0.0000537 +2014 8 5 0 56874.00 0.198614 0.374561 -0.3161119 0.000062 -0.000411 0.001140 -0.001497 0.0001440 0.000070 0.000052 0.0000112 0.000105 0.000106 0.000087 0.000121 0.0000518 +2014 8 6 0 56875.00 0.199855 0.373229 -0.3162225 0.000042 -0.000377 0.001353 -0.001197 0.0000848 0.000070 0.000052 0.0000119 0.000108 0.000107 0.000086 0.000120 0.0000503 +2014 8 7 0 56876.00 0.201128 0.372199 -0.3162981 0.000011 -0.000330 0.001161 -0.000980 0.0000806 0.000070 0.000052 0.0000134 0.000117 0.000109 0.000086 0.000120 0.0000500 +2014 8 8 0 56877.00 0.202146 0.371218 -0.3164109 -0.000017 -0.000286 0.001014 -0.001045 0.0001586 0.000071 0.000053 0.0000148 0.000123 0.000111 0.000087 0.000121 0.0000513 +2014 8 9 0 56878.00 0.203130 0.370145 -0.3166424 -0.000006 -0.000293 0.001089 -0.001148 0.0003101 0.000071 0.000053 0.0000151 0.000127 0.000115 0.000088 0.000121 0.0000539 +2014 8 10 0 56879.00 0.204273 0.369124 -0.3170438 0.000024 -0.000325 0.001216 -0.001007 0.0004932 0.000071 0.000053 0.0000149 0.000146 0.000137 0.000088 0.000122 0.0000565 +2014 8 11 0 56880.00 0.205465 0.368314 -0.3176239 0.000055 -0.000358 0.001244 -0.000788 0.0006650 0.000072 0.000053 0.0000145 0.000174 0.000168 0.000088 0.000123 0.0000600 +2014 8 12 0 56881.00 0.206552 0.367613 -0.3183567 0.000073 -0.000378 0.000964 -0.000742 0.0007920 0.000072 0.000053 0.0000142 0.000184 0.000179 0.000087 0.000123 0.0000624 +2014 8 13 0 56882.00 0.207365 0.366895 -0.3191825 0.000036 -0.000329 0.000625 -0.000753 0.0008456 0.000071 0.000053 0.0000134 0.000170 0.000166 0.000087 0.000123 0.0000612 +2014 8 14 0 56883.00 0.208010 0.366030 -0.3200209 -0.000021 -0.000259 0.000771 -0.000931 0.0008155 0.000071 0.000052 0.0000119 0.000124 0.000121 0.000086 0.000122 0.0000595 +2014 8 15 0 56884.00 0.208828 0.364876 -0.3207914 -0.000071 -0.000194 0.000959 -0.001337 0.0007165 0.000070 0.000052 0.0000109 0.000093 0.000091 0.000086 0.000121 0.0000590 +2014 8 16 0 56885.00 0.209654 0.363519 -0.3214442 -0.000064 -0.000191 0.000785 -0.001470 0.0005870 0.000070 0.000052 0.0000105 0.000089 0.000087 0.000085 0.000120 0.0000590 +2014 8 17 0 56886.00 0.210194 0.362126 -0.3219674 -0.000023 -0.000226 0.000448 -0.001440 0.0004598 0.000070 0.000052 0.0000101 0.000085 0.000084 0.000085 0.000120 0.0000591 +2014 8 18 0 56887.00 0.210327 0.360751 -0.3223711 0.000024 -0.000270 -0.000015 -0.001437 0.0003487 0.000070 0.000052 0.0000097 0.000080 0.000080 0.000086 0.000121 0.0000579 +2014 8 19 0 56888.00 0.210053 0.359315 -0.3226720 0.000063 -0.000309 -0.000313 -0.001540 0.0002566 0.000071 0.000053 0.0000096 0.000078 0.000079 0.000086 0.000120 0.0000561 +2014 8 20 0 56889.00 0.209723 0.357776 -0.3228923 0.000049 -0.000302 -0.000219 -0.001705 0.0001910 0.000070 0.000053 0.0000098 0.000082 0.000085 0.000086 0.000118 0.0000547 +2014 8 21 0 56890.00 0.209606 0.356136 -0.3230647 0.000015 -0.000277 0.000087 -0.001796 0.0001610 0.000070 0.000053 0.0000102 0.000094 0.000102 0.000086 0.000119 0.0000531 +2014 8 22 0 56891.00 0.209651 0.354398 -0.3232256 -0.000022 -0.000248 0.000069 -0.001760 0.0001655 0.000070 0.000052 0.0000106 0.000103 0.000114 0.000086 0.000119 0.0000518 +2014 8 23 0 56892.00 0.209588 0.352556 -0.3234034 -0.000061 -0.000193 -0.000053 -0.001829 0.0001922 0.000070 0.000052 0.0000112 0.000104 0.000115 0.000085 0.000119 0.0000485 +2014 8 24 0 56893.00 0.209537 0.350672 -0.3236125 -0.000086 -0.000147 0.000024 -0.001832 0.0002319 0.000070 0.000052 0.0000124 0.000108 0.000111 0.000085 0.000120 0.0000455 +2014 8 25 0 56894.00 0.209469 0.348828 -0.3238711 -0.000078 -0.000141 -0.000064 -0.001758 0.0002914 0.000070 0.000052 0.0000140 0.000113 0.000108 0.000086 0.000121 0.0000483 +2014 8 26 0 56895.00 0.209266 0.346958 -0.3242002 -0.000020 -0.000203 -0.000131 -0.001899 0.0003688 0.000070 0.000052 0.0000156 0.000115 0.000107 0.000086 0.000121 0.0000498 +2014 8 27 0 56896.00 0.209094 0.344976 -0.3246107 0.000114 -0.000376 -0.000132 -0.002084 0.0004440 0.000070 0.000052 0.0000193 0.000164 0.000144 0.000085 0.000122 0.0000492 +2014 8 28 0 56897.00 0.208968 0.342856 -0.3250790 0.000143 -0.000391 -0.000034 -0.002094 0.0004872 0.000071 0.000052 0.0000196 0.000160 0.000146 0.000085 0.000123 0.0000490 +2014 8 29 0 56898.00 0.209077 0.340950 -0.3255739 0.000059 -0.000245 0.000277 -0.001629 0.0005016 0.000071 0.000052 0.0000164 0.000127 0.000130 0.000085 0.000123 0.0000498 +2014 8 30 0 56899.00 0.209409 0.339492 -0.3260759 -0.000005 -0.000189 0.000326 -0.001194 0.0005041 0.000071 0.000052 0.0000147 0.000120 0.000126 0.000086 0.000125 0.0000520 +2014 8 31 0 56900.00 0.209568 0.338237 -0.3265830 -0.000035 -0.000225 0.000181 -0.001277 0.0005091 0.000071 0.000052 0.0000133 0.000112 0.000117 0.000086 0.000125 0.0000546 +2014 9 1 0 56901.00 0.209621 0.336870 -0.3270975 -0.000049 -0.000281 0.000123 -0.001451 0.0005157 0.000071 0.000052 0.0000114 0.000092 0.000096 0.000085 0.000126 0.0000557 +2014 9 2 0 56902.00 0.209590 0.335236 -0.3276170 -0.000045 -0.000333 -0.000098 -0.001713 0.0005206 0.000071 0.000052 0.0000102 0.000081 0.000083 0.000085 0.000126 0.0000563 +2014 9 3 0 56903.00 0.209274 0.333399 -0.3281425 -0.000022 -0.000355 -0.000284 -0.001842 0.0005320 0.000071 0.000051 0.0000099 0.000078 0.000081 0.000085 0.000125 0.0000559 +2014 9 4 0 56904.00 0.209021 0.331539 -0.3286894 0.000039 -0.000300 -0.000030 -0.001671 0.0005692 0.000071 0.000051 0.0000102 0.000082 0.000086 0.000084 0.000125 0.0000552 +2014 9 5 0 56905.00 0.209074 0.329826 -0.3292945 -0.000029 -0.000398 0.000213 -0.001568 0.0006608 0.000071 0.000051 0.0000108 0.000092 0.000101 0.000085 0.000126 0.0000555 +2014 9 6 0 56906.00 0.209181 0.328285 -0.3300350 -0.000101 -0.000502 0.000086 -0.001446 0.0008323 0.000072 0.000052 0.0000114 0.000097 0.000108 0.000085 0.000127 0.0000570 +2014 9 7 0 56907.00 0.209033 0.326922 -0.3309747 -0.000111 -0.000521 -0.000250 -0.001265 0.0010438 0.000073 0.000052 0.0000123 0.000104 0.000117 0.000086 0.000128 0.0000547 +2014 9 8 0 56908.00 0.208563 0.325612 -0.3321148 -0.000097 -0.000503 -0.000435 -0.001386 0.0012232 0.000073 0.000052 0.0000137 0.000116 0.000136 0.000086 0.000129 0.0000499 +2014 9 9 0 56909.00 0.207952 0.324048 -0.3333925 -0.000085 -0.000471 -0.000642 -0.001656 0.0013096 0.000072 0.000052 0.0000145 0.000121 0.000144 0.000086 0.000128 0.0000522 +2014 9 10 0 56910.00 0.207010 0.322128 -0.3346909 -0.000153 -0.000504 -0.001083 -0.002042 0.0012699 0.000072 0.000052 0.0000140 0.000121 0.000141 0.000086 0.000127 0.0000550 +2014 9 11 0 56911.00 0.205795 0.319838 -0.3358955 -0.000242 -0.000547 -0.001149 -0.002327 0.0011292 0.000072 0.000051 0.0000127 0.000118 0.000132 0.000085 0.000127 0.0000554 +2014 9 12 0 56912.00 0.204725 0.317500 -0.3369293 -0.000319 -0.000574 -0.000947 -0.002108 0.0009391 0.000072 0.000051 0.0000117 0.000116 0.000126 0.000085 0.000127 0.0000543 +2014 9 13 0 56913.00 0.203804 0.315449 -0.3377755 -0.000325 -0.000526 -0.000580 -0.001923 0.0007617 0.000072 0.000051 0.0000114 0.000114 0.000122 0.000085 0.000128 0.0000527 +2014 9 14 0 56914.00 0.203292 0.313711 -0.3384713 -0.000284 -0.000432 -0.000218 -0.001588 0.0006403 0.000073 0.000052 0.0000111 0.000105 0.000110 0.000086 0.000130 0.0000514 +2014 9 15 0 56915.00 0.203041 0.312271 -0.3390810 -0.000223 -0.000329 -0.000204 -0.001378 0.0005878 0.000073 0.000052 0.0000108 0.000091 0.000091 0.000086 0.000130 0.0000510 +2014 9 16 0 56916.00 0.202647 0.310893 -0.3396694 -0.000157 -0.000233 -0.000477 -0.001288 0.0005924 0.000073 0.000052 0.0000107 0.000086 0.000085 0.000085 0.000130 0.0000479 +2014 9 17 0 56917.00 0.201945 0.309523 -0.3402800 -0.000113 -0.000200 -0.000741 -0.001246 0.0006278 0.000072 0.000051 0.0000108 0.000088 0.000089 0.000084 0.000128 0.0000438 +2014 9 18 0 56918.00 0.201148 0.308136 -0.3409299 -0.000081 -0.000195 -0.000639 -0.001259 0.0006695 0.000071 0.000051 0.0000110 0.000095 0.000106 0.000084 0.000127 0.0000461 +2014 9 19 0 56919.00 0.200442 0.306677 -0.3416168 -0.000058 -0.000201 -0.000628 -0.001448 0.0007058 0.000071 0.000051 0.0000111 0.000099 0.000117 0.000084 0.000127 0.0000501 +2014 9 20 0 56920.00 0.199663 0.305010 -0.3423390 -0.000050 -0.000200 -0.000770 -0.001607 0.0007450 0.000072 0.000051 0.0000112 0.000099 0.000117 0.000084 0.000128 0.0000522 +2014 9 21 0 56921.00 0.198838 0.303276 -0.3431083 -0.000053 -0.000195 -0.000652 -0.001659 0.0007990 0.000072 0.000051 0.0000112 0.000097 0.000107 0.000085 0.000129 0.0000491 +2014 9 22 0 56922.00 0.198227 0.301477 -0.3439395 -0.000063 -0.000191 -0.000431 -0.001876 0.0008624 0.000073 0.000051 0.0000112 0.000094 0.000094 0.000085 0.000129 0.0000458 +2014 9 23 0 56923.00 0.197608 0.299651 -0.3448304 -0.000077 -0.000188 -0.000734 -0.001708 0.0009136 0.000073 0.000051 0.0000113 0.000092 0.000089 0.000085 0.000129 0.0000487 +2014 9 24 0 56924.00 0.196765 0.297958 -0.3457574 -0.000100 -0.000190 -0.000743 -0.001533 0.0009336 0.000073 0.000051 0.0000115 0.000092 0.000089 0.000085 0.000129 0.0000481 +2014 9 25 0 56925.00 0.196178 0.296502 -0.3466847 -0.000121 -0.000193 -0.000481 -0.001130 0.0009149 0.000072 0.000051 0.0000119 0.000092 0.000090 0.000085 0.000128 0.0000453 +2014 9 26 0 56926.00 0.195434 0.295324 -0.3475741 -0.000132 -0.000196 -0.000854 -0.001037 0.0008616 0.000072 0.000051 0.0000122 0.000092 0.000091 0.000085 0.000127 0.0000484 +2014 9 27 0 56927.00 0.194284 0.294191 -0.3484004 -0.000083 -0.000187 -0.001126 -0.001023 0.0007929 0.000072 0.000051 0.0000125 0.000092 0.000091 0.000085 0.000127 0.0000501 +2014 9 28 0 56928.00 0.193150 0.293091 -0.3491618 -0.000010 -0.000173 -0.000912 -0.000987 0.0007324 0.000073 0.000052 0.0000132 0.000097 0.000093 0.000086 0.000128 0.0000507 +2014 9 29 0 56929.00 0.192103 0.291856 -0.3498740 0.000049 -0.000164 -0.000932 -0.001332 0.0006960 0.000073 0.000052 0.0000139 0.000102 0.000095 0.000086 0.000128 0.0000519 +2014 9 30 0 56930.00 0.190843 0.290436 -0.3505680 0.000069 -0.000165 -0.001361 -0.001447 0.0006951 0.000072 0.000052 0.0000147 0.000104 0.000096 0.000085 0.000127 0.0000513 +2014 10 1 0 56931.00 0.189348 0.288878 -0.3512794 -0.000008 -0.000189 -0.001358 -0.001458 0.0007352 0.000073 0.000052 0.0000164 0.000100 0.000095 0.000085 0.000127 0.0000500 +2014 10 2 0 56932.00 0.187939 0.287450 -0.3520557 -0.000119 -0.000223 -0.001306 -0.001164 0.0008279 0.000073 0.000052 0.0000200 0.000092 0.000094 0.000085 0.000127 0.0000493 +2014 10 3 0 56933.00 0.186365 0.286320 -0.3529535 -0.000188 -0.000259 -0.001552 -0.001012 0.0009772 0.000072 0.000052 0.0000300 0.000091 0.000094 0.000085 0.000126 0.0000499 +2014 10 4 0 56934.00 0.184640 0.285138 -0.3540235 -0.000215 -0.000293 -0.001578 -0.001139 0.0011650 0.000072 0.000052 0.0000447 0.000094 0.000097 0.000085 0.000125 0.0000508 +2014 10 5 0 56935.00 0.182880 0.283932 -0.3552835 -0.000219 -0.000320 -0.001845 -0.001110 0.0013562 0.000071 0.000051 0.0000389 0.000103 0.000104 0.000084 0.000123 0.0000507 +2014 10 6 0 56936.00 0.180766 0.282658 -0.3567262 -0.000208 -0.000335 -0.002204 -0.001342 0.0015181 0.000071 0.000051 0.0000252 0.000108 0.000110 0.000084 0.000121 0.0000518 +2014 10 7 0 56937.00 0.178512 0.281140 -0.3582946 -0.000189 -0.000334 -0.002068 -0.001528 0.0016023 0.000071 0.000051 0.0000184 0.000110 0.000111 0.000084 0.000120 0.0000533 +2014 10 8 0 56938.00 0.176445 0.279499 -0.3598982 -0.000178 -0.000291 -0.001795 -0.001551 0.0015850 0.000070 0.000052 0.0000160 0.000108 0.000109 0.000084 0.000119 0.0000539 +2014 10 9 0 56939.00 0.174449 0.278036 -0.3614319 -0.000167 -0.000232 -0.001983 -0.001252 0.0014691 0.000070 0.000052 0.0000143 0.000104 0.000103 0.000084 0.000117 0.0000552 +2014 10 10 0 56940.00 0.172103 0.276896 -0.3628163 -0.000156 -0.000173 -0.002398 -0.001057 0.0013004 0.000069 0.000051 0.0000140 0.000100 0.000100 0.000083 0.000116 0.0000566 +2014 10 11 0 56941.00 0.169516 0.275689 -0.3640339 -0.000148 -0.000130 -0.002433 -0.001251 0.0011392 0.000069 0.000051 0.0000172 0.000100 0.000099 0.000083 0.000117 0.0000584 +2014 10 12 0 56942.00 0.167077 0.274327 -0.3651079 -0.000143 -0.000103 -0.002285 -0.001271 0.0010163 0.000069 0.000051 0.0000266 0.000096 0.000096 0.000083 0.000118 0.0000598 +2014 10 13 0 56943.00 0.164911 0.272939 -0.3660846 -0.000140 -0.000090 -0.001857 -0.001262 0.0009385 0.000069 0.000051 0.0000360 0.000087 0.000087 0.000083 0.000119 0.0000557 +2014 10 14 0 56944.00 0.163199 0.271661 -0.3669959 -0.000137 -0.000094 -0.001323 -0.001073 0.0008966 0.000070 0.000051 0.0000264 0.000082 0.000082 0.000084 0.000120 0.0000510 +2014 10 15 0 56945.00 0.161798 0.270630 -0.3678946 -0.000134 -0.000115 -0.001207 -0.000923 0.0009055 0.000070 0.000051 0.0000170 0.000080 0.000080 0.000084 0.000120 0.0000536 +2014 10 16 0 56946.00 0.160342 0.269578 -0.3688180 -0.000129 -0.000170 -0.001340 -0.001099 0.0009412 0.000070 0.000051 0.0000136 0.000083 0.000086 0.000084 0.000120 0.0000550 +2014 10 17 0 56947.00 0.158914 0.268439 -0.3697802 -0.000127 -0.000226 -0.001294 -0.001004 0.0009838 0.000070 0.000051 0.0000123 0.000088 0.000095 0.000084 0.000121 0.0000550 +2014 10 18 0 56948.00 0.157587 0.267453 -0.3707846 -0.000138 -0.000203 -0.001246 -0.000862 0.0010341 0.000071 0.000052 0.0000120 0.000089 0.000095 0.000085 0.000121 0.0000549 +2014 10 19 0 56949.00 0.156223 0.266397 -0.3718534 -0.000158 -0.000133 -0.001294 -0.001009 0.0011116 0.000071 0.000052 0.0000122 0.000086 0.000092 0.000085 0.000122 0.0000551 +2014 10 20 0 56950.00 0.154883 0.265207 -0.3730152 -0.000176 -0.000073 -0.001207 -0.000998 0.0012142 0.000071 0.000052 0.0000125 0.000083 0.000088 0.000085 0.000121 0.0000560 +2014 10 21 0 56951.00 0.153613 0.264147 -0.3742851 -0.000186 -0.000061 -0.001046 -0.000882 0.0013155 0.000070 0.000052 0.0000133 0.000082 0.000087 0.000084 0.000120 0.0000561 +2014 10 22 0 56952.00 0.152520 0.263275 -0.3756363 -0.000177 -0.000147 -0.001006 -0.000678 0.0013637 0.000070 0.000052 0.0000166 0.000089 0.000098 0.000084 0.000120 0.0000553 +2014 10 23 0 56953.00 0.151200 0.262568 -0.3769834 -0.000203 -0.000221 -0.001421 -0.000809 0.0013254 0.000071 0.000052 0.0000268 0.000092 0.000101 0.000085 0.000121 0.0000544 +2014 10 24 0 56954.00 0.149402 0.261587 -0.3782675 -0.000269 -0.000247 -0.001945 -0.001179 0.0012430 0.000071 0.000052 0.0000539 0.000092 0.000101 0.000086 0.000121 0.0000542 +2014 10 25 0 56955.00 0.147247 0.260333 -0.3794620 -0.000341 -0.000245 -0.002297 -0.001186 0.0011461 0.000071 0.000052 0.0000839 0.000093 0.000101 0.000085 0.000120 0.0000538 +2014 10 26 0 56956.00 0.144821 0.259065 -0.3805607 -0.000390 -0.000225 -0.002298 -0.001204 0.0010610 0.000070 0.000052 0.0000562 0.000096 0.000101 0.000085 0.000119 0.0000524 +2014 10 27 0 56957.00 0.142592 0.257870 -0.3816005 -0.000389 -0.000199 -0.001943 -0.000973 0.0010147 0.000070 0.000052 0.0000289 0.000096 0.000101 0.000085 0.000119 0.0000520 +2014 10 28 0 56958.00 0.140686 0.257131 -0.3825999 -0.000309 -0.000177 -0.001686 -0.000375 0.0009924 0.000070 0.000052 0.0000183 0.000097 0.000101 0.000084 0.000120 0.0000527 +2014 10 29 0 56959.00 0.138881 0.256737 -0.3836014 -0.000101 -0.000173 -0.001632 -0.000246 0.0010190 0.000070 0.000051 0.0000149 0.000093 0.000096 0.000083 0.000120 0.0000528 +2014 10 30 0 56960.00 0.137026 0.256379 -0.3846536 -0.000047 -0.000166 -0.001882 -0.000275 0.0010989 0.000070 0.000051 0.0000139 0.000092 0.000096 0.000083 0.000121 0.0000528 +2014 10 31 0 56961.00 0.134895 0.256036 -0.3858161 -0.000150 -0.000154 -0.002208 -0.000359 0.0012392 0.000070 0.000051 0.0000135 0.000094 0.000097 0.000084 0.000122 0.0000529 +2014 11 1 0 56962.00 0.132544 0.255464 -0.3871468 -0.000211 -0.000146 -0.002320 -0.000685 0.0014236 0.000071 0.000051 0.0000131 0.000094 0.000097 0.000084 0.000122 0.0000525 +2014 11 2 0 56963.00 0.130202 0.254603 -0.3886635 -0.000220 -0.000139 -0.002176 -0.000904 0.0015970 0.000071 0.000051 0.0000123 0.000091 0.000092 0.000084 0.000123 0.0000533 +2014 11 3 0 56964.00 0.128145 0.253656 -0.3903202 -0.000214 -0.000132 -0.001704 -0.000807 0.0016966 0.000072 0.000052 0.0000114 0.000086 0.000085 0.000084 0.000124 0.0000557 +2014 11 4 0 56965.00 0.126544 0.253091 -0.3920217 -0.000200 -0.000121 -0.001362 -0.000244 0.0016901 0.000072 0.000052 0.0000109 0.000084 0.000082 0.000084 0.000125 0.0000559 +2014 11 5 0 56966.00 0.125159 0.253048 -0.3936661 -0.000187 -0.000097 -0.001260 0.000205 0.0015903 0.000072 0.000052 0.0000109 0.000084 0.000083 0.000085 0.000125 0.0000540 +2014 11 6 0 56967.00 0.123749 0.253051 -0.3951816 -0.000175 -0.000069 -0.001363 -0.000110 0.0014368 0.000072 0.000052 0.0000110 0.000085 0.000085 0.000084 0.000125 0.0000524 +2014 11 7 0 56968.00 0.122227 0.252692 -0.3965341 -0.000163 -0.000045 -0.001445 -0.000530 0.0012669 0.000071 0.000051 0.0000114 0.000086 0.000087 0.000083 0.000124 0.0000533 +2014 11 8 0 56969.00 0.120616 0.252183 -0.3977204 -0.000211 -0.000044 -0.001649 -0.000352 0.0011051 0.000071 0.000051 0.0000129 0.000094 0.000095 0.000083 0.000124 0.0000552 +2014 11 9 0 56970.00 0.118598 0.251836 -0.3987526 -0.000267 -0.000056 -0.002209 -0.000281 0.0009653 0.000071 0.000051 0.0000171 0.000129 0.000131 0.000083 0.000124 0.0000537 +2014 11 10 0 56971.00 0.116084 0.251443 -0.3996634 -0.000276 -0.000070 -0.002482 -0.000420 0.0008699 0.000071 0.000051 0.0000231 0.000168 0.000169 0.000083 0.000125 0.0000524 +2014 11 11 0 56972.00 0.113499 0.251132 -0.4005117 -0.000203 -0.000078 -0.002339 -0.000186 0.0008392 0.000071 0.000051 0.0000280 0.000178 0.000180 0.000083 0.000125 0.0000521 +2014 11 12 0 56973.00 0.111354 0.251082 -0.4013620 0.000005 -0.000087 -0.001588 0.000016 0.0008711 0.000072 0.000051 0.0000388 0.000262 0.000267 0.000083 0.000125 0.0000514 +2014 11 13 0 56974.00 0.110000 0.251245 -0.4022714 0.000197 0.000092 -0.000960 0.000145 0.0009384 0.000072 0.000051 0.0000594 0.000483 0.000520 0.000084 0.000125 0.0000511 +2014 11 14 0 56975.00 0.108926 0.251516 -0.4032357 -0.000021 -0.000012 -0.001232 0.000318 0.0009897 0.000072 0.000051 0.0000563 0.000359 0.000389 0.000083 0.000125 0.0000508 +2014 11 15 0 56976.00 0.107312 0.251866 -0.4042457 -0.000268 -0.000179 -0.001866 0.000322 0.0010366 0.000071 0.000051 0.0000429 0.000277 0.000300 0.000083 0.000124 0.0000511 +2014 11 16 0 56977.00 0.105177 0.252110 -0.4053091 -0.000315 -0.000214 -0.002046 0.000128 0.0011012 0.000071 0.000051 0.0000300 0.000229 0.000245 0.000083 0.000124 0.0000520 +2014 11 17 0 56978.00 0.103134 0.252334 -0.4064562 -0.000271 -0.000184 -0.001836 0.000309 0.0011902 0.000071 0.000051 0.0000197 0.000144 0.000150 0.000083 0.000124 0.0000528 +2014 11 18 0 56979.00 0.101128 0.252939 -0.4076890 -0.000189 -0.000120 -0.002065 0.000621 0.0012616 0.000071 0.000051 0.0000159 0.000109 0.000111 0.000083 0.000124 0.0000534 +2014 11 19 0 56980.00 0.098831 0.253522 -0.4089645 -0.000180 -0.000042 -0.002279 0.000344 0.0012699 0.000071 0.000051 0.0000176 0.000147 0.000156 0.000083 0.000123 0.0000537 +2014 11 20 0 56981.00 0.096634 0.253807 -0.4102016 -0.000166 -0.000001 -0.001957 0.000169 0.0012014 0.000072 0.000051 0.0000200 0.000200 0.000218 0.000083 0.000124 0.0000545 +2014 11 21 0 56982.00 0.094790 0.254096 -0.4113522 -0.000103 -0.000052 -0.001660 0.000206 0.0011155 0.000072 0.000052 0.0000173 0.000149 0.000160 0.000084 0.000124 0.0000556 +2014 11 22 0 56983.00 0.092994 0.254351 -0.4124438 -0.000068 -0.000110 -0.001789 0.000000 0.0010694 0.000072 0.000052 0.0000148 0.000127 0.000135 0.000084 0.000125 0.0000555 +2014 11 23 0 56984.00 0.091094 0.254341 -0.4135035 -0.000083 -0.000133 -0.001664 -0.000355 0.0010416 0.000072 0.000052 0.0000132 0.000117 0.000121 0.000084 0.000124 0.0000540 +2014 11 24 0 56985.00 0.089566 0.254065 -0.4145315 -0.000123 -0.000124 -0.001113 -0.000410 0.0010111 0.000071 0.000051 0.0000117 0.000102 0.000101 0.000084 0.000122 0.0000516 +2014 11 25 0 56986.00 0.088523 0.253897 -0.4155292 -0.000171 -0.000080 -0.000910 -0.000195 0.0009906 0.000071 0.000051 0.0000108 0.000097 0.000094 0.000084 0.000122 0.0000493 +2014 11 26 0 56987.00 0.087574 0.254040 -0.4165212 -0.000206 0.000005 -0.000781 0.000052 0.0010132 0.000072 0.000052 0.0000103 0.000089 0.000087 0.000085 0.000124 0.0000548 +2014 11 27 0 56988.00 0.086951 0.254487 -0.4175756 -0.000217 0.000072 -0.000540 0.000685 0.0011103 0.000072 0.000053 0.0000101 0.000086 0.000085 0.000086 0.000125 0.0000623 +2014 11 28 0 56989.00 0.086336 0.255266 -0.4187590 -0.000203 0.000101 -0.000793 0.000906 0.0012575 0.000072 0.000052 0.0000103 0.000087 0.000085 0.000085 0.000123 0.0000690 +2014 11 29 0 56990.00 0.085032 0.256040 -0.4200922 -0.000178 0.000104 -0.001528 0.000608 0.0013971 0.000071 0.000052 0.0000108 0.000088 0.000087 0.000084 0.000122 0.0000736 +2014 11 30 0 56991.00 0.083101 0.256739 -0.4215359 -0.000155 0.000088 -0.001918 0.000752 0.0014771 0.000070 0.000051 0.0000117 0.000089 0.000088 0.000083 0.000120 0.0000730 +2014 12 1 0 56992.00 0.080994 0.257691 -0.4230208 -0.000144 0.000060 -0.001941 0.000991 0.0014851 0.000069 0.000051 0.0000122 0.000090 0.000089 0.000083 0.000119 0.0000712 +2014 12 2 0 56993.00 0.078953 0.258722 -0.4244851 -0.000156 0.000028 -0.001979 0.000823 0.0014397 0.000070 0.000052 0.0000121 0.000090 0.000089 0.000084 0.000120 0.0000679 +2014 12 3 0 56994.00 0.076866 0.259474 -0.4258891 -0.000277 -0.000002 -0.002099 0.000468 0.0013655 0.000071 0.000052 0.0000114 0.000087 0.000087 0.000084 0.000121 0.0000650 +2014 12 4 0 56995.00 0.074759 0.259881 -0.4272137 -0.000335 -0.000023 -0.001952 0.000171 0.0012752 0.000071 0.000052 0.0000107 0.000082 0.000084 0.000085 0.000122 0.0000643 +2014 12 5 0 56996.00 0.073111 0.260160 -0.4284370 -0.000235 -0.000026 -0.001202 0.000397 0.0011604 0.000070 0.000052 0.0000112 0.000085 0.000086 0.000084 0.000121 0.0000632 +2014 12 6 0 56997.00 0.071976 0.260944 -0.4295292 -0.000121 -0.000012 -0.001043 0.001056 0.0010327 0.000070 0.000051 0.0000140 0.000086 0.000087 0.000084 0.000122 0.0000587 +2014 12 7 0 56998.00 0.070788 0.261918 -0.4305126 -0.000063 0.000013 -0.001127 0.000806 0.0009535 0.000070 0.000051 0.0000215 0.000089 0.000089 0.000084 0.000123 0.0000532 +2014 12 8 0 56999.00 0.069426 0.262670 -0.4314614 -0.000044 0.000039 -0.001425 0.000705 0.0009532 0.000070 0.000051 0.0000309 0.000098 0.000094 0.000084 0.000122 0.0000552 +2014 12 9 0 57000.00 0.067544 0.263568 -0.4324394 -0.000058 0.000054 -0.002174 0.000901 0.0010028 0.000070 0.000051 0.0000261 0.000106 0.000099 0.000083 0.000121 0.0000553 +2014 12 10 0 57001.00 0.065157 0.264348 -0.4334746 -0.000101 0.000050 -0.002270 0.000654 0.0010657 0.000070 0.000051 0.0000184 0.000107 0.000100 0.000084 0.000122 0.0000542 +2014 12 11 0 57002.00 0.062799 0.265107 -0.4345700 -0.000179 -0.000011 -0.002198 0.000811 0.0011251 0.000070 0.000052 0.0000148 0.000105 0.000099 0.000085 0.000123 0.0000538 +2014 12 12 0 57003.00 0.060546 0.266148 -0.4357218 -0.000257 -0.000074 -0.002178 0.001101 0.0011773 0.000071 0.000052 0.0000127 0.000102 0.000097 0.000085 0.000124 0.0000553 +2014 12 13 0 57004.00 0.058234 0.267318 -0.4369190 -0.000267 -0.000065 -0.002391 0.001034 0.0012143 0.000071 0.000052 0.0000122 0.000103 0.000101 0.000085 0.000123 0.0000574 +2014 12 14 0 57005.00 0.055674 0.268365 -0.4381420 -0.000223 -0.000002 -0.002591 0.000991 0.0012348 0.000071 0.000052 0.0000130 0.000112 0.000118 0.000085 0.000123 0.0000580 +2014 12 15 0 57006.00 0.052963 0.269421 -0.4393854 -0.000162 0.000074 -0.002615 0.001211 0.0012561 0.000071 0.000052 0.0000149 0.000124 0.000145 0.000086 0.000124 0.0000589 +2014 12 16 0 57007.00 0.050194 0.270644 -0.4406559 -0.000105 0.000139 -0.002780 0.001218 0.0012809 0.000071 0.000052 0.0000161 0.000129 0.000154 0.000085 0.000123 0.0000597 +2014 12 17 0 57008.00 0.047346 0.271690 -0.4419442 -0.000115 0.000107 -0.002828 0.000739 0.0012824 0.000070 0.000052 0.0000166 0.000130 0.000151 0.000085 0.000122 0.0000589 +2014 12 18 0 57009.00 0.044917 0.272193 -0.4432065 -0.000153 0.000040 -0.002014 0.000352 0.0012324 0.000071 0.000052 0.0000173 0.000132 0.000138 0.000085 0.000122 0.0000586 +2014 12 19 0 57010.00 0.043355 0.272626 -0.4443940 -0.000198 -0.000030 -0.001179 0.000548 0.0011385 0.000071 0.000052 0.0000179 0.000134 0.000130 0.000085 0.000123 0.0000561 +2014 12 20 0 57011.00 0.042118 0.273237 -0.4454761 -0.000240 -0.000079 -0.001208 0.000517 0.0010302 0.000072 0.000053 0.0000179 0.000132 0.000127 0.000086 0.000124 0.0000545 +2014 12 21 0 57012.00 0.040800 0.273648 -0.4464607 -0.000272 -0.000100 -0.001253 0.000322 0.0009494 0.000072 0.000053 0.0000164 0.000123 0.000118 0.000086 0.000124 0.0000572 +2014 12 22 0 57013.00 0.039595 0.273958 -0.4473941 -0.000286 -0.000087 -0.001111 0.000306 0.0009308 0.000071 0.000052 0.0000142 0.000112 0.000108 0.000086 0.000123 0.0000589 +2014 12 23 0 57014.00 0.038628 0.274317 -0.4483483 -0.000272 -0.000033 -0.000877 0.000531 0.0009900 0.000071 0.000053 0.0000132 0.000108 0.000105 0.000086 0.000123 0.0000592 +2014 12 24 0 57015.00 0.037854 0.274911 -0.4493971 -0.000218 0.000077 -0.000637 0.000730 0.0011166 0.000072 0.000053 0.0000128 0.000112 0.000107 0.000086 0.000124 0.0000587 +2014 12 25 0 57016.00 0.037121 0.275600 -0.4505944 -0.000193 0.000149 -0.000700 0.000750 0.0012765 0.000072 0.000053 0.0000128 0.000113 0.000108 0.000086 0.000123 0.0000545 +2014 12 26 0 57017.00 0.036186 0.276314 -0.4519451 -0.000212 0.000158 -0.001028 0.000617 0.0014182 0.000072 0.000053 0.0000131 0.000116 0.000111 0.000087 0.000123 0.0000635 +2014 12 27 0 57018.00 0.035258 0.276968 -0.4534133 -0.000246 0.000130 -0.000801 0.000715 0.0015041 0.000071 0.000053 0.0000143 0.000131 0.000125 0.000086 0.000122 0.0000699 +2014 12 28 0 57019.00 0.034672 0.277753 -0.4549265 -0.000273 0.000083 -0.000414 0.001001 0.0015036 0.000070 0.000052 0.0000165 0.000151 0.000147 0.000085 0.000120 0.0000700 +2014 12 29 0 57020.00 0.034227 0.278675 -0.4563892 -0.000273 0.000035 -0.000476 0.000985 0.0014092 0.000070 0.000052 0.0000178 0.000158 0.000154 0.000085 0.000121 0.0000666 +2014 12 30 0 57021.00 0.033488 0.279572 -0.4577210 -0.000222 0.000003 -0.001015 0.000967 0.0012542 0.000071 0.000052 0.0000182 0.000160 0.000155 0.000085 0.000121 0.0000642 +2014 12 31 0 57022.00 0.032208 0.280335 -0.4588913 -0.000089 0.000011 -0.001458 0.000584 0.0010982 0.000071 0.000052 0.0000230 0.000226 0.000218 0.000084 0.000122 0.0000626 diff --git a/src/test/resources/eopc04/eopc04.15 b/src/test/resources/eopc04/eopc04.15 new file mode 100644 index 0000000000..e6dd7ac6ff --- /dev/null +++ b/src/test/resources/eopc04/eopc04.15 @@ -0,0 +1,371 @@ +# EARTH ORIENTATION PARAMETER (EOP) PRODUCT CENTER CENTER (PARIS OBSERVATORY) - INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE +# EOP (IERS) 20 C04 TIME SERIES consistent with ITRF 2020 - sampled at 12h UTC +# Contact: christian.bizouard@obspm.fr +# Reference Precession-Nutation Model: IAU 2000 +# format(4(i4),f10.2,2(f12.6),f12.7,2(f12.6),2(f12.6),f12.7,2(f12.6),f12.7,2(f12.6),2(f12.6),f12.7) +# YR MM DD HH MJD x(") y(") UT1-UTC(s) dX(") dY(") xrt(") yrt(") LOD(s) x Er y Er UT1-UTC Er dX Er dY Er xrt Er yrt Er LOD Er +2015 1 1 12 57023.50 0.030148 0.281014 -0.4604082 -0.000006 0.000056 -0.001205 0.000527 0.0009282 0.000070 0.000052 0.0000255 0.000181 0.000182 0.000084 0.000114 0.0000178 +2015 1 2 12 57024.50 0.029219 0.281441 -0.4612797 -0.000041 0.000098 -0.000619 0.000394 0.0008149 0.000069 0.000052 0.0000343 0.000169 0.000170 0.000083 0.000113 0.0000179 +2015 1 3 12 57025.50 0.028777 0.281824 -0.4620431 -0.000115 0.000142 -0.000269 0.000436 0.0007094 0.000069 0.000052 0.0000297 0.000141 0.000143 0.000083 0.000113 0.0000177 +2015 1 4 12 57026.50 0.028595 0.282407 -0.4627156 -0.000202 0.000181 -0.000195 0.000785 0.0006401 0.000070 0.000052 0.0000200 0.000127 0.000128 0.000084 0.000114 0.0000175 +2015 1 5 12 57027.50 0.028181 0.283151 -0.4633451 -0.000277 0.000207 -0.000444 0.000689 0.0006268 0.000070 0.000053 0.0000160 0.000124 0.000125 0.000085 0.000114 0.0000174 +2015 1 6 12 57028.50 0.027570 0.283994 -0.4639915 -0.000282 0.000206 -0.000816 0.001187 0.0006779 0.000071 0.000053 0.0000152 0.000125 0.000126 0.000085 0.000115 0.0000173 +2015 1 7 12 57029.50 0.026490 0.285047 -0.4647156 -0.000184 0.000167 -0.001150 0.000943 0.0007801 0.000072 0.000054 0.0000161 0.000140 0.000141 0.000086 0.000117 0.0000174 +2015 1 8 12 57030.50 0.025192 0.285811 -0.4655555 -0.000086 0.000122 -0.001260 0.000582 0.0009043 0.000072 0.000054 0.0000180 0.000177 0.000176 0.000087 0.000117 0.0000172 +2015 1 9 12 57031.50 0.024041 0.286304 -0.4665177 -0.000017 0.000074 -0.000986 0.000591 0.0010180 0.000072 0.000054 0.0000196 0.000194 0.000192 0.000086 0.000117 0.0000167 +2015 1 10 12 57032.50 0.023048 0.287241 -0.4675780 -0.000032 0.000026 -0.001028 0.001248 0.0010925 0.000071 0.000053 0.0000213 0.000171 0.000172 0.000086 0.000117 0.0000161 +2015 1 11 12 57033.50 0.022026 0.288345 -0.4686919 -0.000071 -0.000014 -0.000972 0.001006 0.0011262 0.000071 0.000053 0.0000231 0.000120 0.000127 0.000086 0.000117 0.0000154 +2015 1 12 12 57034.50 0.020975 0.289306 -0.4698252 -0.000118 -0.000040 -0.001052 0.001024 0.0011363 0.000071 0.000053 0.0000198 0.000093 0.000099 0.000086 0.000116 0.0000150 +2015 1 13 12 57035.50 0.019672 0.290379 -0.4709628 -0.000160 -0.000046 -0.001398 0.000980 0.0011408 0.000070 0.000053 0.0000144 0.000087 0.000093 0.000085 0.000115 0.0000152 +2015 1 14 12 57036.50 0.018069 0.291484 -0.4721024 -0.000173 -0.000019 -0.001718 0.001296 0.0011404 0.000070 0.000053 0.0000124 0.000086 0.000092 0.000085 0.000114 0.0000158 +2015 1 15 12 57037.50 0.016088 0.292936 -0.4732349 -0.000148 0.000042 -0.002050 0.001406 0.0011225 0.000069 0.000053 0.0000121 0.000084 0.000089 0.000084 0.000113 0.0000164 +2015 1 16 12 57038.50 0.014006 0.294142 -0.4743416 -0.000123 0.000097 -0.002178 0.000901 0.0010889 0.000069 0.000053 0.0000125 0.000083 0.000087 0.000085 0.000112 0.0000168 +2015 1 17 12 57039.50 0.011856 0.294899 -0.4754148 -0.000110 0.000120 -0.002036 0.000719 0.0010581 0.000069 0.000052 0.0000142 0.000083 0.000086 0.000084 0.000112 0.0000170 +2015 1 18 12 57040.50 0.009953 0.295725 -0.4764719 -0.000103 0.000125 -0.001653 0.000620 0.0010634 0.000068 0.000052 0.0000179 0.000080 0.000084 0.000084 0.000111 0.0000167 +2015 1 19 12 57041.50 0.008681 0.296498 -0.4775619 -0.000103 0.000114 -0.001106 0.000865 0.0011245 0.000069 0.000053 0.0000181 0.000076 0.000082 0.000085 0.000112 0.0000161 +2015 1 20 12 57042.50 0.007691 0.297229 -0.4787436 -0.000109 0.000090 -0.000606 0.000644 0.0012495 0.000069 0.000053 0.0000138 0.000075 0.000081 0.000085 0.000112 0.0000155 +2015 1 21 12 57043.50 0.007139 0.298079 -0.4800700 -0.000121 0.000051 -0.000433 0.001113 0.0014067 0.000069 0.000053 0.0000119 0.000075 0.000081 0.000084 0.000111 0.0000155 +2015 1 22 12 57044.50 0.006561 0.299104 -0.4815451 -0.000138 0.000004 -0.000613 0.000815 0.0015347 0.000070 0.000053 0.0000115 0.000079 0.000087 0.000085 0.000112 0.0000163 +2015 1 23 12 57045.50 0.005646 0.299890 -0.4831119 -0.000158 -0.000031 -0.001127 0.000743 0.0015817 0.000070 0.000053 0.0000115 0.000083 0.000091 0.000085 0.000113 0.0000170 +2015 1 24 12 57046.50 0.004573 0.300654 -0.4846765 -0.000183 -0.000029 -0.000864 0.000886 0.0015303 0.000071 0.000054 0.0000118 0.000085 0.000095 0.000087 0.000116 0.0000168 +2015 1 25 12 57047.50 0.003435 0.301783 -0.4861450 -0.000205 -0.000013 -0.001340 0.001023 0.0013920 0.000070 0.000053 0.0000123 0.000094 0.000105 0.000086 0.000114 0.0000159 +2015 1 26 12 57048.50 0.002492 0.302579 -0.4874478 -0.000216 0.000012 -0.000256 0.000854 0.0012066 0.000069 0.000053 0.0000128 0.000102 0.000115 0.000085 0.000112 0.0000155 +2015 1 27 12 57049.50 0.002334 0.303771 -0.4885638 -0.000213 0.000027 -0.000074 0.001381 0.0010288 0.000069 0.000053 0.0000127 0.000101 0.000113 0.000085 0.000111 0.0000158 +2015 1 28 12 57050.50 0.002172 0.305258 -0.4895250 -0.000189 0.000030 0.000030 0.001641 0.0009107 0.000069 0.000053 0.0000121 0.000089 0.000097 0.000085 0.000112 0.0000166 +2015 1 29 12 57051.50 0.002477 0.307262 -0.4904007 -0.000125 0.000070 0.000739 0.002307 0.0008569 0.000070 0.000053 0.0000117 0.000085 0.000091 0.000086 0.000114 0.0000172 +2015 1 30 12 57052.50 0.003260 0.309675 -0.4912443 -0.000046 0.000141 0.000967 0.002389 0.0008278 0.000070 0.000053 0.0000118 0.000087 0.000092 0.000086 0.000114 0.0000173 +2015 1 31 12 57053.50 0.004143 0.311995 -0.4920745 -0.000064 0.000169 0.000815 0.002273 0.0008413 0.000069 0.000053 0.0000123 0.000090 0.000096 0.000086 0.000114 0.0000171 +2015 2 1 12 57054.50 0.004675 0.314349 -0.4929445 -0.000116 0.000164 0.000145 0.002459 0.0009088 0.000070 0.000053 0.0000133 0.000099 0.000112 0.000087 0.000114 0.0000172 +2015 2 2 12 57055.50 0.004376 0.316704 -0.4939024 -0.000147 0.000132 -0.000335 0.002162 0.0010213 0.000070 0.000053 0.0000145 0.000106 0.000127 0.000087 0.000114 0.0000174 +2015 2 3 12 57056.50 0.004052 0.318809 -0.4949726 -0.000096 0.000071 -0.000257 0.002055 0.0011074 0.000069 0.000053 0.0000164 0.000115 0.000140 0.000085 0.000113 0.0000173 +2015 2 4 12 57057.50 0.003522 0.320601 -0.4961047 0.000053 0.000045 -0.000659 0.001571 0.0011261 0.000069 0.000053 0.0000189 0.000144 0.000172 0.000086 0.000113 0.0000170 +2015 2 5 12 57058.50 0.002874 0.321944 -0.4972554 0.000135 0.000116 -0.000434 0.001407 0.0012079 0.000070 0.000054 0.0000177 0.000134 0.000158 0.000087 0.000114 0.0000168 +2015 2 6 12 57059.50 0.002377 0.323611 -0.4985115 -0.000045 0.000188 -0.000544 0.001895 0.0013001 0.000070 0.000054 0.0000148 0.000106 0.000124 0.000087 0.000115 0.0000167 +2015 2 7 12 57060.50 0.001907 0.325093 -0.4998418 -0.000102 0.000321 -0.000081 0.001215 0.0013565 0.000070 0.000053 0.0000138 0.000103 0.000118 0.000086 0.000113 0.0000167 +2015 2 8 12 57061.50 0.001933 0.326419 -0.5012026 -0.000108 0.000411 0.000113 0.001490 0.0013505 0.000069 0.000053 0.0000131 0.000097 0.000106 0.000086 0.000112 0.0000167 +2015 2 9 12 57062.50 0.002073 0.327714 -0.5025280 -0.000101 0.000364 0.000342 0.001206 0.0012847 0.000068 0.000053 0.0000128 0.000092 0.000099 0.000085 0.000110 0.0000168 +2015 2 10 12 57063.50 0.002194 0.329033 -0.5037759 -0.000143 0.000002 -0.000010 0.001456 0.0012118 0.000068 0.000053 0.0000136 0.000098 0.000105 0.000084 0.000109 0.0000167 +2015 2 11 12 57064.50 0.002186 0.330439 -0.5049634 -0.000138 -0.000263 0.000216 0.001423 0.0011772 0.000068 0.000053 0.0000155 0.000122 0.000132 0.000085 0.000109 0.0000165 +2015 2 12 12 57065.50 0.002317 0.331821 -0.5061320 -0.000017 0.000043 0.000161 0.001259 0.0011684 0.000068 0.000052 0.0000162 0.000126 0.000133 0.000085 0.000109 0.0000161 +2015 2 13 12 57066.50 0.002551 0.332942 -0.5072958 -0.000045 0.000117 0.000219 0.001108 0.0011558 0.000068 0.000052 0.0000145 0.000097 0.000103 0.000084 0.000109 0.0000158 +2015 2 14 12 57067.50 0.002739 0.334036 -0.5084514 0.000034 0.000102 0.000148 0.001184 0.0011587 0.000068 0.000052 0.0000137 0.000096 0.000101 0.000084 0.000110 0.0000156 +2015 2 15 12 57068.50 0.002790 0.335108 -0.5096316 0.000123 0.000048 0.000058 0.001107 0.0012100 0.000069 0.000052 0.0000132 0.000092 0.000098 0.000085 0.000112 0.0000155 +2015 2 16 12 57069.50 0.002911 0.336450 -0.5108948 0.000151 0.000008 0.000155 0.001492 0.0013284 0.000070 0.000053 0.0000130 0.000089 0.000096 0.000085 0.000114 0.0000154 +2015 2 17 12 57070.50 0.002772 0.337889 -0.5123038 0.000017 0.000051 -0.000125 0.001123 0.0014960 0.000070 0.000053 0.0000138 0.000095 0.000103 0.000085 0.000115 0.0000155 +2015 2 18 12 57071.50 0.002756 0.338977 -0.5138857 -0.000177 0.000146 0.000165 0.001254 0.0016637 0.000070 0.000053 0.0000147 0.000115 0.000125 0.000085 0.000115 0.0000157 +2015 2 19 12 57072.50 0.002702 0.340377 -0.5156118 -0.000163 0.000179 -0.000144 0.001332 0.0017737 0.000071 0.000053 0.0000133 0.000091 0.000097 0.000085 0.000116 0.0000156 +2015 2 20 12 57073.50 0.002475 0.341828 -0.5174023 -0.000070 0.000175 -0.000258 0.001601 0.0017884 0.000070 0.000052 0.0000120 0.000081 0.000086 0.000084 0.000116 0.0000157 +2015 2 21 12 57074.50 0.002321 0.343620 -0.5191567 -0.000055 0.000159 0.000022 0.001824 0.0017049 0.000070 0.000052 0.0000118 0.000081 0.000085 0.000085 0.000117 0.0000163 +2015 2 22 12 57075.50 0.002692 0.345353 -0.5207873 -0.000046 0.000138 0.000726 0.001721 0.0015470 0.000071 0.000053 0.0000119 0.000082 0.000084 0.000085 0.000118 0.0000169 +2015 2 23 12 57076.50 0.003284 0.347436 -0.5222382 -0.000037 0.000125 0.000330 0.002166 0.0013524 0.000072 0.000053 0.0000120 0.000083 0.000083 0.000086 0.000119 0.0000171 +2015 2 24 12 57077.50 0.003268 0.349465 -0.5234936 -0.000017 0.000146 -0.000186 0.001665 0.0011627 0.000071 0.000053 0.0000121 0.000083 0.000083 0.000085 0.000119 0.0000171 +2015 2 25 12 57078.50 0.003093 0.351077 -0.5245744 0.000022 0.000220 0.000039 0.001386 0.0010057 0.000071 0.000052 0.0000122 0.000084 0.000084 0.000085 0.000118 0.0000172 +2015 2 26 12 57079.50 0.003249 0.352416 -0.5255213 0.000059 0.000298 0.000299 0.001299 0.0008961 0.000071 0.000052 0.0000123 0.000085 0.000085 0.000084 0.000118 0.0000176 +2015 2 27 12 57080.50 0.003276 0.353971 -0.5263859 0.000080 0.000354 -0.000201 0.001633 0.0008414 0.000071 0.000052 0.0000128 0.000085 0.000086 0.000084 0.000118 0.0000175 +2015 2 28 12 57081.50 0.003138 0.355676 -0.5272253 0.000055 0.000341 -0.000037 0.001693 0.0008504 0.000071 0.000052 0.0000147 0.000085 0.000085 0.000084 0.000119 0.0000174 +2015 3 1 12 57082.50 0.003264 0.357606 -0.5280996 0.000022 0.000306 0.000385 0.001961 0.0009051 0.000071 0.000052 0.0000187 0.000083 0.000084 0.000084 0.000119 0.0000175 +2015 3 2 12 57083.50 0.003695 0.359649 -0.5290420 -0.000010 0.000256 0.000487 0.001867 0.0009846 0.000072 0.000052 0.0000191 0.000080 0.000083 0.000084 0.000120 0.0000174 +2015 3 3 12 57084.50 0.003887 0.361440 -0.5300654 -0.000030 0.000202 0.000026 0.001522 0.0010568 0.000071 0.000052 0.0000146 0.000080 0.000083 0.000084 0.000119 0.0000172 +2015 3 4 12 57085.50 0.004036 0.362996 -0.5311525 -0.000018 0.000155 0.000387 0.001407 0.0011094 0.000071 0.000052 0.0000126 0.000080 0.000083 0.000083 0.000118 0.0000169 +2015 3 5 12 57086.50 0.004532 0.364378 -0.5322882 0.000028 0.000122 0.000520 0.001224 0.0011622 0.000071 0.000052 0.0000121 0.000080 0.000082 0.000084 0.000119 0.0000170 +2015 3 6 12 57087.50 0.004935 0.365532 -0.5334802 0.000065 0.000098 0.000266 0.000961 0.0012263 0.000071 0.000052 0.0000120 0.000080 0.000082 0.000083 0.000119 0.0000169 +2015 3 7 12 57088.50 0.004852 0.366781 -0.5347360 0.000068 0.000088 -0.000501 0.001169 0.0012825 0.000071 0.000052 0.0000121 0.000081 0.000083 0.000083 0.000119 0.0000164 +2015 3 8 12 57089.50 0.004320 0.367766 -0.5360398 0.000057 0.000087 -0.000541 0.000699 0.0013218 0.000071 0.000052 0.0000124 0.000083 0.000085 0.000083 0.000119 0.0000162 +2015 3 9 12 57090.50 0.003761 0.368698 -0.5373758 0.000038 0.000093 -0.000447 0.001043 0.0013496 0.000072 0.000052 0.0000126 0.000086 0.000087 0.000083 0.000120 0.0000163 +2015 3 10 12 57091.50 0.003506 0.369883 -0.5387367 0.000019 0.000104 -0.000152 0.001172 0.0013736 0.000071 0.000051 0.0000128 0.000087 0.000087 0.000082 0.000117 0.0000162 +2015 3 11 12 57092.50 0.003269 0.371049 -0.5401224 0.000008 0.000116 -0.000218 0.000915 0.0014028 0.000070 0.000051 0.0000130 0.000089 0.000089 0.000082 0.000117 0.0000164 +2015 3 12 12 57093.50 0.003095 0.372173 -0.5415396 0.000003 0.000132 -0.000264 0.001029 0.0014345 0.000070 0.000051 0.0000134 0.000093 0.000091 0.000082 0.000117 0.0000164 +2015 3 13 12 57094.50 0.002747 0.373195 -0.5429901 0.000009 0.000153 -0.000168 0.000724 0.0014669 0.000069 0.000051 0.0000134 0.000095 0.000092 0.000081 0.000116 0.0000164 +2015 3 14 12 57095.50 0.002760 0.374081 -0.5444783 0.000040 0.000185 0.000079 0.000896 0.0015101 0.000070 0.000051 0.0000127 0.000091 0.000089 0.000081 0.000116 0.0000163 +2015 3 15 12 57096.50 0.002778 0.375082 -0.5460243 0.000074 0.000214 0.000017 0.000800 0.0015835 0.000070 0.000051 0.0000117 0.000082 0.000083 0.000082 0.000117 0.0000161 +2015 3 16 12 57097.50 0.002801 0.375939 -0.5476687 0.000103 0.000234 0.000034 0.000749 0.0017129 0.000071 0.000051 0.0000110 0.000078 0.000080 0.000082 0.000117 0.0000162 +2015 3 17 12 57098.50 0.002807 0.376998 -0.5494699 0.000109 0.000228 0.000026 0.000995 0.0018979 0.000070 0.000051 0.0000109 0.000077 0.000079 0.000082 0.000117 0.0000164 +2015 3 18 12 57099.50 0.003013 0.377952 -0.5514665 0.000081 0.000186 0.000631 0.000749 0.0020959 0.000070 0.000051 0.0000111 0.000077 0.000080 0.000082 0.000116 0.0000168 +2015 3 19 12 57100.50 0.003967 0.378926 -0.5536392 0.000050 0.000145 0.001140 0.001109 0.0022357 0.000071 0.000052 0.0000113 0.000077 0.000080 0.000083 0.000117 0.0000175 +2015 3 20 12 57101.50 0.005371 0.380342 -0.5559018 0.000031 0.000132 0.001692 0.001638 0.0022667 0.000071 0.000052 0.0000115 0.000077 0.000080 0.000083 0.000117 0.0000181 +2015 3 21 12 57102.50 0.006970 0.382336 -0.5581411 0.000049 0.000189 0.001270 0.002099 0.0021959 0.000071 0.000052 0.0000115 0.000078 0.000081 0.000082 0.000116 0.0000185 +2015 3 22 12 57103.50 0.008029 0.384294 -0.5602747 0.000075 0.000256 0.000982 0.001880 0.0020661 0.000070 0.000052 0.0000115 0.000079 0.000082 0.000082 0.000116 0.0000184 +2015 3 23 12 57104.50 0.008914 0.386215 -0.5622678 0.000102 0.000315 0.000822 0.001917 0.0019291 0.000070 0.000051 0.0000115 0.000081 0.000083 0.000082 0.000115 0.0000179 +2015 3 24 12 57105.50 0.009682 0.387922 -0.5641265 0.000128 0.000338 0.000732 0.001279 0.0017946 0.000069 0.000051 0.0000114 0.000080 0.000083 0.000081 0.000113 0.0000178 +2015 3 25 12 57106.50 0.010365 0.389185 -0.5658466 0.000144 0.000313 0.000606 0.001083 0.0016330 0.000069 0.000051 0.0000113 0.000077 0.000080 0.000081 0.000113 0.0000178 +2015 3 26 12 57107.50 0.010732 0.390378 -0.5674031 0.000117 0.000286 0.000246 0.001008 0.0014757 0.000069 0.000052 0.0000113 0.000076 0.000079 0.000082 0.000113 0.0000175 +2015 3 27 12 57108.50 0.010948 0.391551 -0.5688272 0.000058 0.000262 0.000230 0.001136 0.0013915 0.000069 0.000051 0.0000113 0.000078 0.000081 0.000082 0.000112 0.0000173 +2015 3 28 12 57109.50 0.011216 0.392675 -0.5702032 0.000049 0.000220 0.000375 0.000965 0.0013714 0.000068 0.000051 0.0000114 0.000079 0.000081 0.000081 0.000110 0.0000175 +2015 3 29 12 57110.50 0.011760 0.393653 -0.5715776 0.000067 0.000177 0.000644 0.000944 0.0013827 0.000069 0.000052 0.0000113 0.000080 0.000082 0.000082 0.000110 0.0000176 +2015 3 30 12 57111.50 0.012307 0.394657 -0.5729669 0.000093 0.000144 0.000715 0.000990 0.0013930 0.000069 0.000052 0.0000113 0.000082 0.000082 0.000083 0.000109 0.0000173 +2015 3 31 12 57112.50 0.013130 0.395851 -0.5743589 0.000101 0.000140 0.001228 0.001046 0.0013843 0.000069 0.000053 0.0000114 0.000081 0.000082 0.000083 0.000109 0.0000169 +2015 4 1 12 57113.50 0.014738 0.397130 -0.5757329 0.000079 0.000175 0.001975 0.001272 0.0013601 0.000069 0.000052 0.0000116 0.000078 0.000081 0.000083 0.000110 0.0000164 +2015 4 2 12 57114.50 0.016640 0.398790 -0.5770759 0.000064 0.000207 0.001697 0.001735 0.0013215 0.000069 0.000052 0.0000128 0.000076 0.000080 0.000083 0.000110 0.0000164 +2015 4 3 12 57115.50 0.018051 0.400671 -0.5783749 0.000088 0.000199 0.001238 0.001835 0.0012721 0.000069 0.000053 0.0000165 0.000077 0.000080 0.000083 0.000110 0.0000166 +2015 4 4 12 57116.50 0.018855 0.402582 -0.5796251 0.000126 0.000179 0.000550 0.001619 0.0012295 0.000071 0.000053 0.0000193 0.000078 0.000082 0.000085 0.000112 0.0000169 +2015 4 5 12 57117.50 0.019373 0.404109 -0.5808412 0.000169 0.000158 0.000498 0.001457 0.0012085 0.000071 0.000054 0.0000168 0.000083 0.000089 0.000085 0.000113 0.0000171 +2015 4 6 12 57118.50 0.019921 0.405444 -0.5820470 0.000208 0.000144 0.000704 0.001172 0.0012100 0.000070 0.000053 0.0000140 0.000086 0.000093 0.000084 0.000112 0.0000173 +2015 4 7 12 57119.50 0.020580 0.406540 -0.5832626 0.000235 0.000147 0.000693 0.000809 0.0012286 0.000069 0.000052 0.0000129 0.000087 0.000093 0.000083 0.000110 0.0000169 +2015 4 8 12 57120.50 0.021170 0.407461 -0.5845010 0.000241 0.000185 0.000550 0.000626 0.0012479 0.000069 0.000052 0.0000127 0.000088 0.000095 0.000082 0.000109 0.0000167 +2015 4 9 12 57121.50 0.021526 0.408527 -0.5857624 0.000215 0.000227 0.000092 0.001139 0.0012723 0.000068 0.000052 0.0000124 0.000088 0.000094 0.000082 0.000109 0.0000169 +2015 4 10 12 57122.50 0.021378 0.410022 -0.5870680 0.000165 0.000222 -0.000360 0.001299 0.0013598 0.000068 0.000052 0.0000117 0.000078 0.000083 0.000082 0.000109 0.0000171 +2015 4 11 12 57123.50 0.021132 0.411156 -0.5884907 0.000164 0.000205 -0.000104 0.000936 0.0014935 0.000068 0.000052 0.0000114 0.000078 0.000083 0.000082 0.000109 0.0000172 +2015 4 12 12 57124.50 0.021125 0.412086 -0.5900576 0.000187 0.000179 0.000114 0.000602 0.0016402 0.000068 0.000052 0.0000114 0.000078 0.000082 0.000083 0.000109 0.0000173 +2015 4 13 12 57125.50 0.021402 0.412715 -0.5917702 0.000214 0.000148 0.000460 0.000774 0.0017785 0.000068 0.000052 0.0000114 0.000078 0.000082 0.000082 0.000108 0.0000169 +2015 4 14 12 57126.50 0.021687 0.413729 -0.5936120 0.000209 0.000118 0.000384 0.000823 0.0018959 0.000068 0.000052 0.0000114 0.000078 0.000082 0.000082 0.000108 0.0000168 +2015 4 15 12 57127.50 0.022572 0.414676 -0.5955547 0.000144 0.000094 0.001381 0.001148 0.0019830 0.000069 0.000052 0.0000113 0.000078 0.000082 0.000083 0.000111 0.0000168 +2015 4 16 12 57128.50 0.023792 0.416133 -0.5975549 0.000079 0.000080 0.001026 0.001670 0.0020042 0.000069 0.000053 0.0000113 0.000077 0.000081 0.000084 0.000112 0.0000169 +2015 4 17 12 57129.50 0.024708 0.417419 -0.5995294 0.000044 0.000087 0.001011 0.000930 0.0019267 0.000069 0.000053 0.0000112 0.000077 0.000081 0.000084 0.000111 0.0000172 +2015 4 18 12 57130.50 0.025782 0.418398 -0.6013771 0.000083 0.000138 0.000955 0.001203 0.0017536 0.000069 0.000053 0.0000112 0.000077 0.000081 0.000084 0.000111 0.0000173 +2015 4 19 12 57131.50 0.026651 0.419336 -0.6030196 0.000138 0.000195 0.001025 0.000800 0.0015238 0.000069 0.000053 0.0000111 0.000077 0.000079 0.000084 0.000112 0.0000172 +2015 4 20 12 57132.50 0.027659 0.420248 -0.6044285 0.000192 0.000245 0.001082 0.001234 0.0012996 0.000069 0.000052 0.0000111 0.000077 0.000079 0.000084 0.000113 0.0000172 +2015 4 21 12 57133.50 0.028470 0.421442 -0.6056360 0.000220 0.000265 0.000691 0.000934 0.0011268 0.000069 0.000052 0.0000112 0.000077 0.000079 0.000083 0.000113 0.0000169 +2015 4 22 12 57134.50 0.029511 0.422041 -0.6067059 0.000213 0.000248 0.001353 0.000762 0.0010249 0.000070 0.000052 0.0000114 0.000078 0.000081 0.000084 0.000114 0.0000161 +2015 4 23 12 57135.50 0.030708 0.423210 -0.6077126 0.000201 0.000240 0.001059 0.001521 0.0010017 0.000069 0.000052 0.0000117 0.000078 0.000083 0.000083 0.000113 0.0000155 +2015 4 24 12 57136.50 0.031659 0.424499 -0.6087335 0.000199 0.000240 0.001061 0.001236 0.0010577 0.000069 0.000052 0.0000120 0.000081 0.000088 0.000083 0.000113 0.0000153 +2015 4 25 12 57137.50 0.032794 0.425663 -0.6098357 0.000242 0.000201 0.001489 0.001210 0.0011535 0.000069 0.000051 0.0000121 0.000083 0.000091 0.000083 0.000113 0.0000154 +2015 4 26 12 57138.50 0.034491 0.426940 -0.6110370 0.000299 0.000146 0.001872 0.001586 0.0012466 0.000068 0.000051 0.0000124 0.000093 0.000102 0.000082 0.000112 0.0000157 +2015 4 27 12 57139.50 0.036115 0.428570 -0.6123215 0.000347 0.000093 0.001733 0.001477 0.0013142 0.000068 0.000051 0.0000125 0.000103 0.000114 0.000082 0.000113 0.0000160 +2015 4 28 12 57140.50 0.037586 0.429921 -0.6136610 0.000346 0.000070 0.001224 0.001402 0.0013585 0.000069 0.000051 0.0000123 0.000103 0.000113 0.000083 0.000113 0.0000165 +2015 4 29 12 57141.50 0.038573 0.431082 -0.6150361 0.000281 0.000090 0.000964 0.001029 0.0013915 0.000069 0.000052 0.0000118 0.000087 0.000096 0.000083 0.000114 0.0000168 +2015 4 30 12 57142.50 0.039430 0.431912 -0.6164351 0.000220 0.000115 0.000916 0.000900 0.0014045 0.000069 0.000051 0.0000114 0.000081 0.000089 0.000083 0.000114 0.0000166 +2015 5 1 12 57143.50 0.040000 0.432794 -0.6178291 0.000208 0.000132 0.000420 0.000967 0.0013745 0.000069 0.000052 0.0000113 0.000081 0.000089 0.000083 0.000115 0.0000161 +2015 5 2 12 57144.50 0.040185 0.433672 -0.6191696 0.000215 0.000142 0.000185 0.001100 0.0012958 0.000070 0.000052 0.0000114 0.000080 0.000086 0.000084 0.000116 0.0000157 +2015 5 3 12 57145.50 0.040493 0.434843 -0.6204147 0.000229 0.000145 0.000644 0.001451 0.0011883 0.000069 0.000052 0.0000115 0.000080 0.000083 0.000083 0.000116 0.0000155 +2015 5 4 12 57146.50 0.041202 0.436266 -0.6215500 0.000244 0.000136 0.000902 0.001512 0.0010830 0.000070 0.000052 0.0000116 0.000079 0.000082 0.000083 0.000116 0.0000154 +2015 5 5 12 57147.50 0.041923 0.437648 -0.6225936 0.000241 0.000106 0.000822 0.001374 0.0010121 0.000070 0.000052 0.0000122 0.000082 0.000084 0.000083 0.000116 0.0000157 +2015 5 6 12 57148.50 0.042806 0.438976 -0.6235939 0.000218 0.000056 0.001216 0.001425 0.0010062 0.000070 0.000052 0.0000139 0.000104 0.000107 0.000084 0.000117 0.0000159 +2015 5 7 12 57149.50 0.044316 0.440230 -0.6246201 0.000206 0.000038 0.001754 0.001346 0.0010588 0.000070 0.000052 0.0000149 0.000115 0.000117 0.000084 0.000116 0.0000158 +2015 5 8 12 57150.50 0.046107 0.441532 -0.6257217 0.000208 0.000051 0.001920 0.001437 0.0011490 0.000069 0.000052 0.0000141 0.000097 0.000096 0.000083 0.000116 0.0000158 +2015 5 9 12 57151.50 0.048157 0.442931 -0.6269304 0.000212 0.000055 0.002003 0.001586 0.0012726 0.000070 0.000052 0.0000134 0.000094 0.000094 0.000083 0.000116 0.0000160 +2015 5 10 12 57152.50 0.049838 0.444202 -0.6282781 0.000216 0.000056 0.001533 0.000921 0.0014248 0.000069 0.000051 0.0000125 0.000087 0.000088 0.000083 0.000115 0.0000161 +2015 5 11 12 57153.50 0.051421 0.444817 -0.6297871 0.000216 0.000056 0.001845 0.000645 0.0015943 0.000069 0.000052 0.0000120 0.000081 0.000083 0.000083 0.000115 0.0000161 +2015 5 12 12 57154.50 0.053032 0.445392 -0.6314641 0.000204 0.000060 0.001621 0.000498 0.0017561 0.000069 0.000052 0.0000119 0.000081 0.000083 0.000083 0.000114 0.0000162 +2015 5 13 12 57155.50 0.054650 0.445847 -0.6332848 0.000175 0.000071 0.001667 0.000579 0.0018762 0.000069 0.000052 0.0000126 0.000083 0.000086 0.000083 0.000113 0.0000166 +2015 5 14 12 57156.50 0.056385 0.446413 -0.6351916 0.000148 0.000088 0.001917 0.000732 0.0019261 0.000069 0.000052 0.0000136 0.000088 0.000092 0.000084 0.000114 0.0000168 +2015 5 15 12 57157.50 0.058167 0.447052 -0.6371035 0.000135 0.000116 0.001714 0.000412 0.0018812 0.000070 0.000053 0.0000141 0.000089 0.000094 0.000084 0.000115 0.0000166 +2015 5 16 12 57158.50 0.060185 0.447141 -0.6389253 0.000156 0.000170 0.002437 0.000148 0.0017482 0.000070 0.000053 0.0000136 0.000088 0.000093 0.000085 0.000114 0.0000165 +2015 5 17 12 57159.50 0.062669 0.447715 -0.6405869 0.000184 0.000222 0.002295 0.001086 0.0015699 0.000069 0.000052 0.0000126 0.000084 0.000089 0.000084 0.000113 0.0000164 +2015 5 18 12 57160.50 0.064827 0.448592 -0.6420692 0.000211 0.000260 0.002065 0.000912 0.0013997 0.000069 0.000053 0.0000119 0.000081 0.000087 0.000085 0.000112 0.0000167 +2015 5 19 12 57161.50 0.066820 0.449143 -0.6434017 0.000221 0.000261 0.002070 0.000260 0.0012781 0.000069 0.000053 0.0000117 0.000081 0.000087 0.000084 0.000111 0.0000169 +2015 5 20 12 57162.50 0.069031 0.449357 -0.6446391 0.000204 0.000209 0.002265 0.000223 0.0012071 0.000068 0.000052 0.0000116 0.000080 0.000088 0.000084 0.000111 0.0000170 +2015 5 21 12 57163.50 0.070935 0.449541 -0.6458221 0.000182 0.000144 0.001713 -0.000011 0.0011622 0.000068 0.000052 0.0000115 0.000079 0.000089 0.000083 0.000111 0.0000169 +2015 5 22 12 57164.50 0.072846 0.449585 -0.6469635 0.000168 0.000085 0.002288 0.000273 0.0011170 0.000068 0.000052 0.0000114 0.000079 0.000089 0.000084 0.000112 0.0000167 +2015 5 23 12 57165.50 0.074959 0.450282 -0.6480576 0.000183 0.000064 0.001751 0.001014 0.0010665 0.000068 0.000052 0.0000115 0.000079 0.000089 0.000084 0.000112 0.0000165 +2015 5 24 12 57166.50 0.076557 0.451000 -0.6491031 0.000203 0.000052 0.001855 0.000387 0.0010226 0.000068 0.000052 0.0000116 0.000080 0.000087 0.000083 0.000112 0.0000163 +2015 5 25 12 57167.50 0.078432 0.451652 -0.6501142 0.000221 0.000044 0.001759 0.000847 0.0010044 0.000067 0.000052 0.0000117 0.000082 0.000086 0.000082 0.000111 0.0000160 +2015 5 26 12 57168.50 0.080091 0.452416 -0.6511198 0.000230 0.000036 0.001791 0.000583 0.0010127 0.000068 0.000052 0.0000119 0.000082 0.000085 0.000082 0.000112 0.0000156 +2015 5 27 12 57169.50 0.082082 0.452927 -0.6521386 0.000219 0.000020 0.002188 0.000667 0.0010293 0.000068 0.000052 0.0000122 0.000083 0.000086 0.000083 0.000112 0.0000156 +2015 5 28 12 57170.50 0.084126 0.453635 -0.6531645 0.000184 -0.000008 0.002088 0.000626 0.0010161 0.000069 0.000052 0.0000129 0.000088 0.000094 0.000084 0.000114 0.0000158 +2015 5 29 12 57171.50 0.086274 0.454256 -0.6541555 0.000155 -0.000021 0.002229 0.000709 0.0009552 0.000069 0.000052 0.0000134 0.000091 0.000100 0.000084 0.000114 0.0000161 +2015 5 30 12 57172.50 0.088211 0.455077 -0.6550670 0.000147 0.000000 0.001913 0.000757 0.0008613 0.000069 0.000053 0.0000130 0.000089 0.000097 0.000084 0.000115 0.0000162 +2015 5 31 12 57173.50 0.090110 0.455724 -0.6558781 0.000148 0.000031 0.001838 0.000533 0.0007600 0.000069 0.000053 0.0000121 0.000082 0.000089 0.000084 0.000115 0.0000161 +2015 6 1 12 57174.50 0.091788 0.456257 -0.6565934 0.000155 0.000063 0.001545 0.000585 0.0006758 0.000069 0.000053 0.0000115 0.000078 0.000084 0.000084 0.000115 0.0000157 +2015 6 2 12 57175.50 0.092899 0.456765 -0.6572397 0.000162 0.000074 0.000940 0.000330 0.0006257 0.000069 0.000053 0.0000113 0.000077 0.000083 0.000084 0.000114 0.0000152 +2015 6 3 12 57176.50 0.093612 0.457014 -0.6578569 0.000165 0.000051 0.000690 0.000211 0.0006177 0.000069 0.000053 0.0000114 0.000076 0.000083 0.000084 0.000115 0.0000150 +2015 6 4 12 57177.50 0.094339 0.457012 -0.6584900 0.000170 0.000021 0.001102 -0.000230 0.0006580 0.000069 0.000053 0.0000116 0.000075 0.000082 0.000085 0.000115 0.0000151 +2015 6 5 12 57178.50 0.095536 0.456873 -0.6591873 0.000178 -0.000006 0.001251 -0.000115 0.0007447 0.000069 0.000053 0.0000121 0.000074 0.000081 0.000084 0.000114 0.0000150 +2015 6 6 12 57179.50 0.096462 0.456799 -0.6599875 0.000195 -0.000014 0.000680 -0.000314 0.0008584 0.000069 0.000053 0.0000140 0.000074 0.000081 0.000084 0.000115 0.0000151 +2015 6 7 12 57180.50 0.096910 0.456431 -0.6609033 0.000213 -0.000019 0.000369 -0.000602 0.0009698 0.000069 0.000053 0.0000180 0.000074 0.000081 0.000085 0.000115 0.0000156 +2015 6 8 12 57181.50 0.097629 0.455838 -0.6619175 0.000228 -0.000024 0.001073 -0.000622 0.0010504 0.000069 0.000053 0.0000184 0.000074 0.000079 0.000085 0.000115 0.0000157 +2015 6 9 12 57182.50 0.098627 0.455506 -0.6629868 0.000238 -0.000032 0.001060 -0.000360 0.0010742 0.000068 0.000053 0.0000140 0.000074 0.000079 0.000084 0.000114 0.0000158 +2015 6 10 12 57183.50 0.099779 0.455302 -0.6640485 0.000238 -0.000049 0.001514 -0.000185 0.0010380 0.000068 0.000053 0.0000121 0.000075 0.000079 0.000084 0.000113 0.0000161 +2015 6 11 12 57184.50 0.101426 0.455229 -0.6650455 0.000225 -0.000075 0.002001 -0.000154 0.0009478 0.000069 0.000053 0.0000115 0.000075 0.000081 0.000085 0.000115 0.0000164 +2015 6 12 12 57185.50 0.103501 0.455053 -0.6659284 0.000214 -0.000091 0.002468 -0.000351 0.0008130 0.000069 0.000053 0.0000115 0.000076 0.000082 0.000085 0.000114 0.0000167 +2015 6 13 12 57186.50 0.105984 0.454812 -0.6666596 0.000212 -0.000080 0.002722 -0.000262 0.0006463 0.000069 0.000053 0.0000117 0.000078 0.000086 0.000085 0.000114 0.0000168 +2015 6 14 12 57187.50 0.108633 0.454593 -0.6672187 0.000214 -0.000058 0.002732 -0.000367 0.0004729 0.000069 0.000053 0.0000122 0.000087 0.000099 0.000085 0.000115 0.0000168 +2015 6 15 12 57188.50 0.111293 0.454310 -0.6676148 0.000217 -0.000032 0.002600 -0.000373 0.0003249 0.000069 0.000053 0.0000127 0.000096 0.000113 0.000085 0.000115 0.0000166 +2015 6 16 12 57189.50 0.113836 0.453849 -0.6678876 0.000217 -0.000014 0.002612 -0.000601 0.0002293 0.000069 0.000053 0.0000127 0.000097 0.000114 0.000084 0.000112 0.0000166 +2015 6 17 12 57190.50 0.116114 0.453523 -0.6680984 0.000213 -0.000012 0.002028 -0.000233 0.0002039 0.000069 0.000053 0.0000122 0.000087 0.000099 0.000084 0.000112 0.0000167 +2015 6 18 12 57191.50 0.118067 0.453332 -0.6683189 0.000209 -0.000012 0.002230 -0.000378 0.0002479 0.000070 0.000054 0.0000116 0.000077 0.000085 0.000085 0.000113 0.0000169 +2015 6 19 12 57192.50 0.120255 0.453124 -0.6686132 0.000208 -0.000009 0.002141 -0.000228 0.0003439 0.000068 0.000053 0.0000114 0.000075 0.000082 0.000084 0.000110 0.0000167 +2015 6 20 12 57193.50 0.122244 0.452961 -0.6690162 0.000218 0.000010 0.001970 -0.000097 0.0004633 0.000068 0.000052 0.0000116 0.000077 0.000084 0.000083 0.000109 0.0000162 +2015 6 21 12 57194.50 0.124123 0.452968 -0.6695429 0.000225 0.000022 0.001958 -0.000102 0.0005871 0.000068 0.000052 0.0000122 0.000082 0.000091 0.000082 0.000109 0.0000162 +2015 6 22 12 57195.50 0.126115 0.452608 -0.6701865 0.000222 0.000020 0.002094 -0.000535 0.0006973 0.000068 0.000053 0.0000128 0.000086 0.000096 0.000083 0.000110 0.0000163 +2015 6 23 12 57196.50 0.128169 0.452171 -0.6709303 0.000193 -0.000026 0.002092 -0.000431 0.0007824 0.000070 0.000054 0.0000139 0.000090 0.000100 0.000084 0.000112 0.0000165 +2015 6 24 12 57197.50 0.130321 0.451768 -0.6717384 0.000138 -0.000110 0.002337 -0.000488 0.0008240 0.000069 0.000053 0.0000166 0.000118 0.000132 0.000084 0.000112 0.0000165 +2015 6 25 12 57198.50 0.132779 0.451310 -0.6725615 0.000126 -0.000107 0.002714 -0.000481 0.0008245 0.000069 0.000052 0.0000175 0.000127 0.000146 0.000083 0.000111 0.0000166 +2015 6 26 12 57199.50 0.135144 0.450995 -0.6733823 0.000160 -0.000022 0.002133 -0.000426 0.0008201 0.000068 0.000052 0.0000152 0.000096 0.000111 0.000082 0.000111 0.0000165 +2015 6 27 12 57200.50 0.137093 0.450572 -0.6742040 0.000181 -0.000011 0.001902 -0.000481 0.0008096 0.000068 0.000052 0.0000139 0.000092 0.000106 0.000082 0.000112 0.0000160 +2015 6 28 12 57201.50 0.138722 0.450045 -0.6749911 0.000194 -0.000039 0.001623 -0.000751 0.0007562 0.000068 0.000051 0.0000128 0.000083 0.000093 0.000082 0.000111 0.0000159 +2015 6 29 12 57202.50 0.140196 0.449266 -0.6757063 0.000199 -0.000086 0.001414 -0.000769 0.0006741 0.000068 0.000051 0.0000121 0.000077 0.000086 0.000082 0.000111 0.0000161 +2015 6 30 12 57203.50 0.141496 0.448561 -0.6763386 0.000196 -0.000121 0.001316 -0.000813 0.0006038 0.000068 0.000051 0.0000119 0.000077 0.000085 0.000082 0.000110 0.0000162 +2015 7 1 12 57204.50 0.142884 0.447717 0.3230687 0.000184 -0.000129 0.001491 -0.000773 0.0006013 0.000068 0.000051 0.0000121 0.000080 0.000089 0.000082 0.000110 0.0000168 +2015 7 2 12 57205.50 0.144332 0.447005 0.3224276 0.000172 -0.000131 0.001439 -0.000747 0.0006920 0.000068 0.000051 0.0000123 0.000083 0.000091 0.000082 0.000110 0.0000170 +2015 7 3 12 57206.50 0.145807 0.446139 0.3216593 0.000164 -0.000129 0.001566 -0.001025 0.0008468 0.000068 0.000051 0.0000123 0.000082 0.000091 0.000082 0.000111 0.0000170 +2015 7 4 12 57207.50 0.147551 0.445097 0.3207253 0.000160 -0.000123 0.002038 -0.000930 0.0010189 0.000068 0.000051 0.0000122 0.000081 0.000089 0.000082 0.000112 0.0000172 +2015 7 5 12 57208.50 0.149803 0.444364 0.3196285 0.000158 -0.000118 0.002314 -0.000539 0.0011700 0.000068 0.000051 0.0000120 0.000078 0.000086 0.000082 0.000112 0.0000171 +2015 7 6 12 57209.50 0.151906 0.443927 0.3184024 0.000160 -0.000115 0.002023 -0.000285 0.0012768 0.000068 0.000051 0.0000119 0.000078 0.000085 0.000082 0.000112 0.0000164 +2015 7 7 12 57210.50 0.153862 0.443787 0.3170943 0.000170 -0.000121 0.001971 -0.000044 0.0013305 0.000069 0.000052 0.0000120 0.000078 0.000085 0.000083 0.000112 0.0000157 +2015 7 8 12 57211.50 0.155874 0.443677 0.3157607 0.000188 -0.000139 0.001935 -0.000262 0.0013244 0.000069 0.000052 0.0000122 0.000079 0.000086 0.000083 0.000112 0.0000154 +2015 7 9 12 57212.50 0.157699 0.443189 0.3144642 0.000203 -0.000154 0.001806 -0.000634 0.0012536 0.000068 0.000052 0.0000124 0.000082 0.000087 0.000083 0.000111 0.0000153 +2015 7 10 12 57213.50 0.159557 0.442326 0.3132719 0.000208 -0.000156 0.002110 -0.000980 0.0011175 0.000068 0.000052 0.0000125 0.000083 0.000088 0.000082 0.000110 0.0000153 +2015 7 11 12 57214.50 0.161765 0.441207 0.3122451 0.000187 -0.000132 0.002467 -0.001249 0.0009283 0.000068 0.000052 0.0000122 0.000081 0.000086 0.000083 0.000111 0.0000154 +2015 7 12 12 57215.50 0.164372 0.439995 0.3114244 0.000163 -0.000102 0.002662 -0.001117 0.0007177 0.000068 0.000052 0.0000116 0.000076 0.000082 0.000082 0.000110 0.0000158 +2015 7 13 12 57216.50 0.167017 0.438952 0.3108045 0.000144 -0.000073 0.002607 -0.000891 0.0005371 0.000068 0.000052 0.0000112 0.000073 0.000080 0.000082 0.000109 0.0000159 +2015 7 14 12 57217.50 0.169399 0.438007 0.3103303 0.000143 -0.000055 0.002220 -0.000942 0.0004298 0.000067 0.000052 0.0000110 0.000072 0.000079 0.000081 0.000108 0.0000160 +2015 7 15 12 57218.50 0.171400 0.436982 0.3099174 0.000173 -0.000056 0.001815 -0.001097 0.0004082 0.000067 0.000052 0.0000109 0.000073 0.000079 0.000082 0.000107 0.0000163 +2015 7 16 12 57219.50 0.173025 0.435604 0.3094911 0.000206 -0.000067 0.001622 -0.001434 0.0004447 0.000067 0.000052 0.0000108 0.000073 0.000078 0.000082 0.000107 0.0000168 +2015 7 17 12 57220.50 0.174790 0.434378 0.3090201 0.000228 -0.000095 0.002004 -0.001067 0.0004898 0.000068 0.000052 0.0000108 0.000073 0.000078 0.000082 0.000108 0.0000171 +2015 7 18 12 57221.50 0.176653 0.433416 0.3085185 0.000223 -0.000151 0.001713 -0.000991 0.0005057 0.000068 0.000052 0.0000110 0.000074 0.000079 0.000082 0.000109 0.0000170 +2015 7 19 12 57222.50 0.178130 0.432470 0.3080226 0.000208 -0.000208 0.001516 -0.001041 0.0004880 0.000068 0.000052 0.0000115 0.000077 0.000082 0.000082 0.000109 0.0000170 +2015 7 20 12 57223.50 0.179548 0.431744 0.3075495 0.000185 -0.000252 0.001115 -0.000579 0.0004662 0.000068 0.000053 0.0000118 0.000079 0.000085 0.000083 0.000110 0.0000168 +2015 7 21 12 57224.50 0.180591 0.430889 0.3070873 0.000156 -0.000260 0.001029 -0.001029 0.0004666 0.000069 0.000053 0.0000119 0.000080 0.000086 0.000084 0.000111 0.0000165 +2015 7 22 12 57225.50 0.181605 0.429968 0.3066073 0.000122 -0.000211 0.000834 -0.000822 0.0004937 0.000069 0.000053 0.0000118 0.000082 0.000089 0.000084 0.000111 0.0000161 +2015 7 23 12 57226.50 0.182550 0.429192 0.3060946 0.000094 -0.000155 0.001200 -0.000770 0.0005234 0.000069 0.000053 0.0000117 0.000085 0.000095 0.000085 0.000111 0.0000159 +2015 7 24 12 57227.50 0.183763 0.428336 0.3055644 0.000087 -0.000113 0.001427 -0.001028 0.0005237 0.000068 0.000052 0.0000117 0.000086 0.000097 0.000084 0.000110 0.0000159 +2015 7 25 12 57228.50 0.185249 0.427360 0.3050601 0.000125 -0.000116 0.001476 -0.001074 0.0004763 0.000068 0.000053 0.0000121 0.000086 0.000096 0.000084 0.000111 0.0000163 +2015 7 26 12 57229.50 0.186571 0.426170 0.3046238 0.000170 -0.000132 0.001479 -0.001509 0.0004009 0.000069 0.000053 0.0000131 0.000086 0.000094 0.000085 0.000112 0.0000168 +2015 7 27 12 57230.50 0.188270 0.424729 0.3042564 0.000210 -0.000157 0.001976 -0.001451 0.0003480 0.000069 0.000054 0.0000140 0.000086 0.000093 0.000086 0.000113 0.0000167 +2015 7 28 12 57231.50 0.190219 0.423564 0.3039084 0.000223 -0.000182 0.001862 -0.001151 0.0003624 0.000069 0.000054 0.0000142 0.000086 0.000093 0.000086 0.000113 0.0000160 +2015 7 29 12 57232.50 0.192051 0.422311 0.3035035 0.000190 -0.000201 0.001843 -0.001313 0.0004571 0.000069 0.000054 0.0000135 0.000084 0.000091 0.000086 0.000114 0.0000157 +2015 7 30 12 57233.50 0.193805 0.420917 0.3029695 0.000148 -0.000218 0.001745 -0.001453 0.0006143 0.000069 0.000053 0.0000129 0.000081 0.000088 0.000086 0.000114 0.0000159 +2015 7 31 12 57234.50 0.195454 0.419461 0.3022629 0.000111 -0.000228 0.001660 -0.001561 0.0008010 0.000069 0.000053 0.0000130 0.000080 0.000087 0.000085 0.000113 0.0000161 +2015 8 1 12 57235.50 0.197247 0.418013 0.3013684 0.000105 -0.000227 0.001912 -0.001337 0.0009893 0.000069 0.000053 0.0000149 0.000080 0.000087 0.000085 0.000113 0.0000161 +2015 8 2 12 57236.50 0.199142 0.416599 0.3002948 0.000105 -0.000220 0.002036 -0.001582 0.0011506 0.000069 0.000054 0.0000191 0.000079 0.000086 0.000086 0.000114 0.0000160 +2015 8 3 12 57237.50 0.201220 0.415200 0.2990889 0.000107 -0.000209 0.002141 -0.001179 0.0012482 0.000070 0.000054 0.0000196 0.000077 0.000085 0.000086 0.000114 0.0000159 +2015 8 4 12 57238.50 0.203384 0.414053 0.2978254 0.000107 -0.000196 0.002410 -0.001234 0.0012591 0.000070 0.000054 0.0000151 0.000077 0.000084 0.000086 0.000114 0.0000162 +2015 8 5 12 57239.50 0.205822 0.412874 0.2966005 0.000098 -0.000182 0.002424 -0.001196 0.0011780 0.000069 0.000054 0.0000130 0.000077 0.000085 0.000085 0.000112 0.0000166 +2015 8 6 12 57240.50 0.207842 0.411702 0.2954894 0.000077 -0.000169 0.001879 -0.001257 0.0010399 0.000069 0.000054 0.0000125 0.000081 0.000088 0.000086 0.000112 0.0000167 +2015 8 7 12 57241.50 0.209452 0.410596 0.2945262 0.000058 -0.000158 0.001306 -0.001074 0.0008873 0.000069 0.000054 0.0000124 0.000084 0.000089 0.000086 0.000111 0.0000168 +2015 8 8 12 57242.50 0.210364 0.409375 0.2937096 0.000044 -0.000151 0.000709 -0.001581 0.0007487 0.000068 0.000054 0.0000122 0.000083 0.000089 0.000085 0.000110 0.0000168 +2015 8 9 12 57243.50 0.210975 0.407581 0.2930182 0.000036 -0.000147 0.000522 -0.001980 0.0006418 0.000070 0.000055 0.0000120 0.000081 0.000086 0.000087 0.000112 0.0000169 +2015 8 10 12 57244.50 0.211678 0.405777 0.2924109 0.000036 -0.000146 0.000834 -0.001708 0.0005838 0.000069 0.000054 0.0000117 0.000078 0.000082 0.000086 0.000111 0.0000170 +2015 8 11 12 57245.50 0.212469 0.404181 0.2918323 0.000046 -0.000147 0.000726 -0.001503 0.0005838 0.000069 0.000053 0.0000116 0.000077 0.000081 0.000085 0.000112 0.0000162 +2015 8 12 12 57246.50 0.213220 0.402416 0.2912259 0.000070 -0.000150 0.000934 -0.002021 0.0006348 0.000069 0.000053 0.0000117 0.000077 0.000082 0.000085 0.000113 0.0000152 +2015 8 13 12 57247.50 0.214552 0.400494 0.2905521 0.000107 -0.000156 0.001646 -0.001704 0.0007117 0.000069 0.000053 0.0000119 0.000080 0.000084 0.000084 0.000113 0.0000152 +2015 8 14 12 57248.50 0.216168 0.398810 0.2898022 0.000142 -0.000159 0.001695 -0.001637 0.0007818 0.000069 0.000053 0.0000120 0.000082 0.000085 0.000084 0.000114 0.0000157 +2015 8 15 12 57249.50 0.217785 0.397396 0.2889978 0.000166 -0.000155 0.001356 -0.001220 0.0008224 0.000069 0.000053 0.0000119 0.000081 0.000084 0.000084 0.000114 0.0000161 +2015 8 16 12 57250.50 0.218920 0.396209 0.2881689 0.000181 -0.000150 0.001069 -0.001374 0.0008336 0.000069 0.000053 0.0000114 0.000076 0.000080 0.000084 0.000115 0.0000161 +2015 8 17 12 57251.50 0.219768 0.394706 0.2873397 0.000188 -0.000149 0.000517 -0.001436 0.0008273 0.000070 0.000053 0.0000111 0.000073 0.000077 0.000084 0.000115 0.0000159 +2015 8 18 12 57252.50 0.219713 0.393231 0.2865173 0.000181 -0.000161 -0.000382 -0.001626 0.0008212 0.000069 0.000052 0.0000111 0.000073 0.000077 0.000083 0.000115 0.0000158 +2015 8 19 12 57253.50 0.219452 0.391353 0.2856950 0.000160 -0.000192 0.000079 -0.001948 0.0008223 0.000070 0.000053 0.0000114 0.000076 0.000080 0.000084 0.000116 0.0000156 +2015 8 20 12 57254.50 0.219775 0.389502 0.2848713 0.000137 -0.000220 0.000606 -0.001922 0.0008196 0.000070 0.000053 0.0000119 0.000081 0.000084 0.000084 0.000117 0.0000155 +2015 8 21 12 57255.50 0.220646 0.387431 0.2840581 0.000122 -0.000220 0.001184 -0.002106 0.0008016 0.000070 0.000053 0.0000121 0.000082 0.000085 0.000084 0.000117 0.0000157 +2015 8 22 12 57256.50 0.221794 0.385564 0.2832714 0.000135 -0.000147 0.001092 -0.001790 0.0007755 0.000071 0.000053 0.0000120 0.000082 0.000085 0.000085 0.000118 0.0000163 +2015 8 23 12 57257.50 0.222673 0.383736 0.2825013 0.000145 -0.000081 0.000868 -0.001878 0.0007707 0.000071 0.000053 0.0000116 0.000079 0.000083 0.000086 0.000119 0.0000167 +2015 8 24 12 57258.50 0.223593 0.381982 0.2817174 0.000141 -0.000051 0.001124 -0.001742 0.0008025 0.000072 0.000053 0.0000114 0.000078 0.000083 0.000086 0.000120 0.0000168 +2015 8 25 12 57259.50 0.224725 0.380140 0.2808808 0.000102 -0.000098 0.001356 -0.001961 0.0008727 0.000072 0.000054 0.0000112 0.000078 0.000084 0.000086 0.000120 0.0000168 +2015 8 26 12 57260.50 0.225770 0.378170 0.2799596 0.000068 -0.000192 0.000730 -0.001946 0.0009759 0.000072 0.000053 0.0000110 0.000079 0.000087 0.000086 0.000120 0.0000168 +2015 8 27 12 57261.50 0.226050 0.376087 0.2789175 0.000122 -0.000242 0.000146 -0.002344 0.0011240 0.000071 0.000053 0.0000108 0.000077 0.000082 0.000085 0.000119 0.0000169 +2015 8 28 12 57262.50 0.226159 0.374026 0.2776961 0.000189 -0.000263 0.000054 -0.001604 0.0013269 0.000070 0.000052 0.0000108 0.000076 0.000080 0.000084 0.000118 0.0000168 +2015 8 29 12 57263.50 0.226034 0.372592 0.2762586 0.000164 -0.000269 -0.000091 -0.001408 0.0015412 0.000070 0.000052 0.0000110 0.000077 0.000081 0.000083 0.000118 0.0000165 +2015 8 30 12 57264.50 0.226042 0.371231 0.2746304 0.000113 -0.000262 0.000289 -0.001259 0.0017027 0.000070 0.000052 0.0000115 0.000081 0.000084 0.000084 0.000118 0.0000162 +2015 8 31 12 57265.50 0.226185 0.370145 0.2728826 0.000052 -0.000246 0.000077 -0.001058 0.0017774 0.000070 0.000052 0.0000120 0.000084 0.000088 0.000083 0.000118 0.0000160 +2015 9 1 12 57266.50 0.226253 0.369178 0.2711068 0.000004 -0.000227 0.000087 -0.000976 0.0017592 0.000070 0.000052 0.0000121 0.000086 0.000089 0.000082 0.000117 0.0000160 +2015 9 2 12 57267.50 0.226456 0.367979 0.2693882 -0.000011 -0.000212 0.000468 -0.001318 0.0016660 0.000070 0.000052 0.0000121 0.000088 0.000095 0.000083 0.000117 0.0000160 +2015 9 3 12 57268.50 0.227043 0.366414 0.2677880 -0.000014 -0.000197 0.000878 -0.001648 0.0015270 0.000070 0.000051 0.0000120 0.000093 0.000107 0.000083 0.000117 0.0000161 +2015 9 4 12 57269.50 0.227757 0.365136 0.2663377 0.000004 -0.000184 0.000535 -0.001004 0.0013725 0.000070 0.000052 0.0000124 0.000094 0.000111 0.000083 0.000117 0.0000164 +2015 9 5 12 57270.50 0.227937 0.364017 0.2650389 0.000070 -0.000176 0.000143 -0.001382 0.0012298 0.000070 0.000052 0.0000141 0.000091 0.000106 0.000084 0.000118 0.0000167 +2015 9 6 12 57271.50 0.228055 0.362545 0.2638683 0.000135 -0.000170 0.000144 -0.001465 0.0011192 0.000070 0.000052 0.0000175 0.000083 0.000092 0.000084 0.000117 0.0000171 +2015 9 7 12 57272.50 0.228043 0.361030 0.2627880 0.000181 -0.000167 -0.000159 -0.001614 0.0010494 0.000071 0.000052 0.0000173 0.000076 0.000081 0.000084 0.000118 0.0000174 +2015 9 8 12 57273.50 0.227692 0.359326 0.2617572 0.000190 -0.000164 -0.000482 -0.001901 0.0010241 0.000070 0.000052 0.0000134 0.000074 0.000079 0.000084 0.000117 0.0000174 +2015 9 9 12 57274.50 0.227108 0.357379 0.2607252 0.000129 -0.000162 -0.000713 -0.001888 0.0010405 0.000070 0.000052 0.0000116 0.000074 0.000078 0.000083 0.000116 0.0000172 +2015 9 10 12 57275.50 0.226215 0.355501 0.2596722 0.000054 -0.000168 -0.000906 -0.001920 0.0010718 0.000070 0.000052 0.0000112 0.000075 0.000079 0.000084 0.000116 0.0000172 +2015 9 11 12 57276.50 0.225294 0.353467 0.2585771 0.000057 -0.000191 -0.000795 -0.002155 0.0011428 0.000071 0.000053 0.0000114 0.000079 0.000082 0.000084 0.000117 0.0000172 +2015 9 12 12 57277.50 0.224849 0.351273 0.2573629 0.000047 -0.000192 -0.000023 -0.002031 0.0012673 0.000070 0.000052 0.0000117 0.000080 0.000083 0.000084 0.000116 0.0000172 +2015 9 13 12 57278.50 0.224883 0.349403 0.2560508 0.000036 -0.000181 0.000017 -0.001910 0.0013412 0.000070 0.000052 0.0000123 0.000085 0.000087 0.000084 0.000116 0.0000176 +2015 9 14 12 57279.50 0.224739 0.347437 0.2547015 0.000024 -0.000160 -0.000088 -0.001857 0.0013438 0.000070 0.000052 0.0000130 0.000091 0.000092 0.000084 0.000116 0.0000177 +2015 9 15 12 57280.50 0.224502 0.345892 0.2533874 0.000017 -0.000137 -0.000429 -0.001382 0.0012859 0.000070 0.000052 0.0000132 0.000092 0.000093 0.000083 0.000115 0.0000173 +2015 9 16 12 57281.50 0.223942 0.344459 0.2521397 0.000015 -0.000116 -0.000604 -0.001564 0.0012239 0.000069 0.000052 0.0000131 0.000091 0.000092 0.000083 0.000114 0.0000172 +2015 9 17 12 57282.50 0.223077 0.343077 0.2509285 0.000018 -0.000097 -0.001247 -0.001350 0.0012021 0.000069 0.000052 0.0000130 0.000089 0.000089 0.000082 0.000113 0.0000171 +2015 9 18 12 57283.50 0.221898 0.341377 0.2497234 0.000028 -0.000081 -0.000899 -0.001947 0.0012045 0.000069 0.000051 0.0000128 0.000088 0.000088 0.000082 0.000112 0.0000173 +2015 9 19 12 57284.50 0.221054 0.339442 0.2485132 0.000049 -0.000071 -0.000835 -0.001808 0.0012068 0.000069 0.000051 0.0000123 0.000085 0.000086 0.000082 0.000111 0.0000180 +2015 9 20 12 57285.50 0.220020 0.337422 0.2473118 0.000072 -0.000069 -0.001031 -0.002268 0.0011965 0.000069 0.000051 0.0000115 0.000078 0.000081 0.000082 0.000111 0.0000179 +2015 9 21 12 57286.50 0.219151 0.335151 0.2461170 0.000092 -0.000082 -0.000608 -0.002138 0.0012033 0.000068 0.000051 0.0000110 0.000074 0.000077 0.000082 0.000110 0.0000174 +2015 9 22 12 57287.50 0.218568 0.333067 0.2448909 0.000103 -0.000120 -0.000272 -0.002113 0.0012651 0.000068 0.000051 0.0000108 0.000074 0.000077 0.000081 0.000108 0.0000174 +2015 9 23 12 57288.50 0.218277 0.331042 0.2435623 0.000097 -0.000192 -0.000251 -0.002080 0.0014081 0.000067 0.000051 0.0000106 0.000073 0.000077 0.000080 0.000106 0.0000172 +2015 9 24 12 57289.50 0.218151 0.329056 0.2420491 0.000085 -0.000262 0.000071 -0.001827 0.0016236 0.000067 0.000051 0.0000104 0.000073 0.000077 0.000081 0.000106 0.0000171 +2015 9 25 12 57290.50 0.218201 0.327523 0.2403044 0.000065 -0.000302 -0.000040 -0.001474 0.0018584 0.000067 0.000051 0.0000104 0.000073 0.000077 0.000081 0.000106 0.0000173 +2015 9 26 12 57291.50 0.217981 0.325913 0.2383459 0.000034 -0.000272 -0.000354 -0.001623 0.0020418 0.000067 0.000051 0.0000104 0.000072 0.000077 0.000082 0.000106 0.0000175 +2015 9 27 12 57292.50 0.217044 0.324374 0.2362559 0.000004 -0.000224 -0.001389 -0.001665 0.0021213 0.000068 0.000052 0.0000104 0.000072 0.000076 0.000083 0.000107 0.0000174 +2015 9 28 12 57293.50 0.215360 0.322388 0.2341448 -0.000021 -0.000172 -0.001847 -0.002229 0.0020872 0.000068 0.000052 0.0000104 0.000072 0.000076 0.000083 0.000108 0.0000173 +2015 9 29 12 57294.50 0.213277 0.320017 0.2321147 -0.000028 -0.000142 -0.002126 -0.002465 0.0019617 0.000068 0.000053 0.0000105 0.000072 0.000076 0.000082 0.000108 0.0000178 +2015 9 30 12 57295.50 0.211151 0.317372 0.2302403 -0.000010 -0.000155 -0.001885 -0.002663 0.0017777 0.000069 0.000053 0.0000107 0.000074 0.000078 0.000084 0.000110 0.0000179 +2015 10 1 12 57296.50 0.209759 0.314774 0.2285660 0.000013 -0.000171 -0.000637 -0.002179 0.0015677 0.000069 0.000053 0.0000110 0.000077 0.000081 0.000084 0.000110 0.0000179 +2015 10 2 12 57297.50 0.209395 0.312872 0.2271007 0.000041 -0.000169 -0.000097 -0.001756 0.0013712 0.000068 0.000053 0.0000113 0.000078 0.000082 0.000083 0.000109 0.0000176 +2015 10 3 12 57298.50 0.209219 0.311038 0.2258073 0.000079 -0.000116 -0.000190 -0.001827 0.0012372 0.000068 0.000052 0.0000115 0.000079 0.000083 0.000083 0.000109 0.0000176 +2015 10 4 12 57299.50 0.208816 0.309028 0.2245953 0.000098 -0.000068 -0.000301 -0.001996 0.0012026 0.000068 0.000052 0.0000120 0.000081 0.000084 0.000083 0.000108 0.0000179 +2015 10 5 12 57300.50 0.208444 0.307094 0.2233727 0.000083 -0.000046 -0.000361 -0.001613 0.0012492 0.000067 0.000052 0.0000125 0.000082 0.000085 0.000082 0.000107 0.0000178 +2015 10 6 12 57301.50 0.207815 0.305516 0.2220790 -0.000006 -0.000067 -0.000663 -0.001615 0.0013288 0.000067 0.000052 0.0000136 0.000087 0.000089 0.000081 0.000106 0.0000174 +2015 10 7 12 57302.50 0.207058 0.303935 0.2207189 -0.000023 -0.000180 -0.000708 -0.001383 0.0013848 0.000067 0.000052 0.0000148 0.000101 0.000102 0.000081 0.000106 0.0000168 +2015 10 8 12 57303.50 0.206236 0.302620 0.2193216 0.000140 -0.000281 -0.000888 -0.001136 0.0014270 0.000067 0.000052 0.0000141 0.000091 0.000094 0.000082 0.000108 0.0000168 +2015 10 9 12 57304.50 0.205099 0.301382 0.2178572 0.000139 -0.000093 -0.001400 -0.001181 0.0015132 0.000068 0.000052 0.0000139 0.000081 0.000086 0.000082 0.000109 0.0000168 +2015 10 10 12 57305.50 0.203209 0.299975 0.2162825 0.000155 -0.000033 -0.002106 -0.001487 0.0016182 0.000068 0.000052 0.0000168 0.000080 0.000085 0.000082 0.000110 0.0000167 +2015 10 11 12 57306.50 0.201008 0.298297 0.2146339 0.000168 -0.000037 -0.001986 -0.001751 0.0016667 0.000068 0.000052 0.0000220 0.000078 0.000082 0.000082 0.000110 0.0000164 +2015 10 12 12 57307.50 0.198992 0.296620 0.2129682 0.000173 -0.000078 -0.002005 -0.001563 0.0016611 0.000068 0.000052 0.0000195 0.000074 0.000078 0.000082 0.000110 0.0000161 +2015 10 13 12 57308.50 0.197004 0.294915 0.2113241 0.000165 -0.000128 -0.001922 -0.001729 0.0016115 0.000068 0.000052 0.0000140 0.000073 0.000076 0.000082 0.000110 0.0000163 +2015 10 14 12 57309.50 0.194977 0.293092 0.2097659 0.000134 -0.000145 -0.001961 -0.001835 0.0015080 0.000068 0.000052 0.0000119 0.000073 0.000076 0.000081 0.000110 0.0000164 +2015 10 15 12 57310.50 0.193053 0.291199 0.2083129 0.000078 -0.000111 -0.001797 -0.001603 0.0014188 0.000068 0.000052 0.0000117 0.000075 0.000079 0.000082 0.000110 0.0000160 +2015 10 16 12 57311.50 0.191088 0.289679 0.2069063 0.000030 -0.000084 -0.001881 -0.001374 0.0014054 0.000068 0.000051 0.0000125 0.000076 0.000081 0.000081 0.000110 0.0000156 +2015 10 17 12 57312.50 0.189193 0.288103 0.2054761 0.000007 -0.000081 -0.001686 -0.001766 0.0014532 0.000068 0.000052 0.0000150 0.000075 0.000080 0.000082 0.000111 0.0000154 +2015 10 18 12 57313.50 0.187643 0.286425 0.2039847 -0.000004 -0.000088 -0.001372 -0.001538 0.0015203 0.000069 0.000052 0.0000164 0.000073 0.000077 0.000082 0.000113 0.0000156 +2015 10 19 12 57314.50 0.186325 0.284971 0.2024342 -0.000005 -0.000104 -0.001000 -0.001329 0.0015734 0.000069 0.000052 0.0000132 0.000071 0.000075 0.000082 0.000113 0.0000161 +2015 10 20 12 57315.50 0.185325 0.283729 0.2008433 -0.000002 -0.000124 -0.000859 -0.001071 0.0016143 0.000069 0.000052 0.0000117 0.000071 0.000075 0.000081 0.000112 0.0000164 +2015 10 21 12 57316.50 0.184296 0.282828 0.1992035 0.000003 -0.000144 -0.000916 -0.000859 0.0016874 0.000069 0.000052 0.0000118 0.000074 0.000078 0.000082 0.000113 0.0000165 +2015 10 22 12 57317.50 0.183173 0.281978 0.1974499 0.000006 -0.000162 -0.001288 -0.000879 0.0018342 0.000068 0.000051 0.0000125 0.000080 0.000084 0.000081 0.000113 0.0000164 +2015 10 23 12 57318.50 0.181622 0.280958 0.1955194 0.000005 -0.000167 -0.001642 -0.001204 0.0020283 0.000069 0.000051 0.0000137 0.000082 0.000086 0.000082 0.000113 0.0000164 +2015 10 24 12 57319.50 0.179943 0.279617 0.1933943 -0.000008 -0.000148 -0.001576 -0.001398 0.0022090 0.000069 0.000051 0.0000163 0.000079 0.000083 0.000082 0.000114 0.0000163 +2015 10 25 12 57320.50 0.178563 0.278363 0.1911245 -0.000024 -0.000122 -0.001059 -0.001181 0.0023117 0.000069 0.000052 0.0000169 0.000074 0.000078 0.000082 0.000115 0.0000165 +2015 10 26 12 57321.50 0.177746 0.277240 0.1888058 -0.000040 -0.000094 -0.000509 -0.001101 0.0023050 0.000069 0.000052 0.0000132 0.000071 0.000075 0.000082 0.000115 0.0000166 +2015 10 27 12 57322.50 0.176963 0.276144 0.1865493 -0.000052 -0.000073 -0.000918 -0.001022 0.0021920 0.000069 0.000052 0.0000115 0.000071 0.000075 0.000082 0.000114 0.0000164 +2015 10 28 12 57323.50 0.175535 0.275338 0.1844454 -0.000056 -0.000065 -0.001752 -0.000666 0.0020091 0.000069 0.000052 0.0000115 0.000073 0.000078 0.000082 0.000115 0.0000163 +2015 10 29 12 57324.50 0.173267 0.274566 0.1825373 -0.000056 -0.000061 -0.002531 -0.000993 0.0018097 0.000069 0.000052 0.0000121 0.000076 0.000083 0.000081 0.000114 0.0000161 +2015 10 30 12 57325.50 0.170386 0.273301 0.1808170 -0.000052 -0.000060 -0.002960 -0.001528 0.0016389 0.000069 0.000051 0.0000134 0.000077 0.000084 0.000081 0.000114 0.0000160 +2015 10 31 12 57326.50 0.167456 0.271625 0.1792427 -0.000039 -0.000061 -0.002530 -0.001870 0.0015197 0.000069 0.000052 0.0000174 0.000077 0.000084 0.000082 0.000115 0.0000157 +2015 11 1 12 57327.50 0.165213 0.270021 0.1777597 -0.000024 -0.000063 -0.001902 -0.001424 0.0014534 0.000069 0.000052 0.0000233 0.000075 0.000081 0.000082 0.000115 0.0000157 +2015 11 2 12 57328.50 0.163359 0.268903 0.1763231 -0.000010 -0.000063 -0.001706 -0.000864 0.0014262 0.000069 0.000052 0.0000203 0.000073 0.000078 0.000082 0.000115 0.0000159 +2015 11 3 12 57329.50 0.161687 0.268179 0.1748983 -0.000001 -0.000060 -0.001325 -0.000672 0.0014286 0.000069 0.000052 0.0000143 0.000072 0.000077 0.000082 0.000114 0.0000161 +2015 11 4 12 57330.50 0.160486 0.267672 0.1734594 -0.000001 -0.000050 -0.001012 -0.000402 0.0014546 0.000069 0.000052 0.0000122 0.000072 0.000077 0.000081 0.000112 0.0000161 +2015 11 5 12 57331.50 0.159049 0.267297 0.1719836 -0.000012 -0.000033 -0.001683 -0.000567 0.0015052 0.000069 0.000052 0.0000119 0.000074 0.000079 0.000082 0.000113 0.0000161 +2015 11 6 12 57332.50 0.157035 0.266605 0.1704408 -0.000022 -0.000035 -0.002054 -0.000923 0.0015754 0.000069 0.000052 0.0000127 0.000075 0.000080 0.000082 0.000112 0.0000163 +2015 11 7 12 57333.50 0.155208 0.265653 0.1688360 -0.000029 -0.000091 -0.001504 -0.001055 0.0016185 0.000069 0.000052 0.0000154 0.000077 0.000082 0.000082 0.000112 0.0000165 +2015 11 8 12 57334.50 0.153819 0.264589 0.1672220 -0.000032 -0.000142 -0.001284 -0.001169 0.0016035 0.000068 0.000052 0.0000178 0.000081 0.000085 0.000082 0.000112 0.0000170 +2015 11 9 12 57335.50 0.152329 0.263795 0.1656439 -0.000030 -0.000157 -0.001649 -0.000597 0.0015559 0.000069 0.000052 0.0000155 0.000083 0.000087 0.000082 0.000112 0.0000172 +2015 11 10 12 57336.50 0.150562 0.263122 0.1641131 -0.000018 -0.000051 -0.001655 -0.000861 0.0015098 0.000069 0.000052 0.0000142 0.000087 0.000090 0.000082 0.000111 0.0000171 +2015 11 11 12 57337.50 0.148934 0.262298 0.1626197 -0.000017 -0.000132 -0.001525 -0.000901 0.0014723 0.000069 0.000052 0.0000145 0.000099 0.000103 0.000083 0.000111 0.0000169 +2015 11 12 12 57338.50 0.147217 0.261443 0.1611684 -0.000048 -0.000453 -0.001642 -0.001007 0.0014202 0.000068 0.000052 0.0000147 0.000103 0.000109 0.000082 0.000110 0.0000168 +2015 11 13 12 57339.50 0.145411 0.260539 0.1597859 -0.000062 -0.000046 -0.001753 -0.000944 0.0013615 0.000068 0.000052 0.0000140 0.000083 0.000088 0.000082 0.000110 0.0000166 +2015 11 14 12 57340.50 0.143592 0.259679 0.1584277 -0.000047 0.000058 -0.001711 -0.000968 0.0013575 0.000068 0.000052 0.0000158 0.000084 0.000088 0.000082 0.000111 0.0000166 +2015 11 15 12 57341.50 0.141859 0.259042 0.1570559 -0.000020 0.000031 -0.001771 -0.000432 0.0013882 0.000068 0.000052 0.0000177 0.000085 0.000091 0.000082 0.000111 0.0000165 +2015 11 16 12 57342.50 0.139893 0.258661 0.1556399 0.000005 -0.000040 -0.001835 -0.000516 0.0014370 0.000068 0.000052 0.0000157 0.000087 0.000092 0.000083 0.000111 0.0000162 +2015 11 17 12 57343.50 0.137942 0.258080 0.1541840 0.000018 -0.000057 -0.001782 -0.000744 0.0014839 0.000069 0.000053 0.0000154 0.000094 0.000101 0.000083 0.000111 0.0000158 +2015 11 18 12 57344.50 0.135784 0.257370 0.1526675 -0.000035 0.000010 -0.002298 -0.000794 0.0015566 0.000069 0.000053 0.0000172 0.000121 0.000133 0.000084 0.000111 0.0000152 +2015 11 19 12 57345.50 0.133382 0.256630 0.1510669 -0.000119 -0.000015 -0.002370 -0.000770 0.0016401 0.000070 0.000053 0.0000166 0.000117 0.000124 0.000085 0.000112 0.0000149 +2015 11 20 12 57346.50 0.130995 0.255878 0.1493978 -0.000026 -0.000300 -0.002126 -0.000635 0.0017143 0.000069 0.000053 0.0000151 0.000092 0.000097 0.000084 0.000111 0.0000152 +2015 11 21 12 57347.50 0.128988 0.255498 0.1476356 0.000028 -0.000339 -0.001920 -0.000121 0.0018021 0.000069 0.000053 0.0000167 0.000091 0.000095 0.000084 0.000112 0.0000156 +2015 11 22 12 57348.50 0.126765 0.255358 0.1458061 0.000054 -0.000261 -0.002141 -0.000280 0.0018441 0.000069 0.000052 0.0000181 0.000088 0.000090 0.000083 0.000112 0.0000155 +2015 11 23 12 57349.50 0.124369 0.255001 0.1439675 0.000051 -0.000143 -0.002400 -0.000546 0.0018116 0.000069 0.000053 0.0000152 0.000086 0.000088 0.000084 0.000113 0.0000153 +2015 11 24 12 57350.50 0.121854 0.254416 0.1422096 -0.000004 -0.000072 -0.002218 -0.000710 0.0017034 0.000069 0.000053 0.0000135 0.000085 0.000087 0.000083 0.000113 0.0000153 +2015 11 25 12 57351.50 0.120098 0.254060 0.1405652 -0.000026 -0.000095 -0.001256 -0.000022 0.0015855 0.000069 0.000053 0.0000140 0.000089 0.000090 0.000084 0.000114 0.0000156 +2015 11 26 12 57352.50 0.118987 0.254239 0.1390302 0.000095 -0.000182 -0.000963 0.000225 0.0014607 0.000070 0.000053 0.0000174 0.000123 0.000116 0.000085 0.000115 0.0000159 +2015 11 27 12 57353.50 0.118139 0.254321 0.1376561 0.000111 -0.000213 -0.000645 0.000109 0.0013365 0.000070 0.000053 0.0000227 0.000117 0.000112 0.000085 0.000116 0.0000159 +2015 11 28 12 57354.50 0.117194 0.254394 0.1363123 0.000071 -0.000210 -0.001222 0.000135 0.0013645 0.000070 0.000053 0.0000262 0.000097 0.000098 0.000084 0.000115 0.0000158 +2015 11 29 12 57355.50 0.115528 0.254303 0.1348935 0.000001 -0.000186 -0.001767 -0.000270 0.0014636 0.000069 0.000053 0.0000206 0.000078 0.000082 0.000084 0.000115 0.0000153 +2015 11 30 12 57356.50 0.113671 0.253904 0.1333821 -0.000069 -0.000151 -0.001842 -0.000246 0.0015571 0.000070 0.000054 0.0000143 0.000073 0.000078 0.000085 0.000116 0.0000150 +2015 12 1 12 57357.50 0.111633 0.253821 0.1317835 -0.000106 -0.000129 -0.002018 -0.000093 0.0016317 0.000071 0.000054 0.0000119 0.000073 0.000077 0.000085 0.000116 0.0000151 +2015 12 2 12 57358.50 0.109653 0.253397 0.1301321 -0.000088 -0.000123 -0.001637 -0.000279 0.0016717 0.000071 0.000054 0.0000113 0.000073 0.000077 0.000086 0.000117 0.0000153 +2015 12 3 12 57359.50 0.107985 0.253281 0.1284487 -0.000022 -0.000090 -0.001626 0.000108 0.0016896 0.000071 0.000054 0.0000111 0.000073 0.000077 0.000086 0.000116 0.0000156 +2015 12 4 12 57360.50 0.106557 0.253254 0.1267648 0.000071 -0.000039 -0.001167 -0.000049 0.0016747 0.000071 0.000054 0.0000117 0.000072 0.000077 0.000086 0.000116 0.0000158 +2015 12 5 12 57361.50 0.105474 0.253007 0.1251099 0.000085 -0.000030 -0.001018 -0.000093 0.0016489 0.000071 0.000055 0.0000139 0.000072 0.000077 0.000087 0.000117 0.0000162 +2015 12 6 12 57362.50 0.104332 0.252834 0.1234587 0.000049 -0.000047 -0.001316 -0.000070 0.0016572 0.000071 0.000055 0.0000155 0.000071 0.000076 0.000087 0.000117 0.0000166 +2015 12 7 12 57363.50 0.102995 0.252454 0.1217866 -0.000018 -0.000076 -0.001241 -0.000301 0.0016724 0.000071 0.000055 0.0000129 0.000071 0.000075 0.000087 0.000116 0.0000167 +2015 12 8 12 57364.50 0.101775 0.251866 0.1201222 -0.000088 -0.000101 -0.001115 -0.000403 0.0016471 0.000070 0.000054 0.0000113 0.000071 0.000075 0.000085 0.000114 0.0000164 +2015 12 9 12 57365.50 0.100244 0.251426 0.1185037 -0.000138 -0.000105 -0.001978 -0.000353 0.0015865 0.000069 0.000054 0.0000111 0.000072 0.000076 0.000085 0.000113 0.0000167 +2015 12 10 12 57366.50 0.097852 0.251019 0.1169555 -0.000181 -0.000106 -0.002736 -0.000298 0.0015090 0.000070 0.000054 0.0000114 0.000073 0.000078 0.000085 0.000113 0.0000167 +2015 12 11 12 57367.50 0.095146 0.250623 0.1154860 -0.000184 -0.000121 -0.002569 -0.000305 0.0014351 0.000070 0.000054 0.0000122 0.000074 0.000078 0.000086 0.000114 0.0000162 +2015 12 12 12 57368.50 0.092734 0.250515 0.1140764 -0.000086 -0.000182 -0.002427 0.000016 0.0013930 0.000070 0.000054 0.0000147 0.000074 0.000079 0.000086 0.000114 0.0000164 +2015 12 13 12 57369.50 0.090467 0.250512 0.1126840 -0.000014 -0.000209 -0.002176 0.000050 0.0014026 0.000069 0.000053 0.0000174 0.000076 0.000080 0.000085 0.000112 0.0000167 +2015 12 14 12 57370.50 0.088329 0.250618 0.1112525 -0.000018 -0.000166 -0.002280 0.000314 0.0014696 0.000069 0.000053 0.0000137 0.000076 0.000081 0.000085 0.000111 0.0000167 +2015 12 15 12 57371.50 0.086040 0.250594 0.1097301 -0.000177 0.000007 -0.002043 -0.000227 0.0015783 0.000068 0.000053 0.0000117 0.000079 0.000085 0.000084 0.000110 0.0000169 +2015 12 16 12 57372.50 0.084251 0.250099 0.1080917 -0.000360 0.000178 -0.001450 -0.000355 0.0017006 0.000068 0.000053 0.0000117 0.000089 0.000096 0.000084 0.000110 0.0000167 +2015 12 17 12 57373.50 0.082801 0.249969 0.1063317 -0.000249 0.000044 -0.001460 0.000212 0.0018228 0.000069 0.000053 0.0000121 0.000085 0.000090 0.000084 0.000111 0.0000166 +2015 12 18 12 57374.50 0.081153 0.250483 0.1044505 -0.000063 -0.000191 -0.001944 0.000767 0.0019336 0.000069 0.000053 0.0000127 0.000082 0.000087 0.000084 0.000112 0.0000163 +2015 12 19 12 57375.50 0.079085 0.250963 0.1024770 -0.000088 -0.000294 -0.002090 0.000315 0.0019975 0.000069 0.000053 0.0000151 0.000080 0.000085 0.000084 0.000113 0.0000167 +2015 12 20 12 57376.50 0.076961 0.251279 0.1004791 -0.000130 -0.000335 -0.002370 0.000507 0.0019814 0.000070 0.000053 0.0000181 0.000076 0.000082 0.000085 0.000114 0.0000170 +2015 12 21 12 57377.50 0.074599 0.251515 0.0985406 -0.000133 -0.000293 -0.002185 0.000174 0.0018848 0.000070 0.000054 0.0000140 0.000074 0.000080 0.000085 0.000115 0.0000171 +2015 12 22 12 57378.50 0.072400 0.251597 0.0967271 -0.000031 -0.000141 -0.002104 0.000095 0.0017361 0.000070 0.000053 0.0000117 0.000074 0.000080 0.000084 0.000114 0.0000170 +2015 12 23 12 57379.50 0.070239 0.251832 0.0950745 0.000144 0.000076 -0.002245 0.000519 0.0015729 0.000070 0.000053 0.0000113 0.000076 0.000082 0.000085 0.000114 0.0000167 +2015 12 24 12 57380.50 0.067824 0.252610 0.0935728 0.000205 0.000175 -0.002645 0.000935 0.0014431 0.000070 0.000053 0.0000126 0.000076 0.000082 0.000085 0.000114 0.0000164 +2015 12 25 12 57381.50 0.065204 0.253265 0.0921672 0.000191 0.000195 -0.002519 0.000444 0.0013827 0.000070 0.000053 0.0000176 0.000079 0.000084 0.000085 0.000114 0.0000160 +2015 12 26 12 57382.50 0.062958 0.253566 0.0907828 0.000126 0.000159 -0.001970 0.000356 0.0013996 0.000070 0.000054 0.0000260 0.000089 0.000092 0.000086 0.000114 0.0000157 +2015 12 27 12 57383.50 0.060820 0.254162 0.0893460 0.000035 0.000091 -0.002276 0.000712 0.0014881 0.000070 0.000053 0.0000253 0.000098 0.000098 0.000085 0.000114 0.0000157 +2015 12 28 12 57384.50 0.058629 0.254657 0.0877862 -0.000059 0.000014 -0.002025 0.000498 0.0016180 0.000070 0.000054 0.0000162 0.000101 0.000100 0.000086 0.000114 0.0000157 +2015 12 29 12 57385.50 0.056495 0.255366 0.0861170 -0.000128 -0.000045 -0.002095 0.000940 0.0017221 0.000071 0.000054 0.0000131 0.000102 0.000101 0.000086 0.000115 0.0000159 +2015 12 30 12 57386.50 0.054402 0.255964 0.0843466 -0.000165 -0.000079 -0.001883 0.000430 0.0018232 0.000071 0.000054 0.0000129 0.000105 0.000102 0.000087 0.000115 0.0000159 +2015 12 31 12 57387.50 0.052338 0.256440 0.0824702 -0.000178 -0.000085 -0.002013 0.000610 0.0019030 0.000070 0.000054 0.0000169 0.000103 0.000101 0.000086 0.000114 0.0000156 diff --git a/src/test/resources/eopc04/tai-utc.dat b/src/test/resources/eopc04/tai-utc.dat new file mode 100644 index 0000000000..89d16226f3 --- /dev/null +++ b/src/test/resources/eopc04/tai-utc.dat @@ -0,0 +1,41 @@ + 1961 JAN 1 =JD 2437300.5 TAI-UTC= 1.4228180 S + (MJD - 37300.) X 0.001296 S + 1961 AUG 1 =JD 2437512.5 TAI-UTC= 1.3728180 S + (MJD - 37300.) X 0.001296 S + 1962 JAN 1 =JD 2437665.5 TAI-UTC= 1.8458580 S + (MJD - 37665.) X 0.0011232S + 1963 NOV 1 =JD 2438334.5 TAI-UTC= 1.9458580 S + (MJD - 37665.) X 0.0011232S + 1964 JAN 1 =JD 2438395.5 TAI-UTC= 3.2401300 S + (MJD - 38761.) X 0.001296 S + 1964 APR 1 =JD 2438486.5 TAI-UTC= 3.3401300 S + (MJD - 38761.) X 0.001296 S + 1964 SEP 1 =JD 2438639.5 TAI-UTC= 3.4401300 S + (MJD - 38761.) X 0.001296 S + 1965 JAN 1 =JD 2438761.5 TAI-UTC= 3.5401300 S + (MJD - 38761.) X 0.001296 S + 1965 MAR 1 =JD 2438820.5 TAI-UTC= 3.6401300 S + (MJD - 38761.) X 0.001296 S + 1965 JUL 1 =JD 2438942.5 TAI-UTC= 3.7401300 S + (MJD - 38761.) X 0.001296 S + 1965 SEP 1 =JD 2439004.5 TAI-UTC= 3.8401300 S + (MJD - 38761.) X 0.001296 S + 1966 JAN 1 =JD 2439126.5 TAI-UTC= 4.3131700 S + (MJD - 39126.) X 0.002592 S + 1968 FEB 1 =JD 2439887.5 TAI-UTC= 4.2131700 S + (MJD - 39126.) X 0.002592 S + 1972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0 S + (MJD - 41317.) X 0.0 S + 1972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0 S + (MJD - 41317.) X 0.0 S + 1973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0 S + (MJD - 41317.) X 0.0 S + 1974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0 S + (MJD - 41317.) X 0.0 S + 1975 JAN 1 =JD 2442413.5 TAI-UTC= 14.0 S + (MJD - 41317.) X 0.0 S + 1976 JAN 1 =JD 2442778.5 TAI-UTC= 15.0 S + (MJD - 41317.) X 0.0 S + 1977 JAN 1 =JD 2443144.5 TAI-UTC= 16.0 S + (MJD - 41317.) X 0.0 S + 1978 JAN 1 =JD 2443509.5 TAI-UTC= 17.0 S + (MJD - 41317.) X 0.0 S + 1979 JAN 1 =JD 2443874.5 TAI-UTC= 18.0 S + (MJD - 41317.) X 0.0 S + 1980 JAN 1 =JD 2444239.5 TAI-UTC= 19.0 S + (MJD - 41317.) X 0.0 S + 1981 JUL 1 =JD 2444786.5 TAI-UTC= 20.0 S + (MJD - 41317.) X 0.0 S + 1982 JUL 1 =JD 2445151.5 TAI-UTC= 21.0 S + (MJD - 41317.) X 0.0 S + 1983 JUL 1 =JD 2445516.5 TAI-UTC= 22.0 S + (MJD - 41317.) X 0.0 S + 1985 JUL 1 =JD 2446247.5 TAI-UTC= 23.0 S + (MJD - 41317.) X 0.0 S + 1988 JAN 1 =JD 2447161.5 TAI-UTC= 24.0 S + (MJD - 41317.) X 0.0 S + 1990 JAN 1 =JD 2447892.5 TAI-UTC= 25.0 S + (MJD - 41317.) X 0.0 S + 1991 JAN 1 =JD 2448257.5 TAI-UTC= 26.0 S + (MJD - 41317.) X 0.0 S + 1992 JUL 1 =JD 2448804.5 TAI-UTC= 27.0 S + (MJD - 41317.) X 0.0 S + 1993 JUL 1 =JD 2449169.5 TAI-UTC= 28.0 S + (MJD - 41317.) X 0.0 S + 1994 JUL 1 =JD 2449534.5 TAI-UTC= 29.0 S + (MJD - 41317.) X 0.0 S + 1996 JAN 1 =JD 2450083.5 TAI-UTC= 30.0 S + (MJD - 41317.) X 0.0 S + 1997 JUL 1 =JD 2450630.5 TAI-UTC= 31.0 S + (MJD - 41317.) X 0.0 S + 1999 JAN 1 =JD 2451179.5 TAI-UTC= 32.0 S + (MJD - 41317.) X 0.0 S + 2006 JAN 1 =JD 2453736.5 TAI-UTC= 33.0 S + (MJD - 41317.) X 0.0 S + 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S + 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S + 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S + 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S diff --git a/src/test/resources/external-resources/UTC-TAI.history b/src/test/resources/external-resources/UTC-TAI.history index 394bde0be4..6103c31ce2 100644 --- a/src/test/resources/external-resources/UTC-TAI.history +++ b/src/test/resources/external-resources/UTC-TAI.history @@ -45,5 +45,6 @@ 2006 Jan. 1.- 2009 Jan. 1 33s 2009 Jan. 1.- 2012 Jul 1 34s 2012 Jul 1 - 2015 Jul 1 35s - 2015 Jul 1 - 36s + 2015 Jul 1 - 2017 Jan 1 36s + 2017 Jan 1 - 37s ---------------------------------------------------------------------- diff --git a/src/test/resources/gnss/attitude/reference-attitude-generator/FindBaseSamples.java b/src/test/resources/gnss/attitude/reference-attitude-generator/FindBaseSamples.java index 792cf639a0..19fe7c36df 100644 --- a/src/test/resources/gnss/attitude/reference-attitude-generator/FindBaseSamples.java +++ b/src/test/resources/gnss/attitude/reference-attitude-generator/FindBaseSamples.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -256,7 +256,7 @@ protected BetaDetector create(final double newMaxCheck, final double newThreshol /** {@inheritDoc} */ @Override public double g(final SpacecraftState s) { - final Vector3D pSun = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D pSun = sun.getPosition(s.getDate(), s.getFrame()); final Vector3D mSat = s.getPVCoordinates().getMomentum(); final double beta = 0.5 * FastMath.PI - Vector3D.angle(pSun, mSat); return beta - targetAngle; @@ -300,7 +300,7 @@ public Action eventOccurred(SpacecraftState s, T detector, boolean increasing) { } private double beta(final SpacecraftState s) { - final Vector3D pSun = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D pSun = sun.getPosition(s.getDate(), s.getFrame()); final Vector3D mSat = s.getPVCoordinates().getMomentum(); return FastMath.toDegrees(0.5 * FastMath.PI - Vector3D.angle(pSun, mSat)); } diff --git a/src/test/resources/gnss/attitude/reference-attitude-generator/GenerateBaseSamples.java b/src/test/resources/gnss/attitude/reference-attitude-generator/GenerateBaseSamples.java index dab3abad3f..33a8eba8d0 100644 --- a/src/test/resources/gnss/attitude/reference-attitude-generator/GenerateBaseSamples.java +++ b/src/test/resources/gnss/attitude/reference-attitude-generator/GenerateBaseSamples.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2022 CS GROUP +/* Copyright 2002-2023 CS GROUP * Licensed to CS GROUP (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -268,7 +268,7 @@ public void handleStep(final SpacecraftState s, final boolean isLast) { Transform t = s.getFrame().getTransformTo(itrf, s.getDate()); Vector3D pSat = t.transformPosition(pvSatInert.getPosition()); Vector3D vSat = t.transformVector(pvSatInert.getVelocity()); - Vector3D pSun = t.transformPosition(sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition()); + Vector3D pSun = t.transformPosition(sun.getPosition(s.getDate(), s.getFrame())); out.format(Locale.US, "%s %4d %16.6f %3s %-11s %-4s" + " %16.6f %16.6f %16.6f %16.9f %16.9f %16.9f" + @@ -282,14 +282,14 @@ public void handleStep(final SpacecraftState s, final boolean isLast) { } private double beta(final SpacecraftState s) { - final Vector3D pSun = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); + final Vector3D pSun = sun.getPosition(s.getDate(), s.getFrame()); final Vector3D mSat = s.getPVCoordinates().getMomentum(); return FastMath.toDegrees(0.5 * FastMath.PI - Vector3D.angle(pSun, mSat)); } private double delta(final SpacecraftState s) { - final Vector3D pSun = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition(); - return FastMath.toDegrees(Vector3D.angle(pSun, s.getPVCoordinates().getPosition())); + final Vector3D pSun = sun.getPosition(s.getDate(), s.getFrame()); + return FastMath.toDegrees(Vector3D.angle(pSun, s.getPosition())); } } diff --git a/src/test/resources/gnss/navigation/Example_Beidou_Rinex400.n b/src/test/resources/gnss/navigation/Example_Beidou_Rinex400.n new file mode 100644 index 0000000000..d52db845ed --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Beidou_Rinex400.n @@ -0,0 +1,111 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH C06 D1 +C06 2022 10 05 00 00 00 9.048783686012e-04-1.184208286986e-11 2.574980159653e-19 + 6.000000000000e+00-7.187500000000e+00 1.097902874919e-09 2.152331220686e+00 + -5.401670932770e-08 3.037390066311e-03 2.197921276093e-05 6.493546108246e+03 + 2.592000000000e+05-9.220093488693e-08 2.872027314940e+00-1.303851604462e-08 + 9.467625640496e-01-4.523750000000e+02-3.092539458236e+00-1.799360664880e-09 + 7.482454531408e-10 0.000000000000e+00 8.740000000000e+02 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 8.600000000000e-09-1.800000000000e-09 + 2.592000000000e+05 5.000000000000e+00 +> EPH C06 D1 +C06 2022 10 05 01 00 00 9.048383217305e-04-1.232258739492e-11 0.000000000000e+00 + 6.000000000000e+00-3.101562500000e+01 9.814694535708e-10 2.414379006611e+00 + -5.899928510189e-07 3.035391098820e-03 2.170773223042e-05 6.493551923752e+03 + 2.628000000000e+05-1.019798219204e-07 2.872020107143e+00-1.993030309677e-07 + 9.467658365973e-01-4.403281250000e+02-3.092084648708e+00-1.720071647888e-09 + 7.871756461682e-10 0.000000000000e+00 8.740000000000e+02 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 8.600000000000e-09-1.800000000000e-09 + 2.628000000000e+05 0.000000000000e+00 +> EPH C01 D2 +C01 2022 10 05 00 00 00 7.311386289075e-04 4.433076128407e-11 0.000000000000e+00 + 1.000000000000e+00 4.685937500000e+02 5.828099906612e-09-7.051768400516e-01 + 1.553120091558e-05 6.273174658418e-04 9.119976311922e-06 6.493450458527e+03 + 2.592000000000e+05-6.938353180885e-08 2.972159174245e+00-1.150183379650e-07 + 8.154252562492e-02-2.836875000000e+02 3.064860233331e-01-4.758055334960e-09 + 3.768014095769e-10 0.000000000000e+00 8.740000000000e+02 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00-4.700000000000e-09-1.000000000000e-08 + 2.592000000000e+05 0.000000000000e+00 +> EPH C01 D2 +C01 2022 10 05 01 00 00 7.312982343137e-04 4.433609035459e-11 0.000000000000e+00 + 1.000000000000e+00 5.809218750000e+02 7.296732509626e-09-4.454274900714e-01 + 1.905485987663e-05 6.221896037459e-04 8.880160748959e-07 6.493437374115e+03 + 2.628000000000e+05-9.033828973770e-08-3.027767551906e+00-6.519258022308e-09 + 7.672476627162e-02-3.609375000000e+01 2.610443894323e-02-6.147041763249e-09 + 2.492960984689e-10 0.000000000000e+00 8.740000000000e+02 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00-4.700000000000e-09-1.000000000000e-08 + 2.628000000000e+05 0.000000000000e+00 +> EPH C19 CNV1 +C19 2022 10 05 00 00 00-8.033910416998e-04-8.589928768288e-11 0.000000000000e+00 + -1.139640808105e-02-1.300156250000e+02 3.453536710809e-09-8.439895553698e-01 + -6.432645022869e-06 5.305649829097e-04 8.089467883110e-06 5.282638737345e+03 + 2.592000000000e+05-4.377216100693e-08 1.698788226948e+00-1.303851604462e-08 + 9.703601465722e-01 2.000742187500e+02-1.021547253715e+00-6.782425372384e-09 + -8.911085468192e-11 9.367106834964e-14 3.000000000000e+00 2.592000000000e+05 + 0.000000000000e+00-5.000000000000e+00-1.000000000000e+00-1.000000000000e+00 + -8.731149137020e-10 9.487848728895e-09-5.820766091347e-09 + -1.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.600000000000e+01 + 2.592000000000e+05 1.600000000000e+01 +> EPH C19 CNV1 +C19 2022 10 05 01 00 00-8.037012303248e-04-8.600675727166e-11 0.000000000000e+00 + -1.503753662109e-02-1.249453125000e+02 3.497824269849e-09-3.540282144905e-01 + -6.213784217834e-06 5.301017081365e-04 8.184462785721e-06 5.282634862808e+03 + 2.628000000000e+05-8.381903171539e-09 1.698763952017e+00-2.607703208923e-08 + 9.703598305819e-01 1.978476562500e+02-1.023948675435e+00-6.793675841011e-09 + -1.107188976008e-10 1.267186223683e-13 3.000000000000e+00 2.628000000000e+05 + 0.000000000000e+00-4.000000000000e+00-1.000000000000e+00-1.000000000000e+00 + -8.731149137020e-10 9.487848728895e-09-5.820766091347e-09 + -1.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.700000000000e+01 + 2.628000000000e+05 1.700000000000e+01 +> EPH C19 CNV2 +C19 2022 10 05 00 00 00-8.033910416998e-04-8.589928768288e-11 0.000000000000e+00 + -1.139640808105e-02-1.300156250000e+02 3.453536710809e-09-8.439895553698e-01 + -6.432645022869e-06 5.305649829097e-04 8.089467883110e-06 5.282638737345e+03 + 2.592000000000e+05-4.377216100693e-08 1.698788226948e+00-1.303851604462e-08 + 9.703601465722e-01 2.000742187500e+02-1.021547253715e+00-6.782425372384e-09 + -8.911085468192e-11 9.367106834964e-14 3.000000000000e+00 2.592000000000e+05 + 0.000000000000e+00-5.000000000000e+00-1.000000000000e+00-1.000000000000e+00 + -2.735760062933e-09 9.487848728895e-09-5.820766091347e-09 + 1.500000000000e+01 0.000000000000e+00 0.000000000000e+00 1.600000000000e+01 + 2.592000000000e+05 1.600000000000e+01 +> EPH C19 CNV2 +C19 2022 10 05 01 00 00-8.037012303248e-04-8.600675727166e-11 0.000000000000e+00 + -1.503753662109e-02-1.249453125000e+02 3.497824269849e-09-3.540282144905e-01 + -6.213784217834e-06 5.301017081365e-04 8.184462785721e-06 5.282634862808e+03 + 2.628000000000e+05-8.381903171539e-09 1.698763952017e+00-2.607703208923e-08 + 9.703598305819e-01 1.978476562500e+02-1.023948675435e+00-6.793675841011e-09 + -1.107188976008e-10 1.267186223683e-13 3.000000000000e+00 2.628000000000e+05 + 0.000000000000e+00-4.000000000000e+00-1.000000000000e+00-1.000000000000e+00 + -2.735760062933e-09 9.487848728895e-09-5.820766091347e-09 + 1.500000000000e+01 0.000000000000e+00 0.000000000000e+00 1.700000000000e+01 + 2.628000000000e+05 1.700000000000e+01 +> EPH C19 CNV3 +C19 2022 10 05 00 00 00-8.033910416998e-04-8.589928768288e-11 0.000000000000e+00 + -1.139640808105e-02-1.300156250000e+02 3.453536710809e-09-8.439895553698e-01 + -6.432645022869e-06 5.305649829097e-04 8.089467883110e-06 5.282638737345e+03 + 2.592000000000e+05-4.377216100693e-08 1.698788226948e+00-1.303851604462e-08 + 9.703601465722e-01 2.000742187500e+02-1.021547253715e+00-6.782425372384e-09 + -8.911085468192e-11 9.367106834964e-14 3.000000000000e+00 2.592000000000e+05 + 0.000000000000e+00-5.000000000000e+00-1.000000000000e+00-1.000000000000e+00 + 1.500000000000e+01 0.000000000000e+00 0.000000000000e+00-5.064066499472e-09 + 2.592000000000e+05 +> EPH C19 CNV3 +C19 2022 10 05 01 00 00-8.037012303248e-04-8.600675727166e-11 0.000000000000e+00 + -1.503753662109e-02-1.249453125000e+02 3.497824269849e-09-3.540282144905e-01 + -6.213784217834e-06 5.301017081365e-04 8.184462785721e-06 5.282634862808e+03 + 2.628000000000e+05-8.381903171539e-09 1.698763952017e+00-2.607703208923e-08 + 9.703598305819e-01 1.978476562500e+02-1.023948675435e+00-6.793675841011e-09 + -1.107188976008e-10 1.267186223683e-13 3.000000000000e+00 2.628000000000e+05 + 0.000000000000e+00-4.000000000000e+00-1.000000000000e+00-1.000000000000e+00 + 1.500000000000e+01 0.000000000000e+00 0.000000000000e+00-5.064066499472e-09 + 2.628000000000e+05 diff --git a/src/test/resources/gnss/navigation/Example_Eop_Rinex400.n b/src/test/resources/gnss/navigation/Example_Eop_Rinex400.n new file mode 100644 index 0000000000..8a53270071 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Eop_Rinex400.n @@ -0,0 +1,23 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EOP G15 CNVX + 2022 10 06 16 38 24 2.734127044678e-01-1.487731933594e-03 0.000000000000e+00 + 2.485857009888e-01-1.955032348633e-03 0.000000000000e+00 + 2.597280000000e+05-3.280282020569e-03 1.151263713837e-04 0.000000000000e+00 +> EOP J04 CNVX + 2022 10 06 00 00 00 2.723026275635e-01-2.049922943115e-03 0.000000000000e+00 + 2.491779327393e-01-1.977920532227e-03 0.000000000000e+00 + 3.421860000000e+05-3.003299236298e-03 2.534389495850e-04 0.000000000000e+00 +> EOP I03 LNAV + 2022 10 05 00 00 00 2.751779556274e-01-1.739501953125e-03 0.000000000000e+00 + 2.516956329346e-01-1.918315887451e-03 0.000000000000e+00 + 2.592480000000e+05-3.213942050934e-03 1.052916049957e-04 0.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/Example_GPS_Rinex400.n b/src/test/resources/gnss/navigation/Example_GPS_Rinex400.n new file mode 100644 index 0000000000..8304e3dfb2 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_GPS_Rinex400.n @@ -0,0 +1,59 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +Manual Orekit 20230226 221319 UTC PGM / RUN BY / DATE +https://doi.org/10.xxxx DOI +Apache V2 LICENSE OF USE +not really a station STATION INFORMATION +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH G01 LNAV +G01 2022 10 05 00 00 00 2.727881073952e-04-6.252776074689e-12 0.000000000000e+00 + 7.000000000000e+01 7.334375000000e+01 3.794800925833e-09 2.948187988778e+00 + 3.667548298836e-06 1.200602215249e-02 4.000961780548e-06 5.153660102844e+03 + 2.592000000000e+05-3.911554813385e-08 2.410755371695e-01-1.192092895508e-07 + 9.889225508565e-01 3.138750000000e+02 9.444071913331e-01-7.813182593274e-09 + 2.167947446570e-10 1.000000000000e+00 2.230000000000e+03 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 5.122274160385e-09 7.000000000000e+01 + 2.520180000000e+05 4.000000000000e+00 +> EPH G01 LNAV +G01 2022 10 05 02 00 00 2.727429382503e-04-6.252776074689e-12 0.000000000000e+00 + 8.300000000000e+01 7.218750000000e+01 3.912305820384e-09-2.284843449198e+00 + 3.810971975327e-06 1.200663216878e-02 3.935769200325e-06 5.153658838272e+03 + 2.664000000000e+05 7.078051567078e-08 2.410187071909e-01-1.136213541031e-07 + 9.889243195245e-01 3.195937500000e+02 9.444381993447e-01-7.855327205909e-09 + 2.807259790781e-10 1.000000000000e+00 2.230000000000e+03 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 5.122274160385e-09 8.300000000000e+01 + 2.592180000000e+05 4.000000000000e+00 +> EPH G01 CNAV +G01 2022 10 05 01 30 00 2.727544924710e-04-6.249223361010e-12 0.000000000000e+00 + -5.971431732178e-03 8.059765625000e+01 3.902841140428e-09-2.547540217036e+00 + 4.114583134651e-06 1.200535643147e-02 3.921799361706e-06 5.153658956190e+03 + 1.863000000000e+05 6.798654794693e-08 2.410328126470e-01-9.872019290924e-08 + 9.889238104290e-01 3.195664062500e+02 9.445845335773e-01-7.854377829842e-09 + 2.891191858317e-10 6.631309981839e-14-6.000000000000e+00 2.000000000000e+00 + -1.000000000000e+00 1.000000000000e+00 5.122274160385e-09 7.000000000000e+00 + -3.492459654808e-10-2.823071554303e-09 6.810296326876e-09 6.897607818246e-09 + 2.592060000000e+05 2.230000000000e+03 +> EPH G01 CNAV +G01 2022 10 05 03 30 00 2.727095270529e-04-6.249223361010e-12 0.000000000000e+00 + 6.052970886230e-03 6.485937500000e+01 3.917484607530e-09-1.497089360832e+00 + 3.447756171227e-06 1.200847816654e-02 3.658235073090e-06 5.153661729174e+03 + 1.863000000000e+05 2.058222889900e-07 2.409767068128e-01-2.607703208923e-08 + 9.889253318638e-01 3.257109375000e+02 9.443265296875e-01-7.952953364480e-09 + 1.892935991239e-10-6.140828474306e-14-6.000000000000e+00 2.000000000000e+00 + -1.000000000000e+00 1.000000000000e+00 5.122274160385e-09 7.000000000000e+00 + -3.492459654808e-10-2.823071554303e-09 6.810296326876e-09 6.897607818246e-09 + 2.664060000000e+05 2.230000000000e+03 +> EPH G04 CNV2 +G04 2022 10 05 04 30 00 1.330042141490e-04 7.226219622680e-12 0.000000000000e+00 + 2.001762390137e-03 6.914062500000e-01 4.625906973308e-09 1.887277537485e+00 + 1.024454832077e-08 3.348654136062e-04 8.376315236092e-06 5.153800325291e+03 + 2.412000000000e+05-4.656612873077e-09 5.171544951605e-01 2.328306436539e-08 + 9.601927657114e-01 2.174140625000e+02-1.737767543851e+00-8.034028170143e-09 + -2.950122884460e-10-1.312310522376e-14-2.000000000000e+00 2.000000000000e+00 + 0.000000000000e+00 1.000000000000e+00-8.789356797934e-09 5.000000000000e+00 + 9.999000000000e+09 9.999000000000e+09 9.999000000000e+09 9.999000000000e+09 + -3.492459654808e-10-7.858034223318e-10 + 3.550860000000e+05 2.044000000000e+03 diff --git a/src/test/resources/gnss/navigation/Example_Galileo_Rinex302.n b/src/test/resources/gnss/navigation/Example_Galileo_Rinex302.n index c9779201c9..ab90522ab8 100644 --- a/src/test/resources/gnss/navigation/Example_Galileo_Rinex302.n +++ b/src/test/resources/gnss/navigation/Example_Galileo_Rinex302.n @@ -1,7 +1,7 @@ 3.02 N: GNSS NAV DATA E: GALILEO RINEX VERSION / TYPE sbf2rin-10.2.0 20160428 003637 LCL PGM / RUN BY / DATE GAL 3.5500E+01 -2.3438E-02 1.6632E-02 0.0000E+00 IONOSPHERIC CORR -GPGA -2.9103830457E-11-4.440892099E-16 918000 1919 TIME SYSTEM CORR +GPGA -2.9103830457E-11-4.440892099E-16 313200 1920 TIME SYSTEM CORR GAUT -1.8626451492E-09 2.664535259E-15 259200 1894 TIME SYSTEM CORR 17 LEAP SECONDS END OF HEADER diff --git a/src/test/resources/gnss/navigation/Example_Galileo_Rinex400.n b/src/test/resources/gnss/navigation/Example_Galileo_Rinex400.n new file mode 100644 index 0000000000..b28f7f6219 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Galileo_Rinex400.n @@ -0,0 +1,29 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH E01 INAV +E01 2022 10 05 00 30 00-5.407053395174e-04-7.347011887759e-12 0.000000000000e+00 + 5.100000000000e+01 5.593750000000e+00 3.506931792071e-09-6.303635591153e-01 + 2.998858690262e-07 2.659204183146e-04 5.604699254036e-06 5.440599237442e+03 + 2.610000000000e+05 2.235174179077e-08-1.950400432315e+00 2.235174179077e-08 + 9.718589427173e-01 2.261875000000e+02-1.107378249080e+00-5.725238479163e-09 + -3.360854278785e-10 5.160000000000e+02 2.230000000000e+03 + 3.120000000000e+00 6.500000000000e+01 2.328306436539e-10 0.000000000000e+00 + 2.618140000000e+05 +> EPH E01 INAV +E01 2022 10 05 00 40 00-5.407097050920e-04-7.347011887759e-12 0.000000000000e+00 + 5.200000000000e+01 6.218750000000e+00 3.508360423008e-09-5.559728844908e-01 + 3.259629011154e-07 2.658234443516e-04 5.586072802544e-06 5.440599044800e+03 + 2.616000000000e+05 1.303851604462e-08-1.950403914060e+00 2.235174179077e-08 + 9.718587291313e-01 2.266562500000e+02-1.107382798756e+00-5.722381217290e-09 + -3.310852195998e-10 5.170000000000e+02 2.230000000000e+03 + 3.120000000000e+00 6.500000000000e+01 2.328306436539e-10 0.000000000000e+00 + 2.622640000000e+05 diff --git a/src/test/resources/gnss/navigation/Example_Glonass_Rinex400.n b/src/test/resources/gnss/navigation/Example_Glonass_Rinex400.n new file mode 100644 index 0000000000..0a03c61324 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Glonass_Rinex400.n @@ -0,0 +1,23 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH R01 FDMA +R01 2022 10 05 00 15 00 1.574866473675e-05 0.000000000000e+00 2.592300000000e+05 + -6.686352539062e+03 3.702297210693e-01 9.313225746155e-10 0.000000000000e+00 + -1.537300634766e+04-2.607774734497e+00 9.313225746155e-10 1.000000000000e+00 + 1.922300781250e+04-1.955271720886e+00-2.793967723846e-09 0.000000000000e+00 + 1.790000000000e+02 8.381903171539e-09 2.000000000000e+00 3.000000000000e+00 +> EPH R01 FDMA +R01 2022 10 05 00 45 00 1.574959605932e-05 0.000000000000e+00 2.610300000000e+05 + -6.399892578125e+03-2.942848205566e-02 9.313225746155e-10 0.000000000000e+00 + -1.960947216797e+04-2.058069229126e+00 0.000000000000e+00 1.000000000000e+00 + 1.500577685547e+04-2.700095176697e+00-2.793967723846e-09 0.000000000000e+00 + 2.430000000000e+02 8.381903171539e-09 2.000000000000e+00 3.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/Example_IRNSS_Rinex400.n b/src/test/resources/gnss/navigation/Example_IRNSS_Rinex400.n new file mode 100644 index 0000000000..e66696f7fb --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_IRNSS_Rinex400.n @@ -0,0 +1,29 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH I02 LNAV +I02 2022 10 05 00 05 36 4.418715834618e-04-2.182787284255e-11 0.000000000000e+00 + 1.610000000000e+02-1.531875000000e+02 4.263034715364e-09-4.329943394951e-01 + -5.219131708145e-06 2.062962856144e-03 4.917383193970e-07 6.493340431213e+03 + 2.595360000000e+05-1.154839992523e-07-1.874347616502e+00 1.527369022369e-07 + 5.123365807379e-01 6.900000000000e+01-2.938609902772e+00-3.767299780300e-09 + -6.739566444280e-10 2.230000000000e+03 + 2.000000000000e+00 0.000000000000e+00-1.862645149231e-09 + 2.598840000000e+05 +> EPH I02 LNAV +I02 2022 10 05 00 20 48 4.418543539941e-04-2.057731762761e-11 0.000000000000e+00 + 1.620000000000e+02-1.587500000000e+02 4.250177036933e-09-3.662553437766e-01 + -5.379319190979e-06 2.063980442472e-03 1.452863216400e-07 6.493343137741e+03 + 2.604480000000e+05-1.229345798492e-07-1.874351047045e+00 1.527369022369e-07 + 5.123359414427e-01 8.006250000000e+01-2.938839367324e+00-3.773014304047e-09 + -6.735994866938e-10 2.230000000000e+03 + 2.000000000000e+00 0.000000000000e+00-1.862645149231e-09 + 2.606520000000e+05 diff --git a/src/test/resources/gnss/navigation/Example_Ion_Rinex400.n b/src/test/resources/gnss/navigation/Example_Ion_Rinex400.n new file mode 100644 index 0000000000..899e7c0de6 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Ion_Rinex400.n @@ -0,0 +1,46 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> ION G17 LNAV + 2022 10 05 23 33 54 2.514570951462e-08 1.490116119385e-08-1.192092895508e-07 + -5.960464477539e-08 1.331200000000e+05-1.638400000000e+04-2.621440000000e+05 + 1.966080000000e+05 +> ION G25 CNVX + 2022 10 05 23 30 42 2.514570951462e-08 1.490116119385e-08-1.192092895508e-07 + -5.960464477539e-08 1.331200000000e+05-1.638400000000e+04-2.621440000000e+05 + 1.966080000000e+05 +> ION E02 IFNV + 2022 10 05 06 21 04 1.207500000000e+02-1.953125000000e-01-7.629394531250e-04 + 0.000000000000e+00 +> ION C35 D1D2 + 2022 10 05 00 00 00 3.539025783539e-08 2.011656761169e-07-1.847743988037e-06 + 2.801418304443e-06 1.044480000000e+05-2.293760000000e+05 1.966080000000e+06 + -1.245184000000e+06 +> ION C29 CNVX + 2022 10 05 02 00 00 2.662500000000e+01-1.250000000000e-01 9.875000000000e+00 + 8.375000000000e+00-1.025000000000e+01 8.750000000000e-01 3.750000000000e-01 + 2.125000000000e+00 1.000000000000e+00 +> ION C35 CNVX + 2022 10 05 04 00 00 2.675000000000e+01-1.250000000000e-01 1.000000000000e+01 + 8.250000000000e+00-1.037500000000e+01 8.750000000000e-01 3.750000000000e-01 + 2.125000000000e+00 7.500000000000e-01 +> ION J01 LNAV + 2022 10 05 00 01 54 2.235174179077e-08 0.000000000000e+00-2.980232238770e-07 + -4.172325134277e-07 1.187840000000e+05-6.553600000000e+04 2.621440000000e+05 + 8.519680000000e+05 0.000000000000e+00 +> ION J02 LNAV + 2022 10 05 00 01 54 2.235174179077e-08 0.000000000000e+00-2.980232238770e-07 + -4.172325134277e-07 1.187840000000e+05-6.553600000000e+04 2.621440000000e+05 + 8.519680000000e+05 1.000000000000e+00 +> ION I02 LNAV + 2022 10 05 23 31 12 6.146728992462e-08 5.140900611877e-07-2.086162567139e-06 + -7.510185241699e-06 2.211840000000e+05-1.245184000000e+06-2.818048000000e+06 + 8.323072000000e+06 diff --git a/src/test/resources/gnss/navigation/Example_Mixed_Rinex305.n b/src/test/resources/gnss/navigation/Example_Mixed_Rinex305.n new file mode 100644 index 0000000000..11b24235ce --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Mixed_Rinex305.n @@ -0,0 +1,35 @@ + 3.05 N: GNSS NAV DATA M: MIXED RINEX VERSION / TYPE +XXRINEXN V3 AIUB 20061002 000123 UTC PGM / RUN BY / DATE +EXAMPLE OF VERSION 3.05 FORMAT COMMENT +GPSA 0.1025E-07 0.7451E-08 -0.5960E-07 -0.5960E-07 IONOSPHERIC CORR +GPSB 0.8806E+05 0.0000E+00 -0.1966E+06 -0.6554E+05 IONOSPHERIC CORR +GPUT 0.2793967723E-08 0.000000000E+00 147456 1395 G10 2 TIME SYSTEM CORR +GLUT 0.7823109626E-06 0.000000000E+00 0 1395 R10 0 TIME SYSTEM CORR + 14 LEAP SECONDS + END OF HEADER +G01 2006 10 01 00 00 00 0.798045657575E-04 0.227373675443E-11 0.000000000000E+00 + 0.560000000000E+02-0.787500000000E+01 0.375658504827E-08 0.265129935612E+01 + -0.411644577980E-06 0.640150101390E-02 0.381097197533E-05 0.515371852875E+04 + 0.000000000000E+00 0.782310962677E-07 0.188667086536E+00-0.391155481338E-07 + 0.989010441512E+00 0.320093750000E+03-0.178449589759E+01-0.775925177541E-08 + 0.828605943335E-10 0.000000000000E+00 0.139500000000E+04 0.000000000000E+00 + 0.200000000000E+01 0.000000000000E+00-0.325962901115E-08 0.560000000000E+02 + -0.600000000000E+02 0.400000000000E+01 +G02 2006 10 01 00 00 00 0.402340665460E-04 0.386535248253E-11 0.000000000000E+00 + 0.135000000000E+03 0.467500000000E+02 0.478269921862E-08-0.238713891022E+01 + 0.250712037086E-05 0.876975362189E-02 0.819191336632E-05 0.515372778320E+04 + 0.000000000000E+00-0.260770320892E-07-0.195156738598E+01 0.128522515297E-06 + 0.948630520258E+00 0.214312500000E+03 0.215165003775E+01-0.794140221985E-08 + -0.437875382124E-09 0.000000000000E+00 0.139500000000E+04 0.000000000000E+00 + 0.200000000000E+01 0.000000000000E+00-0.172294676304E-07 0.391000000000E+03 + -0.600000000000E+02 0.400000000000E+01 +R01 2006 10 01 00 15 00-0.137668102980E-04-0.454747350886E-11 0.900000000000E+02 + 0.157594921875E+05-0.145566368103E+01 0.000000000000E+00 0.000000000000E+00 + -0.813711474609E+04 0.205006790161E+01 0.931322574615E-09 0.700000000000E+01 + 0.183413398438E+05 0.215388488770E+01-0.186264514923E-08 0.100000000000E+01 + 1.790000000000E+02 8.381903171539E-09 2.000000000000E+00 3.000000000000E+00 +R02 2006 10 01 00 15 00-0.506537035108E-04 0.181898940355E-11 0.300000000000E+02 + 0.155536342773E+05-0.419384956360E+00 0.000000000000E+00 0.000000000000E+00 + -0.199011298828E+05 0.324192047119E+00-0.931322574615E-09 0.100000000000E+01 + 0.355333544922E+04 0.352666091919E+01-0.186264514923E-08 0.100000000000E+01 + 5.200000000000E+01 9.456379289034E-09 0.000000000000E+00 0.000000000000E+00 diff --git a/src/test/resources/gnss/navigation/Example_QZSS_Rinex400.n b/src/test/resources/gnss/navigation/Example_QZSS_Rinex400.n new file mode 100644 index 0000000000..2d66528b98 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_QZSS_Rinex400.n @@ -0,0 +1,71 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH J02 LNAV +J02 2022 10 05 00 00 00-6.473157554865e-06-9.094947017729e-13 0.000000000000e+00 + 6.100000000000e+01-6.221875000000e+01 3.107272287506e-09-3.496194153491e-01 + -2.235174179077e-06 7.498337992001e-02 3.991648554802e-06 6.493705060959e+03 + 2.592000000000e+05-8.139759302139e-07-1.885344927612e+00-1.471489667892e-07 + 7.255417466631e-01 5.134375000000e+01-1.561478530488e+00-3.431571510156e-09 + -7.575315542299e-10 2.000000000000e+00 2.230000000000e+03 1.000000000000e+00 + 2.800000000000e+00 0.000000000000e+00 4.656612873077e-10 8.290000000000e+02 + 2.556060000000e+05 0.000000000000e+00 +> EPH J02 LNAV +J02 2022 10 05 01 00 00-6.476417183876e-06-9.094947017729e-13 0.000000000000e+00 + 6.500000000000e+01-8.346875000000e+01 3.267636110160e-09-8.714467824564e-02 + -2.929940819740e-06 7.497888850048e-02 2.829357981682e-06 6.493692176819e+03 + 2.628000000000e+05-3.986060619354e-07-1.885360105387e+00-9.313225746155e-08 + 7.255392450732e-01 8.643750000000e+01-1.561460419563e+00-3.561934083138e-09 + -5.832385799422e-10 2.000000000000e+00 2.230000000000e+03 1.000000000000e+00 + 2.800000000000e+00 0.000000000000e+00 4.656612873077e-10 8.330000000000e+02 + 2.592060000000e+05 0.000000000000e+00 +> EPH J02 CNAV +J02 2022 10 05 00 00 00-6.473361281678e-06-8.846257060213e-13 0.000000000000e+00 + -3.685569763184e-02-1.885156250000e+01 3.236206229551e-09-3.494692921588e-01 + -5.839392542839e-07 7.497679907829e-02 2.163462340832e-06 6.493700015598e+03 + 2.592000000000e+05-3.073364496231e-07-1.885348245511e+00 2.607703208923e-08 + 7.255410554344e-01 1.040820312500e+02-1.561651968936e+00-3.587414379402e-09 + -5.025209320139e-10 1.096716650843e-13-3.000000000000e+00 0.000000000000e+00 + -8.000000000000e+00 0.000000000000e+00 5.820766091347e-10 0.000000000000e+00 + 0.000000000000e+00-3.783497959375e-10 1.600710675120e-09 1.688022166491e-09 + 2.556060000000e+05 2.230000000000e+03 +> EPH J02 CNAV +J02 2022 10 05 01 00 00-6.476329872385e-06-8.846257060213e-13 0.000000000000e+00 + -3.055429458618e-02-2.957031250000e+01 3.272457739572e-09-8.700602652738e-02 + -1.152977347374e-06 7.497753057396e-02 2.469867467880e-06 6.493691254697e+03 + 2.628000000000e+05-3.939494490623e-07-1.885360208523e+00-7.729977369308e-08 + 7.255392406845e-01 9.787500000000e+01-1.561620285056e+00-3.563306232343e-09 + -5.746667943215e-10 9.598178122958e-14-3.000000000000e+00 0.000000000000e+00 + -8.000000000000e+00 0.000000000000e+00 5.820766091347e-10 0.000000000000e+00 + 0.000000000000e+00-3.783497959375e-10 1.600710675120e-09 1.688022166491e-09 + 2.592060000000e+05 2.230000000000e+03 +> EPH J02 CNV2 +J02 2022 10 05 00 00 00-6.473361281678e-06-8.846257060213e-13 0.000000000000e+00 + -3.685569763184e-02-1.885156250000e+01 3.236206229551e-09-3.494692921588e-01 + -5.839392542839e-07 7.497679907829e-02 2.163462340832e-06 6.493700015598e+03 + 2.592000000000e+05-3.073364496231e-07-1.885348245511e+00 2.607703208923e-08 + 7.255410554344e-01 1.040820312500e+02-1.561651968936e+00-3.587414379402e-09 + -5.025209320139e-10 1.096716650843e-13-3.000000000000e+00 0.000000000000e+00 + -8.000000000000e+00 0.000000000000e+00 5.820766091347e-10 0.000000000000e+00 + 0.000000000000e+00-3.783497959375e-10 1.600710675120e-09 1.688022166491e-09 + -9.022187441587e-10-1.746229827404e-10 + 2.556180000000e+05 2.230000000000e+03 +> EPH J02 CNV2 +J02 2022 10 05 01 00 00-6.476329872385e-06-8.846257060213e-13 0.000000000000e+00 + -3.055429458618e-02-2.957031250000e+01 3.272457739572e-09-8.700602652738e-02 + -1.152977347374e-06 7.497753057396e-02 2.469867467880e-06 6.493691254697e+03 + 2.628000000000e+05-3.939494490623e-07-1.885360208523e+00-7.729977369308e-08 + 7.255392406845e-01 9.787500000000e+01-1.561620285056e+00-3.563306232343e-09 + -5.746667943215e-10 9.598178122958e-14-3.000000000000e+00 0.000000000000e+00 + -8.000000000000e+00 0.000000000000e+00 5.820766091347e-10 0.000000000000e+00 + 0.000000000000e+00-3.783497959375e-10 1.600710675120e-09 1.688022166491e-09 + -9.022187441587e-10-1.746229827404e-10 + 2.592180000000e+05 2.230000000000e+03 diff --git a/src/test/resources/gnss/navigation/Example_SBAS_Rinex400.n b/src/test/resources/gnss/navigation/Example_SBAS_Rinex400.n new file mode 100644 index 0000000000..e15870f3f4 --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_SBAS_Rinex400.n @@ -0,0 +1,21 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH S22 SBAS +S22 2022 10 05 00 00 32 0.000000000000e+00 0.000000000000e+00 2.592480000000e+05 + -3.389392800000e+04 0.000000000000e+00 0.000000000000e+00 6.300000000000e+01 + 2.508018800000e+04 0.000000000000e+00 0.000000000000e+00 3.276700000000e+04 + 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.050000000000e+02 +> EPH S22 SBAS +S22 2022 10 05 00 01 52 0.000000000000e+00 0.000000000000e+00 2.593290000000e+05 + -3.389392800000e+04 0.000000000000e+00 0.000000000000e+00 6.300000000000e+01 + 2.508018800000e+04 0.000000000000e+00 0.000000000000e+00 3.276700000000e+04 + 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.060000000000e+02 diff --git a/src/test/resources/gnss/navigation/Example_Sto_Rinex400.n b/src/test/resources/gnss/navigation/Example_Sto_Rinex400.n new file mode 100644 index 0000000000..a5c67da04c --- /dev/null +++ b/src/test/resources/gnss/navigation/Example_Sto_Rinex400.n @@ -0,0 +1,59 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> STO C35 CNVX + 2022 10 04 23 20 00 BDGA + 2.592300000000e+05-2.657179720700e-08 4.884981308351e-14 2.066760391300e-19 +> STO C30 CNVX + 2022 10 04 23 20 00 BDGL + 2.592300000000e+05 8.963979780674e-09 1.416644579422e-13 2.134523027081e-19 +> STO C29 CNVX + 2022 10 04 23 20 00 BDGP + 2.592300000000e+05-2.296292223036e-08 5.906386491006e-14 2.134523027081e-19 +> STO C19 CNVX + 2022 10 04 23 11 28 BDUT UTC(NTSC) + 2.592000000000e+05-9.895302355289e-10 0.000000000000e+00 0.000000000000e+00 +> STO E IFNV + 2022 10 06 00 00 00 GAGP + 3.043000000000e+05 1.309672370553e-09 5.329070518201e-15 0.000000000000e+00 +> STO R FDMA + 2022 10 05 00 00 00 GLGP + 2.809660000000e+05-4.470348358154e-08 0.000000000000e+00 0.000000000000e+00 +> STO R FDMA + 2022 10 05 00 00 00 GLGP + 3.421660000000e+05-4.284083843231e-08 0.000000000000e+00 0.000000000000e+00 +> STO R FDMA + 2022 10 05 00 00 00 GLUT UTC(SU) + 2.701660000000e+05 1.862645149231e-09 0.000000000000e+00 0.000000000000e+00 +> STO G27 LNAV + 2022 10 07 19 56 48 GPUT UTC(USNO) + 3.177840000000e+05-9.313225746155e-10-4.440892098501e-15 0.000000000000e+00 +> STO I06 LNAV + 2022 10 05 00 04 48 IRGL + 2.597280000000e+05 4.589674063027e-08-2.353672812205e-14-6.098637220231e-20 +> STO I06 LNAV + 2022 10 05 00 04 48 IRGP + 2.597160000000e+05 2.095475792885e-09 9.769962616701e-15 4.065758146821e-20 +> STO I06 LNAV + 2022 10 05 00 04 48 IRUT UTCIRN + 2.597160000000e+05 3.667082637548e-09-2.664535259100e-15 0.000000000000e+00 +> STO I06 LNAV + 2022 10 05 00 04 48 IRUT UTC(NPLI) + 2.597640000000e+05-2.910383045673e-10 3.996802888651e-15 0.000000000000e+00 +> STO J02 CNVX + 2022 10 06 03 00 00 QZGP + 2.702880000000e+05 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 +> STO J02 LNAV + 2022 10 08 02 46 24 QZUT UTC(NICT) + 2.701140000000e+05 2.793967723846e-09 0.000000000000e+00 0.000000000000e+00 +> STO E17 SBAS + 2022 10 08 02 46 24 SBUT EGNOS UTC(OP) + 2.701140000000e+05 2.793967723846e-09 0.000000000000e+00 0.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/brdc0130.22g b/src/test/resources/gnss/navigation/brdc0130.22g new file mode 100644 index 0000000000..fd36b42876 --- /dev/null +++ b/src/test/resources/gnss/navigation/brdc0130.22g @@ -0,0 +1,4423 @@ + 2.01 GLONASS NAV DATA RINEX VERSION / TYPE +CCRINEXG V1.4 UX CDDIS 14-JAN-22 18:33 PGM / RUN BY / DATE +IGS BROADCAST EPHEMERIS FILE COMMENT +teqc 2020Oct7 UNAVCO Archive Ops 20220114 3:15: COMMENT + 2022 1 13 -0.186264514923D-08 CORR TO SYSTEM TIME + 18 LEAP SECONDS + END OF HEADER + 1 22 1 13 0 15 0.0 0.718235969543D-05-0.000000000000D+00 0.000000000000D+00 + -0.157661396484D+05 0.223997974396D+01-0.931322574616D-09 0.000000000000D+00 + -0.110145615234D+05 0.505579948425D+00 0.186264514923D-08 0.100000000000D+01 + -0.167630014648D+05-0.243822956085D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 0 15 0.0 0.506312586367D-03 0.000000000000D+00 0.900000000000D+03 + -0.250307167969D+05-0.287216186523D+00 0.000000000000D+00 0.000000000000D+00 + -0.433685302734D+04 0.114396095276D+00 0.186264514923D-08-0.400000000000D+01 + 0.191721093750D+04-0.356917953491D+01-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 0 15 0.0 0.498434528709D-04 0.000000000000D+00 0.900000000000D+03 + -0.179675815430D+05-0.235880279541D+01 0.000000000000D+00 0.000000000000D+00 + 0.480757470703D+04-0.369848251343D+00 0.931322574616D-09 0.500000000000D+01 + 0.174662460938D+05-0.233540153503D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 0 15 0.0 0.123257748783D-03 0.181898940355D-11 0.000000000000D+00 + -0.995521484375D+03-0.308979511261D+01 0.000000000000D+00 0.000000000000D+00 + 0.108257114258D+05-0.648876190186D+00-0.000000000000D+00 0.600000000000D+01 + 0.230998676758D+05 0.169882774353D+00-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 0 15 0.0 0.858632847667D-04 0.909494701773D-12 0.000000000000D+00 + 0.165336494141D+05-0.211061954498D+01 0.931322574616D-09 0.000000000000D+00 + 0.110905361328D+05-0.581928253174D+00-0.186264514923D-08 0.100000000000D+01 + 0.159717910156D+05 0.258754444122D+01-0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 0 15 0.0-0.267308205366D-04-0.181898940355D-11 0.000000000000D+00 + 0.251765273438D+05 0.410938262939D+00 0.000000000000D+00 0.000000000000D+00 + 0.306177832031D+04-0.772905349731D-01-0.186264514923D-08-0.400000000000D+01 + -0.287190917969D+04 0.353269386291D+01 0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 0 15 0.0 0.306135043502D-04 0.000000000000D+00 0.570000000000D+03 + 0.175336533203D+05 0.241744232178D+01-0.000000000000D+00 0.000000000000D+00 + -0.504406005859D+04 0.383993148804D+00-0.000000000000D+00 0.500000000000D+01 + -0.178165517578D+05 0.226114177704D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 0 15 0.0-0.630337744951D-04-0.000000000000D+00 0.000000000000D+00 + 0.926514160156D+03 0.309568405151D+01-0.931322574616D-09 0.000000000000D+00 + -0.109269492188D+05 0.639486312866D+00 0.931322574616D-09 0.600000000000D+01 + -0.229768208008D+05-0.181496620178D+00 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 0 15 0.0 0.337883830071D-04 0.181898940355D-11 0.000000000000D+00 + 0.163031865234D+05-0.173382759094D+01-0.000000000000D+00 0.000000000000D+00 + -0.466591845703D+04 0.194829940796D+01-0.000000000000D+00-0.200000000000D+01 + -0.190143203125D+05-0.196958637238D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 0 15 0.0-0.788616016507D-04 0.000000000000D+00 0.000000000000D+00 + 0.182207592773D+05-0.643858909607D+00 0.931322574616D-09 0.000000000000D+00 + -0.169002661133D+05 0.514728546143D+00-0.186264514923D-08-0.700000000000D+01 + -0.594014355469D+04-0.343935489654D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 0 15 0.0 0.473223626614D-04-0.909494701773D-12 0.000000000000D+00 + 0.988588427734D+04 0.950856208801D+00 0.279396772385D-08 0.000000000000D+00 + -0.202592827148D+05-0.133636379242D+01-0.186264514923D-08 0.000000000000D+00 + 0.119359887695D+05-0.305818843842D+01 0.931322574616D-09 0.000000000000D+00 +12 22 1 13 0 15 0.0 0.294435769319D-03 0.363797880709D-11 0.000000000000D+00 + -0.469766699219D+04 0.200231742859D+01 0.279396772385D-08 0.000000000000D+00 + -0.109995893555D+05-0.234271335602D+01-0.931322574616D-09-0.100000000000D+01 + 0.225536870117D+05-0.723826408386D+00-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 0 15 0.0-0.169714912772D-04 0.000000000000D+00 0.900000000000D+03 + -0.157148544922D+05 0.181461238861D+01 0.000000000000D+00 0.000000000000D+00 + 0.386833886719D+04-0.200246715546D+01 0.000000000000D+00-0.200000000000D+01 + 0.196941997070D+05 0.183996009827D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 0 15 0.0 0.731376931071D-04 0.000000000000D+00 0.300000000000D+02 + -0.180745039062D+05 0.514340400696D+00-0.186264514923D-08 0.000000000000D+00 + 0.174137763672D+05-0.383851051331D+00 0.931322574616D-09-0.700000000000D+01 + 0.458977441406D+04 0.348602199554D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 0 15 0.0 0.989371910691D-04-0.000000000000D+00 0.000000000000D+00 + -0.961221777344D+04-0.103537559509D+01-0.279396772385D-08 0.000000000000D+00 + 0.200382539062D+05 0.137214469910D+01 0.186264514923D-08 0.000000000000D+00 + -0.125005483398D+05 0.299242877960D+01-0.931322574616D-09 0.000000000000D+00 +17 22 1 13 0 15 0.0 0.492074526846D-03 0.272848410532D-11 0.000000000000D+00 + -0.717160058594D+04-0.891517639160D+00-0.279396772385D-08 0.000000000000D+00 + 0.103746801758D+05-0.290842151642D+01 0.186264514923D-08 0.400000000000D+01 + -0.221499746094D+05-0.107612514496D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 0 15 0.0 0.103652477264D-03 0.909494701773D-12 0.000000000000D+00 + 0.185027978516D+04-0.286240577698D+00-0.186264514923D-08 0.000000000000D+00 + 0.226690209961D+05-0.157852268219D+01 0.931322574616D-09-0.300000000000D+01 + -0.115812929688D+05-0.312464714050D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 0 15 0.0-0.174220651388D-03-0.909494701773D-12 0.000000000000D+00 + 0.881554248047D+04 0.398724555969D+00-0.931322574616D-09 0.000000000000D+00 + 0.235914824219D+05 0.461033821106D+00-0.000000000000D+00 0.300000000000D+01 + 0.405129150391D+04-0.355830860138D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 0 15 0.0-0.629527494311D-04-0.000000000000D+00 0.105000000000D+04 + 0.121592021484D+05 0.906950950623D+00 0.931322574616D-09 0.000000000000D+00 + 0.925503515625D+04 0.265273284912D+01-0.186264514923D-08 0.200000000000D+01 + 0.204528085938D+05-0.173678207397D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 0 15 0.0-0.269614160061D-03-0.272848410532D-11 0.000000000000D+00 + 0.647128710938D+04 0.837879180908D+00 0.279396772385D-08 0.000000000000D+00 + -0.117233920898D+05 0.285706138611D+01-0.186264514923D-08 0.400000000000D+01 + 0.217226318359D+05 0.129042148590D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 0 15 0.0-0.157709233463D-03-0.272848410532D-11 0.000000000000D+00 + -0.219563330078D+04 0.243412017822D+00 0.279396772385D-08 0.000000000000D+00 + -0.229490146484D+05 0.148175144196D+01-0.931322574616D-09-0.300000000000D+01 + 0.108026386719D+05 0.320399188995D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 0 15 0.0-0.265752896666D-04-0.909494701773D-12 0.000000000000D+00 + -0.112187817383D+05-0.628602027893D+00 0.000000000000D+00 0.000000000000D+00 + -0.206022421875D+05-0.124171352386D+01 0.931322574616D-09 0.300000000000D+01 + -0.100140185547D+05 0.325916481018D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 0 15 0.0 0.754054635763D-04 0.181898940355D-11 0.000000000000D+00 + -0.126755898438D+05-0.929938316345D+00-0.931322574616D-09 0.000000000000D+00 + -0.814777343750D+04-0.266552734375D+01 0.186264514923D-08 0.200000000000D+01 + -0.205618388672D+05 0.162641143799D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 0 45 0.0 0.718235969543D-05-0.000000000000D+00 0.180000000000D+04 + -0.112019277344D+05 0.279221820831D+01-0.000000000000D+00 0.000000000000D+00 + -0.103564482422D+05 0.197722434998D+00 0.186264514923D-08 0.100000000000D+01 + -0.204474575195D+05-0.162904071808D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 0 45 0.0 0.506314449012D-03 0.000000000000D+00 0.270000000000D+04 + -0.247443613281D+05 0.608350753784D+00 0.000000000000D+00 0.000000000000D+00 + -0.400397021484D+04 0.214316368103D+00 0.186264514923D-08-0.400000000000D+01 + -0.449837304688D+04-0.351273727417D+01-0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 0 45 0.0 0.498453155160D-04 0.000000000000D+00 0.270000000000D+04 + -0.216829941406D+05-0.173132324219D+01 0.000000000000D+00 0.000000000000D+00 + 0.451348730469D+04 0.171403884888D-01 0.931322574616D-09 0.500000000000D+01 + 0.126418090820D+05-0.299046039581D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 0 45 0.0 0.123257748783D-03 0.181898940355D-11 0.180000000000D+04 + -0.658777050781D+04-0.307196331024D+01 0.000000000000D+00 0.000000000000D+00 + 0.100753896484D+05-0.181417465210D+00-0.000000000000D+00 0.600000000000D+01 + 0.225111250000D+05-0.819857597351D+00-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 0 45 0.0 0.858651474118D-04 0.909494701773D-12 0.180000000000D+04 + 0.121571484375D+05-0.271529102325D+01 0.000000000000D+00 0.000000000000D+00 + 0.102680493164D+05-0.300805091858D+00-0.186264514923D-08 0.100000000000D+01 + 0.199529584961D+05 0.180728340149D+01-0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 0 45 0.0-0.267345458269D-04-0.181898940355D-11 0.180000000000D+04 + 0.251215527344D+05-0.475772857666D+00 0.000000000000D+00 0.000000000000D+00 + 0.280446875000D+04-0.168253898621D+00-0.186264514923D-08-0.400000000000D+01 + 0.351576123047D+04 0.351870059967D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 0 45 0.0 0.306144356728D-04 0.000000000000D+00 0.180000000000D+04 + 0.213688251953D+05 0.180505943298D+01-0.000000000000D+00 0.000000000000D+00 + -0.473256738281D+04-0.128068923950D-01-0.931322574616D-09 0.500000000000D+01 + -0.131100708008D+05 0.293417835236D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 0 45 0.0-0.630356371403D-04-0.000000000000D+00 0.180000000000D+04 + 0.652871337891D+04 0.307703304291D+01-0.000000000000D+00 0.000000000000D+00 + -0.101883398438D+05 0.177782058716D+00 0.000000000000D+00 0.600000000000D+01 + -0.224063115234D+05 0.811155319214D+00 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 0 45 0.0 0.337921082974D-04 0.181898940355D-11 0.180000000000D+04 + 0.132240888672D+05-0.164614009857D+01-0.931322574616D-09 0.000000000000D+00 + -0.647662597656D+03 0.248988723755D+01 0.000000000000D+00-0.200000000000D+01 + -0.217762275391D+05-0.107935905457D+01 0.186264514923D-08 0.000000000000D+00 +10 22 1 13 0 45 0.0-0.788606703281D-04 0.000000000000D+00 0.180000000000D+04 + 0.167078525391D+05-0.995236396790D+00 0.000000000000D+00 0.000000000000D+00 + -0.152930917969D+05 0.127748107910D+01-0.931322574616D-09-0.700000000000D+01 + -0.118228798828D+05-0.305477619171D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 0 45 0.0 0.473214313388D-04-0.909494701773D-12 0.180000000000D+04 + 0.110069150391D+05 0.312724113464D+00 0.186264514923D-08 0.000000000000D+00 + -0.222011621094D+05-0.782302856445D+00-0.279396772385D-08 0.000000000000D+00 + 0.604084667969D+04-0.344944286346D+01 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 0 45 0.0 0.294442288578D-03 0.363797880709D-11 0.180000000000D+04 + -0.154628076172D+04 0.148072814941D+01 0.279396772385D-08 0.000000000000D+00 + -0.152758442383D+05-0.236184978485D+01-0.186264514923D-08-0.100000000000D+01 + 0.203980419922D+05-0.165580177307D+01-0.000000000000D+00 0.000000000000D+00 +13 22 1 13 0 45 0.0-0.169733539224D-04 0.000000000000D+00 0.270000000000D+04 + -0.125223461914D+05 0.169139194489D+01 0.931322574616D-09 0.000000000000D+00 + -0.238368164062D+03-0.253199100494D+01 0.000000000000D+00-0.200000000000D+01 + 0.222004189453D+05 0.926602363586D+00-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 0 45 0.0 0.731386244297D-04 0.000000000000D+00 0.348000000000D+04 + -0.167623286133D+05 0.902978897095D+00-0.931322574616D-09 0.000000000000D+00 + 0.160492739258D+05-0.114163303375D+01 0.931322574616D-09-0.700000000000D+01 + 0.106062739258D+05 0.315560913086D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 0 45 0.0 0.989362597465D-04-0.000000000000D+00 0.180000000000D+04 + -0.108811679688D+05-0.390582084656D+00-0.186264514923D-08 0.000000000000D+00 + 0.220694711914D+05 0.845073699951D+00 0.186264514923D-08 0.000000000000D+00 + -0.670006689453D+04 0.341063690185D+01-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 0 45 0.0 0.492080114782D-03 0.272848410532D-11 0.180000000000D+04 + -0.923016894531D+04-0.138250732422D+01-0.186264514923D-08 0.000000000000D+00 + 0.512855273438D+04-0.286946868897D+01 0.186264514923D-08 0.400000000000D+01 + -0.232040400391D+05-0.875368118286D-01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 0 45 0.0 0.103655271232D-03 0.909494701773D-12 0.180000000000D+04 + 0.864237304688D+03-0.828586578369D+00-0.186264514923D-08 0.000000000000D+00 + 0.192818291016D+05-0.214235877991D+01 0.186264514923D-08-0.300000000000D+01 + -0.166859682617D+05-0.251032733917D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 0 45 0.0-0.174223445356D-03-0.909494701773D-12 0.180000000000D+04 + 0.929892529297D+04 0.974636077881D-01-0.186264514923D-08 0.000000000000D+00 + 0.236269423828D+05-0.408647537231D+00 0.000000000000D+00 0.300000000000D+01 + -0.242754443359D+04-0.359364318848D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 0 45 0.0-0.629536807537D-04-0.000000000000D+00 0.180000000000D+04 + 0.139865400391D+05 0.108465003967D+01 0.000000000000D+00 0.000000000000D+00 + 0.134725156250D+05 0.200195980072D+01-0.931322574616D-09 0.200000000000D+01 + 0.165784628906D+05-0.254014110565D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 0 45 0.0-0.269618816674D-03-0.272848410532D-11 0.180000000000D+04 + 0.844875781250D+04 0.134899044037D+01 0.186264514923D-08 0.000000000000D+00 + -0.651789355469D+04 0.287519931793D+01-0.279396772385D-08 0.400000000000D+01 + 0.231767172852D+05 0.314698219299D+00-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 0 45 0.0-0.157714821398D-03-0.272848410532D-11 0.180000000000D+04 + -0.129540576172D+04 0.777829170227D+00 0.279396772385D-08 0.000000000000D+00 + -0.197090166016D+05 0.207630634308D+01-0.186264514923D-08-0.300000000000D+01 + 0.160749711914D+05 0.261600017548D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 0 45 0.0-0.265771523118D-04-0.909494701773D-12 0.180000000000D+04 + -0.122243959961D+05-0.444934844971D+00 0.931322574616D-09 0.000000000000D+00 + -0.220552021484D+05-0.372563362122D+00 0.000000000000D+00 0.300000000000D+01 + -0.383609082031D+04 0.356061649322D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 0 45 0.0 0.754082575440D-04 0.181898940355D-11 0.180000000000D+04 + -0.145314423828D+05-0.109448051453D+01-0.000000000000D+00 0.000000000000D+00 + -0.124153798828D+05-0.204503726959D+01 0.186264514923D-08 0.200000000000D+01 + -0.168760209961D+05 0.244224262237D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 1 15 0.0 0.718235969543D-05-0.000000000000D+00 0.360000000000D+04 + -0.587492675781D+04 0.307785987854D+01-0.000000000000D+00 0.000000000000D+00 + -0.103767602539D+05-0.232708930969D+00 0.931322574616D-09 0.100000000000D+01 + -0.225517944336D+05-0.693924903870D+00 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 1 15 0.0 0.506315380335D-03 0.000000000000D+00 0.360000000000D+04 + -0.228659702148D+05 0.146191310883D+01-0.000000000000D+00 0.000000000000D+00 + -0.371255029297D+04 0.703868865967D-01 0.186264514923D-08-0.400000000000D+01 + -0.105643466797D+05-0.318319511414D+01-0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 1 15 0.0 0.498462468386D-04 0.000000000000D+00 0.450000000000D+04 + -0.240896557617D+05-0.920332908630D+00 0.000000000000D+00 0.000000000000D+00 + 0.475379492188D+04 0.212864875793D+00 0.186264514923D-08 0.500000000000D+01 + 0.683925683594D+04-0.341499996185D+01-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 1 15 0.0 0.123261474073D-03 0.181898940355D-11 0.360000000000D+04 + -0.118708442383D+05-0.274945354462D+01 0.000000000000D+00 0.000000000000D+00 + 0.101519746094D+05 0.251888275146D+00 0.000000000000D+00 0.600000000000D+01 + 0.201861704102D+05-0.174677276611D+01-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 1 15 0.0 0.858651474118D-04 0.909494701773D-12 0.360000000000D+04 + 0.691379248047D+04-0.306261634827D+01 0.000000000000D+00 0.000000000000D+00 + 0.100944345703D+05 0.124096870422D+00-0.931322574616D-09 0.100000000000D+01 + 0.223939824219D+05 0.887335777283D+00-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 1 15 0.0-0.267382711172D-04-0.181898940355D-11 0.360000000000D+04 + 0.234854243164D+05-0.132652091980D+01 0.000000000000D+00 0.000000000000D+00 + 0.260067187500D+04-0.197114944458D-01-0.186264514923D-08-0.400000000000D+01 + 0.963192871094D+04 0.323294258118D+01 0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 1 15 0.0 0.306153669953D-04 0.000000000000D+00 0.360000000000D+04 + 0.239183134766D+05 0.100439262390D+01-0.000000000000D+00 0.000000000000D+00 + -0.497720654297D+04-0.222541809082D+00-0.931322574616D-09 0.500000000000D+01 + -0.739033935547D+04 0.337976455689D+01 0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 1 15 0.0-0.630365684629D-04-0.000000000000D+00 0.366000000000D+04 + 0.118199682617D+05 0.275351905823D+01-0.000000000000D+00 0.000000000000D+00 + -0.102663994141D+05-0.249945640564D+00-0.000000000000D+00 0.600000000000D+01 + -0.200950312500D+05 0.174003505707D+01 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 1 15 0.0 0.337949022651D-04 0.181898940355D-11 0.360000000000D+04 + 0.105079233398D+05-0.134156608582D+01-0.186264514923D-08 0.000000000000D+00 + 0.417514208984D+04 0.282825469971D+01 0.931322574616D-09-0.200000000000D+01 + -0.228502910156D+05-0.106468200684D+00 0.931322574616D-09 0.000000000000D+00 +10 22 1 13 1 15 0.0-0.788606703281D-04 0.000000000000D+00 0.360000000000D+04 + 0.147940634766D+05-0.108787536621D+01-0.000000000000D+00 0.000000000000D+00 + -0.123122060547D+05 0.202207279205D+01-0.000000000000D+00-0.700000000000D+01 + -0.167958437500D+05-0.243497657776D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 1 15 0.0 0.473205000162D-04-0.909494701773D-12 0.360000000000D+04 + 0.111041386719D+05-0.172342300415D+00 0.186264514923D-08 0.000000000000D+00 + -0.229572792969D+05-0.322427749634D-01-0.279396772385D-08 0.000000000000D+00 + -0.321540039062D+03-0.357394886017D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 1 15 0.0 0.294446945190D-03 0.363797880709D-11 0.360000000000D+04 + 0.598943359375D+03 0.902631759644D+00 0.279396772385D-08 0.000000000000D+00 + -0.193286235352D+05-0.209315490723D+01-0.279396772385D-08-0.100000000000D+01 + 0.166696181641D+05-0.246007728577D+01 0.000000000000D+00 0.000000000000D+00 +13 22 1 13 1 15 0.0-0.169742852449D-04-0.000000000000D+00 0.474000000000D+04 + -0.975509667969D+04 0.135391139984D+01 0.186264514923D-08 0.000000000000D+00 + -0.511738037109D+04-0.284682846069D+01-0.931322574616D-09-0.200000000000D+01 + 0.229867939453D+05-0.585260391235D-01-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 1 15 0.0 0.731395557523D-04 0.000000000000D+00 0.522000000000D+04 + -0.149776030273D+05 0.103676319122D+01 0.000000000000D+00 0.000000000000D+00 + 0.133039902344D+05-0.189912319183D+01 0.931322574616D-09-0.700000000000D+01 + 0.158033583984D+05 0.258148002624D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 1 15 0.0 0.989353284240D-04-0.000000000000D+00 0.360000000000D+04 + -0.111042958984D+05 0.111867904663D+00-0.186264514923D-08 0.000000000000D+00 + 0.229583535156D+05 0.115582466125D+00 0.186264514923D-08 0.000000000000D+00 + -0.381083007812D+03 0.356482315064D+01-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 1 15 0.0 0.492085702717D-03 0.272848410532D-11 0.360000000000D+04 + -0.120691186523D+05-0.174165916443D+01-0.931322574616D-09 0.000000000000D+00 + 0.217463867188D+03-0.254387378693D+01 0.279396772385D-08 0.400000000000D+01 + -0.224614042969D+05 0.907212257385D+00 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 1 15 0.0 0.103657133877D-03 0.909494701773D-12 0.360000000000D+04 + -0.116996582031D+04-0.143265247345D+01-0.186264514923D-08 0.000000000000D+00 + 0.151256030273D+05-0.242592906952D+01 0.279396772385D-08-0.300000000000D+01 + -0.205013583984D+05-0.170133209228D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 1 15 0.0-0.174226239324D-03-0.909494701773D-12 0.360000000000D+04 + 0.903474609375D+04-0.421857833862D+00-0.186264514923D-08 0.000000000000D+00 + 0.222005903320D+05-0.114539718628D+01 0.931322574616D-09 0.300000000000D+01 + -0.871861279297D+04-0.335101699829D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 1 15 0.0-0.629536807537D-04-0.000000000000D+00 0.360000000000D+04 + 0.159100825195D+05 0.100740242004D+01-0.000000000000D+00 0.000000000000D+00 + 0.163812778320D+05 0.121710395813D+01-0.000000000000D+00 0.200000000000D+01 + 0.114261484375D+05-0.314759063721D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 1 15 0.0-0.269624404609D-03-0.272848410532D-11 0.360000000000D+04 + 0.112578408203D+05 0.174431705475D+01 0.931322574616D-09 0.000000000000D+00 + -0.154975927734D+04 0.259971523285D+01-0.279396772385D-08 0.400000000000D+01 + 0.228406928711D+05-0.685709953308D+00-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 1 15 0.0-0.157720409334D-03-0.272848410532D-11 0.360000000000D+04 + 0.648533691406D+03 0.138499259949D+01 0.186264514923D-08 0.000000000000D+00 + -0.156422192383D+05 0.239255332947D+01-0.279396772385D-08-0.300000000000D+01 + 0.200984428711D+05 0.182554244995D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 1 15 0.0-0.265799462795D-04-0.909494701773D-12 0.360000000000D+04 + -0.126703735352D+05-0.125122070312D-01 0.186264514923D-08 0.000000000000D+00 + -0.219789331055D+05 0.437901496887D+00-0.931322574616D-09 0.300000000000D+01 + 0.263853857422D+04 0.358668422699D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 1 15 0.0 0.754110515118D-04 0.181898940355D-11 0.624000000000D+04 + -0.164662153320D+05-0.101130008697D+01 0.000000000000D+00 0.000000000000D+00 + -0.154287734375D+05-0.128992843628D+01 0.931322574616D-09 0.200000000000D+01 + -0.118837324219D+05 0.306863021851D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 1 45 0.0 0.718235969543D-05-0.000000000000D+00 0.540000000000D+04 + -0.304306152344D+03 0.306059837341D+01-0.000000000000D+00 0.000000000000D+00 + -0.112070712891D+05-0.684425354004D+00 0.931322574616D-09 0.100000000000D+01 + -0.229132651367D+05 0.294920921326D+00 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 1 45 0.0 0.506316311657D-03 0.000000000000D+00 0.540000000000D+04 + -0.195746450195D+05 0.216100311279D+01-0.000000000000D+00 0.000000000000D+00 + -0.387691162109D+04-0.282760620117D+00 0.186264514923D-08-0.400000000000D+01 + -0.158088735352D+05-0.260597991943D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 1 45 0.0 0.498462468386D-04 0.000000000000D+00 0.630000000000D+04 + -0.249492734375D+05-0.315408706665D-01 0.000000000000D+00 0.000000000000D+00 + 0.513631103516D+04 0.170934677124D+00 0.186264514923D-08 0.500000000000D+01 + 0.506761230469D+03-0.357531166077D+01-0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 1 45 0.0 0.123264268041D-03 0.181898940355D-11 0.630000000000D+04 + -0.163271289062D+05-0.216382789612D+01 0.000000000000D+00 0.000000000000D+00 + 0.109004541016D+05 0.549798965454D+00 0.931322574616D-09 0.600000000000D+01 + 0.163035048828D+05-0.253942680359D+01-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 1 45 0.0 0.858660787344D-04 0.909494701773D-12 0.540000000000D+04 + 0.131459667969D+04-0.310671997070D+01 0.000000000000D+00 0.000000000000D+00 + 0.107413837891D+05 0.593008041382D+00-0.931322574616D-09 0.100000000000D+01 + 0.231059462891D+05-0.101467132568D+00-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 1 45 0.0-0.267419964075D-04-0.181898940355D-11 0.540000000000D+04 + 0.204343447266D+05-0.203092670441D+01 0.000000000000D+00 0.000000000000D+00 + 0.285816796875D+04 0.335279464722D+00-0.186264514923D-08-0.400000000000D+01 + 0.150040034180D+05 0.269725418091D+01-0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 1 45 0.0 0.306162983179D-04 0.000000000000D+00 0.540000000000D+04 + 0.249341127930D+05 0.119920730591D+00-0.000000000000D+00 0.000000000000D+00 + -0.539143066406D+04-0.196607589722D+00-0.186264514923D-08 0.500000000000D+01 + -0.110007470703D+04 0.356410312653D+01 0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 1 45 0.0-0.630374997854D-04-0.000000000000D+00 0.540000000000D+04 + 0.162831933594D+05 0.216769504547D+01-0.000000000000D+00 0.000000000000D+00 + -0.110067690430D+05-0.542891502380D+00-0.000000000000D+00 0.600000000000D+01 + -0.162240034180D+05 0.253292083740D+01 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 1 45 0.0 0.337986275554D-04 0.181898940355D-11 0.540000000000D+04 + 0.847630078125D+04-0.901271820068D+00-0.186264514923D-08 0.000000000000D+00 + 0.937130908203D+04 0.289676380158D+01 0.186264514923D-08-0.200000000000D+01 + -0.221552553711D+05 0.873511314392D+00 0.931322574616D-09 0.000000000000D+00 +10 22 1 13 1 45 0.0-0.788606703281D-04 0.000000000000D+00 0.543000000000D+04 + 0.129391625977D+05-0.935832977295D+00-0.931322574616D-09 0.000000000000D+00 + -0.809162060547D+04 0.263725185394D+01 0.000000000000D+00-0.700000000000D+01 + -0.204757504883D+05-0.162725353241D+01 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 1 45 0.0 0.473186373711D-04-0.909494701773D-12 0.540000000000D+04 + 0.105203579102D+05-0.435676574707D+00 0.931322574616D-09 0.000000000000D+00 + -0.222562797852D+05 0.818769454956D+00-0.186264514923D-08 0.000000000000D+00 + -0.665902441406D+04-0.342193603516D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 1 45 0.0 0.294453464449D-03 0.363797880709D-11 0.540000000000D+04 + 0.173487548828D+04 0.376893997192D+00 0.186264514923D-08 0.000000000000D+00 + -0.226469106445D+05-0.155212020874D+01-0.372529029846D-08-0.100000000000D+01 + 0.116557988281D+05-0.307478237152D+01 0.931322574616D-09 0.000000000000D+00 +13 22 1 13 1 45 0.0-0.169761478901D-04-0.000000000000D+00 0.540000000000D+04 + -0.772582470703D+04 0.887819290161D+00 0.186264514923D-08 0.000000000000D+00 + -0.103186635742D+05-0.288262271881D+01-0.186264514923D-08-0.200000000000D+01 + 0.219925273438D+05-0.103899860382D+01-0.931322574616D-09 0.000000000000D+00 +14 22 1 13 1 45 0.0 0.731404870748D-04 0.000000000000D+00 0.630000000000D+04 + -0.131796582031D+05 0.922539710998D+00 0.000000000000D+00 0.000000000000D+00 + 0.928020849609D+04-0.254413509369D+01 0.000000000000D+00-0.700000000000D+01 + 0.197796967773D+05 0.180802536011D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 1 45 0.0 0.989353284240D-04-0.000000000000D+00 0.540000000000D+04 + -0.106078081055D+05 0.400067329407D+00-0.931322574616D-09 0.000000000000D+00 + 0.224185722656D+05-0.724876403809D+00 0.186264514923D-08 0.000000000000D+00 + 0.596740820312D+04 0.344324970245D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 1 45 0.0 0.492091290653D-03 0.272848410532D-11 0.540000000000D+04 + -0.153672070312D+05-0.188045024872D+01-0.000000000000D+00 0.000000000000D+00 + -0.389667333984D+04-0.199816799164D+01 0.186264514923D-08 0.400000000000D+01 + -0.199806972656D+05 0.183111476898D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 1 45 0.0 0.103658996522D-03 0.909494701773D-12 0.540000000000D+04 + -0.426229003906D+04-0.198515892029D+01-0.931322574616D-09 0.000000000000D+00 + 0.107289335938D+05-0.241036319733D+01 0.279396772385D-08-0.300000000000D+01 + -0.227310595703D+05-0.759850502014D+00 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 1 45 0.0-0.174229033291D-03-0.909494701773D-12 0.540000000000D+04 + 0.769609277344D+04-0.108047676086D+01-0.186264514923D-08 0.000000000000D+00 + 0.196392182617D+05-0.165726947784D+01 0.186264514923D-08 0.300000000000D+01 + -0.143352446289D+05-0.284913253784D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 1 45 0.0-0.629546120763D-04-0.000000000000D+00 0.540000000000D+04 + 0.174499741211D+05 0.659726142883D+00-0.931322574616D-09 0.000000000000D+00 + 0.178436342773D+05 0.414783477783D+00 0.000000000000D+00 0.200000000000D+01 + 0.539306591797D+04-0.351244640350D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 1 45 0.0-0.269629061222D-03-0.272848410532D-11 0.540000000000D+04 + 0.146018969727D+05 0.193009567261D+01 0.000000000000D+00 0.000000000000D+00 + 0.269987890625D+04 0.209007358551D+01-0.186264514923D-08 0.400000000000D+01 + 0.207397529297D+05-0.163359832764D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 1 45 0.0-0.157725065947D-03-0.272848410532D-11 0.540000000000D+04 + 0.366614355469D+04 0.195154762268D+01 0.931322574616D-09 0.000000000000D+00 + -0.112777807617D+05 0.240717983246D+01-0.279396772385D-08-0.300000000000D+01 + 0.225624648438D+05 0.894676208496D+00-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 1 45 0.0-0.265818089247D-04-0.909494701773D-12 0.540000000000D+04 + -0.121530156250D+05 0.612719535828D+00 0.186264514923D-08 0.000000000000D+00 + -0.205790781250D+05 0.108217144012D+01-0.186264514923D-08 0.300000000000D+01 + 0.890908691406D+04 0.333533382416D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 1 45 0.0 0.754138454795D-04 0.181898940355D-11 0.630000000000D+04 + -0.180133354492D+05-0.665031433105D+00 0.931322574616D-09 0.000000000000D+00 + -0.170464443359D+05-0.513663291931D+00 0.000000000000D+00 0.200000000000D+01 + -0.597196191406D+04 0.345729255676D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 2 15 0.0 0.718235969543D-05-0.000000000000D+00 0.798000000000D+04 + 0.496621533203D+04 0.274981403351D+01-0.000000000000D+00 0.000000000000D+00 + -0.127895004883D+05-0.105127906799D+01 0.000000000000D+00 0.100000000000D+01 + -0.215036894531D+05 0.126113891602D+01 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 2 15 0.0 0.506316311657D-03 0.000000000000D+00 0.720000000000D+04 + -0.152330039062D+05 0.261698532105D+01 0.000000000000D+00 0.000000000000D+00 + -0.481217480469D+04-0.771147727966D+00 0.186264514923D-08-0.400000000000D+01 + -0.198239868164D+05-0.182615756989D+01 0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 2 15 0.0 0.498471781611D-04 0.000000000000D+00 0.828000000000D+04 + -0.242270834961D+05 0.817698478699D+00 0.000000000000D+00 0.000000000000D+00 + 0.522416357422D+04-0.111131668091D+00 0.186264514923D-08 0.500000000000D+01 + -0.586496630859D+04-0.345819377899D+01-0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 2 15 0.0 0.123267993331D-03 0.181898940355D-11 0.720000000000D+04 + -0.195508071289D+05-0.139607334137D+01 0.000000000000D+00 0.000000000000D+00 + 0.120031005859D+05 0.635408401489D+00 0.931322574616D-09 0.600000000000D+01 + 0.111618823242D+05-0.313650321960D+01-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 2 15 0.0 0.858679413796D-04 0.909494701773D-12 0.720000000000D+04 + -0.408744921875D+04-0.284746265411D+01 0.000000000000D+00 0.000000000000D+00 + 0.121903408203D+05 0.997383117676D+00-0.000000000000D+00 0.100000000000D+01 + 0.220331259766D+05-0.108291053772D+01-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 2 15 0.0-0.267457216978D-04-0.181898940355D-11 0.726000000000D+04 + 0.163153159180D+05-0.250119686127D+01-0.000000000000D+00 0.000000000000D+00 + 0.388904296875D+04 0.824973106384D+00-0.186264514923D-08-0.400000000000D+01 + 0.192164516602D+05 0.195279502869D+01-0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 2 15 0.0 0.306181609631D-04 0.000000000000D+00 0.720000000000D+04 + 0.243696748047D+05-0.732034683227D+00-0.000000000000D+00 0.000000000000D+00 + -0.553956835938D+04 0.701942443848D-01-0.186264514923D-08 0.500000000000D+01 + 0.527509716797D+04 0.347359371185D+01 0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 2 15 0.0-0.630384311080D-04-0.000000000000D+00 0.720000000000D+04 + 0.195148999023D+05 0.140146732330D+01-0.000000000000D+00 0.000000000000D+00 + -0.120935102539D+05-0.624965667725D+00-0.931322574616D-09 0.600000000000D+01 + -0.110952006836D+05 0.312852859497D+01 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 2 15 0.0 0.338023528457D-04 0.181898940355D-11 0.720000000000D+04 + 0.728387402344D+04-0.426861763000D+00-0.931322574616D-09 0.000000000000D+00 + 0.144250322266D+05 0.266937446594D+01 0.279396772385D-08-0.200000000000D+01 + -0.197469746094D+05 0.178480625153D+01 0.000000000000D+00 0.000000000000D+00 +10 22 1 13 2 15 0.0-0.788606703281D-04-0.000000000000D+00 0.720000000000D+04 + 0.115390029297D+05-0.594984054565D+00-0.931322574616D-09 0.000000000000D+00 + -0.295309570312D+04 0.302853393555D+01 0.931322574616D-09-0.700000000000D+01 + -0.225780292969D+05-0.693315505981D+00 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 2 15 0.0 0.473177060485D-04-0.909494701773D-12 0.720000000000D+04 + 0.968625244141D+04-0.449633598328D+00 0.000000000000D+00 0.000000000000D+00 + -0.200174873047D+05 0.165679931641D+01-0.931322574616D-09 0.000000000000D+00 + -0.124812001953D+05-0.300510120392D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 2 15 0.0 0.294459052384D-03 0.363797880709D-11 0.720000000000D+04 + 0.204528466797D+04-0.627517700195D-03 0.931322574616D-09 0.000000000000D+00 + -0.247875048828D+05-0.797670364380D+00-0.279396772385D-08-0.100000000000D+01 + 0.574296142578D+04-0.345249843597D+01 0.186264514923D-08 0.000000000000D+00 +13 22 1 13 2 15 0.0-0.169770792127D-04-0.000000000000D+00 0.720000000000D+04 + -0.657318896484D+04 0.398029327393D+00 0.186264514923D-08 0.000000000000D+00 + -0.153131523438D+05-0.261720561981D+01-0.279396772385D-08-0.200000000000D+01 + 0.192949365234D+05-0.193878746033D+01-0.000000000000D+00 0.000000000000D+00 +14 22 1 13 2 15 0.0 0.731414183974D-04 0.000000000000D+00 0.810000000000D+04 + -0.117761923828D+05 0.609912872314D+00 0.931322574616D-09 0.000000000000D+00 + 0.427195703125D+04-0.297875308991D+01-0.931322574616D-09-0.700000000000D+01 + 0.222282617188D+05 0.894953727722D+00-0.186264514923D-08 0.000000000000D+00 +15 22 1 13 2 15 0.0 0.989343971014D-04-0.000000000000D+00 0.720000000000D+04 + -0.981273242188D+04 0.441991806030D+00-0.000000000000D+00 0.000000000000D+00 + 0.203492470703D+05-0.156450748444D+01 0.931322574616D-09 0.000000000000D+00 + 0.118545385742D+05 0.305557346344D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 2 15 0.0 0.492097809911D-03 0.272848410532D-11 0.723000000000D+04 + -0.186736186523D+05-0.174555778503D+01 0.000000000000D+00 0.000000000000D+00 + -0.690269775391D+04-0.133091545105D+01 0.931322574616D-09 0.400000000000D+01 + -0.159549233398D+05 0.261283874512D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 2 15 0.0 0.103660859168D-03 0.909494701773D-12 0.720000000000D+04 + -0.821973339844D+04-0.237709903717D+01-0.931322574616D-09 0.000000000000D+00 + 0.661151123047D+04-0.212420845032D+01 0.279396772385D-08-0.300000000000D+01 + -0.232008461914D+05 0.241420745850D+00 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 2 15 0.0-0.174231827259D-03-0.909494701773D-12 0.720000000000D+04 + 0.512512841797D+04-0.177192306519D+01-0.931322574616D-09 0.000000000000D+00 + 0.164026025391D+05-0.189041709900D+01 0.279396772385D-08 0.300000000000D+01 + -0.188428784180D+05-0.212680435181D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 2 15 0.0-0.629546120763D-04-0.000000000000D+00 0.720000000000D+04 + 0.181408588867D+05 0.730924606323D-01-0.931322574616D-09 0.000000000000D+00 + 0.179337426758D+05-0.289468765259D+00 0.931322574616D-09 0.200000000000D+01 + -0.105579394531D+04-0.360655593872D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 2 15 0.0-0.269634649157D-03-0.272848410532D-11 0.720000000000D+04 + 0.180430595703D+05 0.184548664093D+01-0.000000000000D+00 0.000000000000D+00 + 0.589005810547D+04 0.144033050537D+01-0.931322574616D-09 0.400000000000D+01 + 0.170354248047D+05-0.245566558838D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 2 15 0.0-0.157729722560D-03-0.181898940355D-11 0.720000000000D+04 + 0.758305517578D+04 0.236708831787D+01 0.931322574616D-09 0.000000000000D+00 + -0.714330371094D+04 0.214505767822D+01-0.279396772385D-08-0.300000000000D+01 + 0.232785346680D+05-0.103932380676D+00-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 2 15 0.0-0.265846028924D-04-0.909494701773D-12 0.720000000000D+04 + -0.104037363281D+05 0.133864498138D+01 0.186264514923D-08 0.000000000000D+00 + -0.182304121094D+05 0.148237419128D+01-0.279396772385D-08 0.300000000000D+01 + 0.144905722656D+05 0.282604122162D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 2 15 0.0 0.754166394472D-04 0.181898940355D-11 0.810000000000D+04 + -0.187198789062D+05-0.860834121704D-01 0.186264514923D-08 0.000000000000D+00 + -0.173332504883D+05 0.171104431152D+00-0.931322574616D-09 0.200000000000D+01 + 0.401611816406D+03 0.357847213745D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 2 45 0.0 0.718235969543D-05-0.000000000000D+00 0.900000000000D+04 + 0.945022900391D+04 0.219890022278D+01-0.000000000000D+00 0.000000000000D+00 + -0.148845146484D+05-0.124053478241D+01-0.000000000000D+00 0.100000000000D+01 + -0.184316953125D+05 0.213005828857D+01 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 2 45 0.0 0.506317242980D-03 0.000000000000D+00 0.900000000000D+04 + -0.103306040039D+05 0.277935981751D+01 0.000000000000D+00 0.000000000000D+00 + -0.667403564453D+04-0.129420566559D+01 0.931322574616D-09-0.400000000000D+01 + -0.222978872070D+05-0.904785156250D+00 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 2 45 0.0 0.498471781611D-04 0.000000000000D+00 0.900000000000D+04 + -0.220958305664D+05 0.151734066009D+01 0.000000000000D+00 0.000000000000D+00 + 0.461672363281D+04-0.591095924377D+00 0.186264514923D-08 0.500000000000D+01 + -0.117809946289D+05-0.307222461700D+01-0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 2 45 0.0 0.123270787299D-03 0.181898940355D-11 0.900000000000D+04 + -0.213080039062D+05-0.553648948669D+00 0.000000000000D+00 0.000000000000D+00 + 0.130361132812D+05 0.469527244568D+00 0.186264514923D-08 0.600000000000D+01 + 0.515767773438D+04-0.349156475067D+01-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 2 45 0.0 0.858688727021D-04 0.909494701773D-12 0.900000000000D+04 + -0.878078320312D+04-0.233036422730D+01 0.000000000000D+00 0.000000000000D+00 + 0.142334804688D+05 0.123853492737D+01 0.000000000000D+00 0.100000000000D+01 + 0.192574877930D+05-0.198120021820D+01-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 2 45 0.0-0.267513096333D-04-0.181898940355D-11 0.900000000000D+04 + 0.116021772461D+05-0.268608093262D+01-0.000000000000D+00 0.000000000000D+00 + 0.585010742188D+04 0.135105705261D+01-0.931322574616D-09-0.400000000000D+01 + 0.219429804688D+05 0.105692863464D+01-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 2 45 0.0 0.306190922856D-04 0.000000000000D+00 0.900000000000D+04 + 0.223845429688D+05-0.144188594818D+01-0.000000000000D+00 0.000000000000D+00 + -0.501690869141D+04 0.538745880127D+00-0.186264514923D-08 0.500000000000D+01 + 0.112440156250D+05 0.311569499970D+01-0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 2 45 0.0-0.630393624306D-04-0.000000000000D+00 0.900000000000D+04 + 0.212847392578D+05 0.562628746033D+00-0.931322574616D-09 0.000000000000D+00 + -0.131063032227D+05-0.457988739014D+00-0.931322574616D-09 0.600000000000D+01 + -0.510741552734D+04 0.348125457764D+01 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 2 45 0.0 0.338060781360D-04 0.181898940355D-11 0.900000000000D+04 + 0.689706494141D+04-0.228328704834D-01-0.931322574616D-09 0.000000000000D+00 + 0.188140830078D+05 0.216512775421D+01 0.279396772385D-08-0.200000000000D+01 + -0.158134658203D+05 0.255728340149D+01-0.000000000000D+00 0.000000000000D+00 +10 22 1 13 2 45 0.0-0.788606703281D-04-0.000000000000D+00 0.900000000000D+04 + 0.108580917969D+05-0.153260231018D+00-0.931322574616D-09 0.000000000000D+00 + 0.263851855469D+04 0.313362693787D+01 0.186264514923D-08-0.700000000000D+01 + -0.229388061523D+05 0.295271873474D+00 0.931322574616D-09 0.000000000000D+00 +11 22 1 13 2 45 0.0 0.473167747259D-04-0.909494701773D-12 0.900000000000D+04 + 0.904156933594D+04-0.231862068176D+00-0.000000000000D+00 0.000000000000D+00 + -0.163686381836D+05 0.236738014221D+01-0.000000000000D+00 0.000000000000D+00 + -0.173374916992D+05-0.235571384430D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 2 45 0.0 0.294465571642D-03 0.363797880709D-11 0.900000000000D+04 + 0.186246142578D+04-0.163107872009D+00 0.000000000000D+00 0.000000000000D+00 + -0.254460776367D+05 0.768527984619D-01-0.279396772385D-08-0.100000000000D+01 + -0.613001464844D+03-0.356391143799D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 2 45 0.0-0.169817358255D-04-0.000000000000D+00 0.900000000000D+04 + -0.624312841797D+04-0.935745239258D-02 0.931322574616D-09 0.000000000000D+00 + -0.195734355469D+05-0.207462310791D+01-0.372529029846D-08-0.200000000000D+01 + 0.151033598633D+05-0.268819141388D+01 0.000000000000D+00 0.000000000000D+00 +14 22 1 13 2 45 0.0 0.731423497200D-04 0.000000000000D+00 0.990000000000D+04 + -0.110533320312D+05 0.182345390320D+00 0.931322574616D-09 0.000000000000D+00 + -0.127563134766D+04-0.313532447815D+01-0.186264514923D-08-0.700000000000D+01 + 0.229599116211D+05-0.873022079468D-01-0.186264514923D-08 0.000000000000D+00 +15 22 1 13 2 45 0.0 0.989343971014D-04-0.000000000000D+00 0.900000000000D+04 + -0.915759716797D+04 0.250417709351D+00 0.000000000000D+00 0.000000000000D+00 + 0.168556552734D+05-0.228916835785D+01 0.000000000000D+00 0.000000000000D+00 + 0.168255747070D+05 0.243201160431D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 2 45 0.0 0.492104329169D-03 0.272848410532D-11 0.900000000000D+04 + -0.214808349609D+05-0.132828712463D+01 0.931322574616D-09 0.000000000000D+00 + -0.868352197266D+04-0.656131744385D+00 0.931322574616D-09 0.400000000000D+01 + -0.106962084961D+05 0.319220829010D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 2 45 0.0 0.103662721813D-03 0.000000000000D+00 0.900000000000D+04 + -0.126710141602D+05-0.252226829529D+01-0.000000000000D+00 0.000000000000D+00 + 0.320227636719D+04-0.163825607300D+01 0.279396772385D-08-0.300000000000D+01 + -0.218726665039D+05 0.122485923767D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 2 45 0.0-0.174234621227D-03-0.909494701773D-12 0.900000000000D+04 + 0.136759130859D+04-0.237965202331D+01-0.931322574616D-09 0.000000000000D+00 + 0.130063896484D+05-0.183737182617D+01 0.279396772385D-08 0.300000000000D+01 + -0.218927592773D+05-0.123993587494D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 2 45 0.0-0.629555433989D-04-0.000000000000D+00 0.900000000000D+04 + 0.176134033203D+05-0.678669929504D+00-0.931322574616D-09 0.000000000000D+00 + 0.169185600586D+05-0.799892425537D+00 0.186264514923D-08 0.200000000000D+01 + -0.742318750000D+04-0.342247009277D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 2 45 0.0-0.269640237093D-03-0.272848410532D-11 0.900000000000D+04 + 0.210721840820D+05 0.147337818146D+01-0.931322574616D-09 0.000000000000D+00 + 0.786874755859D+04 0.763387680054D+00-0.000000000000D+00 0.400000000000D+01 + 0.120133881836D+05-0.308816909790D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 2 45 0.0-0.157734379172D-03-0.181898940355D-11 0.900000000000D+04 + 0.120430595703D+05 0.254287242889D+01-0.000000000000D+00 0.000000000000D+00 + -0.368141748047D+04 0.167425632477D+01-0.279396772385D-08-0.300000000000D+01 + 0.221941582031D+05-0.109283161163D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 2 45 0.0-0.265873968601D-04-0.909494701773D-12 0.900000000000D+04 + -0.734135400391D+04 0.205205154419D+01 0.931322574616D-09 0.000000000000D+00 + -0.154114916992D+05 0.160258293152D+01-0.372529029846D-08 0.300000000000D+01 + 0.189514003906D+05 0.209825801849D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 2 45 0.0 0.754203647375D-04 0.181898940355D-11 0.990000000000D+04 + -0.182259995117D+05 0.653910636902D+00 0.186264514923D-08 0.000000000000D+00 + -0.165421650391D+05 0.670876502991D+00-0.186264514923D-08 0.200000000000D+01 + 0.674421386719D+04 0.342316627502D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 3 15 0.0 0.718142837286D-05-0.000000000000D+00 0.108000000000D+05 + 0.127908828125D+05 0.149639892578D+01-0.000000000000D+00 0.000000000000D+00 + -0.171100942383D+05-0.118895626068D+01-0.931322574616D-09 0.100000000000D+01 + -0.139344497070D+05 0.283445358276D+01 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 3 15 0.0 0.506319105625D-03 0.000000000000D+00 0.108000000000D+05 + -0.540710644531D+04 0.264364242554D+01 0.000000000000D+00 0.000000000000D+00 + -0.942602343750D+04-0.174210643768D+01 0.931322574616D-09-0.400000000000D+01 + -0.230394335938D+05 0.860452651978D-01 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 3 15 0.0 0.498471781611D-04 0.000000000000D+00 0.108000000000D+05 + -0.189061479492D+05 0.198248386383D+01 0.000000000000D+00 0.000000000000D+00 + 0.302504638672D+04-0.118884468079D+01 0.186264514923D-08 0.500000000000D+01 + -0.167811855469D+05-0.244724464416D+01 0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 3 15 0.0 0.123274512589D-03 0.181898940355D-11 0.108300000000D+05 + -0.215693129883D+05 0.246824264526D+00 0.931322574616D-09 0.000000000000D+00 + 0.135450053711D+05 0.578374862671D-01 0.186264514923D-08 0.600000000000D+01 + -0.124539746094D+04-0.357674026489D+01-0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 3 15 0.0 0.858698040247D-04 0.909494701773D-12 0.108000000000D+05 + -0.123717558594D+05-0.163909053803D+01 0.000000000000D+00 0.000000000000D+00 + 0.165072607422D+05 0.124468994141D+01 0.931322574616D-09 0.100000000000D+01 + 0.149926660156D+05-0.272678089142D+01-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 3 15 0.0-0.267550349236D-04-0.181898940355D-11 0.108000000000D+05 + 0.682162109375D+04-0.257884216309D+01-0.000000000000D+00 0.000000000000D+00 + 0.870957373047D+04 0.180530452728D+01-0.931322574616D-09-0.400000000000D+01 + 0.229719238281D+05 0.788316726685D-01-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 3 15 0.0 0.306209549308D-04 0.909494701773D-12 0.108000000000D+05 + 0.193162153320D+05-0.192412662506D+01-0.000000000000D+00 0.000000000000D+00 + -0.352470214844D+04 0.113207912445D+01-0.186264514923D-08 0.500000000000D+01 + 0.163474799805D+05 0.251821041107D+01-0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 3 15 0.0-0.630393624306D-04 0.000000000000D+00 0.108000000000D+05 + 0.215666538086D+05-0.232750892639D+00-0.931322574616D-09 0.000000000000D+00 + -0.135958100586D+05-0.484018325806D-01-0.186264514923D-08 0.600000000000D+01 + 0.127525000000D+04 0.356457710266D+01 0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 3 15 0.0 0.338098034263D-04 0.272848410532D-11 0.108000000000D+05 + 0.710510986328D+04 0.221279144287D+00-0.000000000000D+00 0.000000000000D+00 + 0.220894487305D+05 0.144524288178D+01 0.279396772385D-08-0.200000000000D+01 + -0.106598569336D+05 0.313177394867D+01-0.931322574616D-09 0.000000000000D+00 +10 22 1 13 3 15 0.0-0.788606703281D-04-0.000000000000D+00 0.108000000000D+05 + 0.109848315430D+05 0.284439086914D+00-0.931322574616D-09 0.000000000000D+00 + 0.814344775391D+04 0.293276405335D+01 0.279396772385D-08-0.700000000000D+01 + -0.215279418945D+05 0.126241588593D+01 0.000000000000D+00 0.000000000000D+00 +11 22 1 13 3 15 0.0 0.473149120808D-04-0.909494701773D-12 0.108000000000D+05 + 0.895548535156D+04 0.158063888550D+00-0.000000000000D+00 0.000000000000D+00 + -0.116299653320D+05 0.285402297974D+01 0.000000000000D+00 0.000000000000D+00 + -0.208521406250D+05-0.152408695221D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 3 15 0.0 0.294471159577D-03 0.363797880709D-11 0.108000000000D+05 + 0.160399658203D+04-0.839405059815D-01-0.000000000000D+00 0.000000000000D+00 + -0.245065952148D+05 0.958384513855D+00-0.186264514923D-08-0.100000000000D+01 + -0.692157470703D+04-0.340012931824D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 3 15 0.0-0.169826671481D-04-0.000000000000D+00 0.108000000000D+05 + -0.650331298828D+04-0.245241165161D+00 0.000000000000D+00 0.000000000000D+00 + -0.226548447266D+05-0.132154464722D+01-0.372529029846D-08-0.200000000000D+01 + 0.974277636719D+04-0.322926139831D+01 0.186264514923D-08 0.000000000000D+00 +14 22 1 13 3 15 0.0 0.731423497200D-04 0.000000000000D+00 0.116100000000D+05 + -0.111270556641D+05-0.257342338562D+00 0.931322574616D-09 0.000000000000D+00 + -0.683166699219D+04-0.298754501343D+01-0.279396772385D-08-0.700000000000D+01 + 0.219179282227D+05-0.106298160553D+01-0.931322574616D-09 0.000000000000D+00 +15 22 1 13 3 15 0.0 0.989334657788D-04-0.000000000000D+00 0.111300000000D+05 + -0.901901904297D+04-0.119700431824D+00 0.000000000000D+00 0.000000000000D+00 + 0.122367338867D+05-0.280075454712D+01-0.000000000000D+00 0.000000000000D+00 + 0.204969667969D+05 0.162091064453D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 3 15 0.0 0.492110848427D-03 0.272848410532D-11 0.113400000000D+05 + -0.233079677734D+05-0.666604995727D+00 0.931322574616D-09 0.000000000000D+00 + -0.932698828125D+04-0.843009948730D-01 0.000000000000D+00 0.400000000000D+01 + -0.461143750000D+04 0.352478218079D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 3 15 0.0 0.103663653135D-03 0.000000000000D+00 0.108000000000D+05 + -0.171217934570D+05-0.237217426300D+01 0.000000000000D+00 0.000000000000D+00 + 0.773421386719D+03-0.105335521698D+01 0.186264514923D-08-0.300000000000D+01 + -0.188481000977D+05 0.211396217346D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 3 15 0.0-0.174237415195D-03-0.909494701773D-12 0.108000000000D+05 + -0.332649804688D+04-0.279643917084D+01-0.000000000000D+00 0.000000000000D+00 + 0.993691259766D+04-0.153759574890D+01 0.279396772385D-08 0.300000000000D+01 + -0.232489545898D+05-0.257162094116D+00 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 3 15 0.0-0.629564747214D-04-0.000000000000D+00 0.108000000000D+05 + 0.156610092773D+05-0.149101924896D+01-0.931322574616D-09 0.000000000000D+00 + 0.152076997070D+05-0.105588626862D+01 0.279396772385D-08 0.200000000000D+01 + -0.132176948242D+05-0.297408294678D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 3 15 0.0-0.269644893706D-03-0.272848410532D-11 0.108000000000D+05 + 0.231919794922D+05 0.843998908997D+00-0.931322574616D-09 0.000000000000D+00 + 0.869007226562D+04 0.172171592712D+00 0.000000000000D+00 0.400000000000D+01 + 0.606164257812D+04-0.348191642761D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 3 15 0.0-0.157738104463D-03-0.181898940355D-11 0.108000000000D+05 + 0.165613793945D+05 0.242684555054D+01-0.000000000000D+00 0.000000000000D+00 + -0.118195263672D+04 0.109398269653D+01-0.186264514923D-08-0.300000000000D+01 + 0.193960253906D+05-0.199582672119D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 3 15 0.0-0.265892595053D-04-0.909494701773D-12 0.108000000000D+05 + -0.309360546875D+04 0.263726806641D+01 0.000000000000D+00 0.000000000000D+00 + -0.126232387695D+05 0.145410251617D+01-0.372529029846D-08 0.300000000000D+01 + 0.219467539062D+05 0.120831775665D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 3 15 0.0 0.754240900278D-04 0.181898940355D-11 0.117000000000D+05 + -0.163297485352D+05 0.145353507996D+01 0.931322574616D-09 0.000000000000D+00 + -0.150655693359D+05 0.926337242127D+00-0.279396772385D-08 0.200000000000D+01 + 0.125661152344D+05 0.300372028351D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 3 45 0.0 0.718142837286D-05-0.000000000000D+00 0.126000000000D+05 + 0.148117221680D+05 0.751416206360D+00-0.931322574616D-09 0.000000000000D+00 + -0.190058227539D+05-0.874022483826D+00-0.931322574616D-09 0.100000000000D+01 + -0.835946435547D+04 0.331974887848D+01 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 3 45 0.0 0.506320036948D-03 0.000000000000D+00 0.126000000000D+05 + -0.968132812500D+03 0.225150299072D+01-0.000000000000D+00 0.000000000000D+00 + -0.128398779297D+05-0.201469039917D+01 0.000000000000D+00-0.400000000000D+01 + -0.219928427734D+05 0.106904411316D+01 0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 3 45 0.0 0.498471781611D-04 0.000000000000D+00 0.126000000000D+05 + -0.151282011719D+05 0.216700077057D+01 0.931322574616D-09 0.000000000000D+00 + 0.328830566406D+03-0.179978370666D+01 0.186264514923D-08 0.500000000000D+01 + -0.204766274414D+05-0.163202476501D+01 0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 3 45 0.0 0.123277306557D-03 0.181898940355D-11 0.132000000000D+05 + -0.205093090820D+05 0.898428916931D+00 0.931322574616D-09 0.000000000000D+00 + 0.131262114258D+05-0.549467086792D+00 0.186264514923D-08 0.600000000000D+01 + -0.755202050781D+04-0.338502883911D+01-0.000000000000D+00 0.000000000000D+00 + 5 22 1 13 3 45 0.0 0.858707353473D-04 0.909494701773D-12 0.126000000000D+05 + -0.146417055664D+05-0.881702423096D+00 0.931322574616D-09 0.000000000000D+00 + 0.185531206055D+05 0.983659744263D+00 0.931322574616D-09 0.100000000000D+01 + 0.956777685547D+04-0.326173973084D+01-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 3 45 0.0-0.267596915364D-04-0.181898940355D-11 0.126000000000D+05 + 0.247134228516D+04-0.221791458130D+01 0.000000000000D+00 0.000000000000D+00 + 0.122457412109D+05 0.208798503876D+01-0.000000000000D+00-0.400000000000D+01 + 0.222228286133D+05-0.905821800232D+00-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 3 45 0.0 0.306228175759D-04 0.000000000000D+00 0.126000000000D+05 + 0.156240385742D+05-0.213063144684D+01-0.000000000000D+00 0.000000000000D+00 + -0.927645019531D+03 0.174809741974D+01-0.931322574616D-09 0.500000000000D+01 + 0.201930878906D+05 0.172707653046D+01-0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 3 45 0.0-0.630393624306D-04 0.000000000000D+00 0.126000000000D+05 + 0.205368041992D+05-0.879114151001D+00-0.931322574616D-09 0.000000000000D+00 + -0.131643984375D+05 0.553560256958D+00-0.186264514923D-08 0.600000000000D+01 + 0.755963623047D+04 0.337290191650D+01 0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 3 45 0.0 0.338144600391D-04 0.272848410532D-11 0.126000000000D+05 + 0.756127734375D+04 0.246109008789D+00-0.000000000000D+00 0.000000000000D+00 + 0.239434077148D+05 0.603366851807D+00 0.279396772385D-08-0.200000000000D+01 + -0.468452636719D+04 0.346448135376D+01-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 3 45 0.0-0.788616016507D-04-0.000000000000D+00 0.126000000000D+05 + 0.118169418945D+05 0.614232063293D+00-0.000000000000D+00 0.000000000000D+00 + 0.130277934570D+05 0.245220470429D+01 0.279396772385D-08-0.700000000000D+01 + -0.184520351562D+05 0.213325405121D+01-0.000000000000D+00 0.000000000000D+00 +11 22 1 13 3 45 0.0 0.473139807582D-04-0.909494701773D-12 0.137100000000D+05 + 0.965956250000D+04 0.629059791565D+00-0.000000000000D+00 0.000000000000D+00 + -0.626717773438D+04 0.305377674103D+01 0.931322574616D-09 0.000000000000D+00 + -0.227533281250D+05-0.574638366699D+00 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 3 45 0.0 0.294476747513D-03 0.272848410532D-11 0.126000000000D+05 + 0.169526513672D+04 0.218790054321D+00-0.000000000000D+00 0.000000000000D+00 + -0.220608989258D+05 0.173214054108D+01-0.931322574616D-09-0.100000000000D+01 + -0.126953232422D+05-0.297345924377D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 3 45 0.0-0.169835984707D-04-0.000000000000D+00 0.126000000000D+05 + -0.698813574219D+04-0.252789497375D+00-0.000000000000D+00 0.000000000000D+00 + -0.242635771484D+05-0.456650733948D+00-0.372529029846D-08-0.200000000000D+01 + 0.362843066406D+04-0.352029991150D+01 0.186264514923D-08 0.000000000000D+00 +14 22 1 13 3 45 0.0 0.731479376554D-04 0.000000000000D+00 0.126000000000D+05 + -0.119240209961D+05-0.604681015015D+00 0.000000000000D+00 0.000000000000D+00 + -0.118589628906D+05-0.255483627319D+01-0.372529029846D-08-0.700000000000D+01 + 0.191824379883D+05-0.195677280426D+01-0.000000000000D+00 0.000000000000D+00 +15 22 1 13 3 45 0.0 0.989334657788D-04-0.000000000000D+00 0.126000000000D+05 + -0.964352197266D+04-0.580897331238D+00 0.000000000000D+00 0.000000000000D+00 + 0.694145166016D+04-0.303292465210D+01-0.931322574616D-09 0.000000000000D+00 + 0.225857495117D+05 0.684955596924D+00-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 3 45 0.0 0.492117367685D-03 0.363797880709D-11 0.135000000000D+05 + -0.237807446289D+05 0.160553932190D+00 0.931322574616D-09 0.000000000000D+00 + -0.910274218750D+04 0.295868873596D+00-0.931322574616D-09 0.400000000000D+01 + 0.182929345703D+04 0.358520507812D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 3 45 0.0 0.103665515780D-03 0.000000000000D+00 0.126000000000D+05 + -0.210314897461D+05-0.192477512360D+01 0.931322574616D-09 0.000000000000D+00 + -0.598782714844D+03-0.483201980591D+00 0.931322574616D-09-0.300000000000D+01 + -0.143608642578D+05 0.283939170837D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 3 45 0.0-0.174240209162D-03-0.909494701773D-12 0.126000000000D+05 + -0.853621337891D+04-0.294231700897D+01 0.000000000000D+00 0.000000000000D+00 + 0.757211572266D+04-0.107024955749D+01 0.279396772385D-08 0.300000000000D+01 + -0.228065957031D+05 0.745472908020D+00 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 3 45 0.0-0.629574060440D-04-0.000000000000D+00 0.126000000000D+05 + 0.122805629883D+05-0.224549961090D+01-0.000000000000D+00 0.000000000000D+00 + 0.132805014648D+05-0.104158401489D+01 0.279396772385D-08 0.200000000000D+01 + -0.179914985352D+05-0.229564762116D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 3 45 0.0-0.269650481641D-03-0.272848410532D-11 0.126000000000D+05 + 0.239995585938D+05 0.306606292725D-01-0.931322574616D-09 0.000000000000D+00 + 0.859719628906D+04-0.239162445068D+00 0.931322574616D-09 0.400000000000D+01 + -0.359395507812D+03-0.360617637634D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 3 45 0.0-0.157740898430D-03-0.181898940355D-11 0.126000000000D+05 + 0.205997832031D+05 0.201254844665D+01-0.931322574616D-09 0.000000000000D+00 + 0.259334960938D+03 0.517563819885D+00-0.931322574616D-09-0.300000000000D+01 + 0.151024580078D+05-0.274377155304D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 3 45 0.0-0.265920534730D-04-0.909494701773D-12 0.126000000000D+05 + 0.201582763672D+04 0.299530601502D+01-0.000000000000D+00 0.000000000000D+00 + -0.103050698242D+05 0.109288787842D+01-0.372529029846D-08 0.300000000000D+01 + 0.232452050781D+05 0.225052833557D+00-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 3 45 0.0 0.754278153181D-04 0.181898940355D-11 0.135000000000D+05 + -0.130264877930D+05 0.219797992706D+01 0.000000000000D+00 0.000000000000D+00 + -0.133644033203D+05 0.921588897705D+00-0.372529029846D-08 0.200000000000D+01 + 0.174183413086D+05 0.235276126862D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 4 15 0.0 0.718142837286D-05-0.000000000000D+00 0.144000000000D+05 + 0.155378520508D+05 0.756845474243D-01-0.931322574616D-09 0.000000000000D+00 + -0.201112924805D+05-0.318382263184D+00-0.931322574616D-09 0.100000000000D+01 + -0.213780712891D+04 0.354829216003D+01 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 4 15 0.0 0.506321899593D-03 0.909494701773D-12 0.144300000000D+05 + 0.259211279297D+04 0.168322181702D+01-0.000000000000D+00 0.000000000000D+00 + -0.165289760742D+05-0.203873729706D+01 0.000000000000D+00-0.400000000000D+01 + -0.192415126953D+05 0.196788215637D+01 0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 4 15 0.0 0.498481094837D-04 0.000000000000D+00 0.144000000000D+05 + -0.112744067383D+05 0.207062149048D+01 0.000000000000D+00 0.000000000000D+00 + -0.339500146484D+04-0.231251525879D+01 0.931322574616D-09 0.500000000000D+01 + -0.225804008789D+05-0.690346717834D+00 0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 4 15 0.0 0.123280100524D-03 0.181898940355D-11 0.144000000000D+05 + -0.184730688477D+05 0.132122039795D+01 0.931322574616D-09 0.000000000000D+00 + 0.115012045898D+05-0.126553726196D+01 0.186264514923D-08 0.600000000000D+01 + -0.132736801758D+05-0.293097114563D+01-0.000000000000D+00 0.000000000000D+00 + 5 22 1 13 4 15 0.0 0.858716666698D-04 0.000000000000D+00 0.144300000000D+05 + -0.155752792969D+05-0.172889709473D+00 0.931322574616D-09 0.000000000000D+00 + 0.198953281250D+05 0.468954086304D+00 0.931322574616D-09 0.100000000000D+01 + 0.340219042969D+04-0.354437828064D+01-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 4 15 0.0-0.267634168267D-04-0.181898940355D-11 0.144000000000D+05 + -0.105623388672D+04-0.168012046814D+01 0.000000000000D+00 0.000000000000D+00 + 0.160781279297D+05 0.212499237060D+01 0.000000000000D+00-0.400000000000D+01 + 0.197529306641D+05-0.182071304321D+01-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 4 15 0.0 0.306237488985D-04 0.000000000000D+00 0.144000000000D+05 + 0.118144394531D+05-0.205782222748D+01-0.000000000000D+00 0.000000000000D+00 + 0.271564111328D+04 0.227644538879D+01-0.931322574616D-09 0.500000000000D+01 + 0.224849111328D+05 0.802874565124D+00-0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 4 15 0.0-0.630384311080D-04 0.000000000000D+00 0.144000000000D+05 + 0.185389931641D+05-0.129823684692D+01-0.931322574616D-09 0.000000000000D+00 + -0.115386586914D+05 0.126201152801D+01-0.186264514923D-08 0.600000000000D+01 + 0.132616054688D+05 0.292173862457D+01-0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 4 15 0.0 0.338191166520D-04 0.272848410532D-11 0.144000000000D+05 + 0.784719042969D+04 0.325708389282D-01 0.000000000000D+00 0.000000000000D+00 + 0.242550473633D+05-0.249390602112D+00 0.279396772385D-08-0.200000000000D+01 + 0.165167285156D+04 0.353020668030D+01-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 4 15 0.0-0.788634642959D-04-0.000000000000D+00 0.144000000000D+05 + 0.130796767578D+05 0.751195907593D+00 0.000000000000D+00 0.000000000000D+00 + 0.168439140625D+05 0.176025295258D+01 0.372529029846D-08-0.700000000000D+01 + -0.139469594727D+05 0.283995723724D+01-0.931322574616D-09 0.000000000000D+00 +11 22 1 13 4 15 0.0 0.473121181130D-04-0.909494701773D-12 0.144000000000D+05 + 0.112045722656D+05 0.107426357269D+01-0.000000000000D+00 0.000000000000D+00 + -0.821071777344D+03 0.294727325440D+01 0.186264514923D-08 0.000000000000D+00 + -0.228941787109D+05 0.419130325317D+00 0.931322574616D-09 0.000000000000D+00 +12 22 1 13 4 15 0.0 0.294482335448D-03 0.272848410532D-11 0.144000000000D+05 + 0.249056884766D+04 0.685322761536D+00-0.000000000000D+00 0.000000000000D+00 + -0.183951020508D+05 0.229997444153D+01 0.000000000000D+00-0.100000000000D+01 + -0.174875263672D+05-0.231655502319D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 4 15 0.0-0.169845297933D-04-0.000000000000D+00 0.144000000000D+05 + -0.726657226562D+04-0.172195434570D-01-0.000000000000D+00 0.000000000000D+00 + -0.243006401367D+05 0.405235290527D+00-0.279396772385D-08-0.200000000000D+01 + -0.276650830078D+04-0.353905868530D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 4 15 0.0 0.731488689780D-04 0.000000000000D+00 0.144000000000D+05 + -0.131949750977D+05-0.771543502808D+00-0.000000000000D+00 0.000000000000D+00 + -0.158946982422D+05-0.189927673340D+01-0.372529029846D-08-0.700000000000D+01 + 0.149643627930D+05-0.269959259033D+01 0.931322574616D-09 0.000000000000D+00 +15 22 1 13 4 15 0.0 0.989334657788D-04-0.000000000000D+00 0.144000000000D+05 + -0.111019453125D+05-0.102827262878D+01 0.000000000000D+00 0.000000000000D+00 + 0.150122900391D+04-0.296161651611D+01-0.186264514923D-08 0.000000000000D+00 + 0.229311162109D+05-0.303682327270D+00-0.931322574616D-09 0.000000000000D+00 +17 22 1 13 4 15 0.0 0.492123886943D-03 0.363797880709D-11 0.153000000000D+05 + -0.226949208984D+05 0.104574871063D+01 0.000000000000D+00 0.000000000000D+00 + -0.840923144531D+04 0.431938171387D+00-0.279396772385D-08 0.400000000000D+01 + 0.812893994141D+04 0.336907386780D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 4 15 0.0 0.103666447103D-03 0.000000000000D+00 0.144000000000D+05 + -0.238990380859D+05-0.122551345825D+01 0.931322574616D-09 0.000000000000D+00 + -0.103977734375D+04-0.349922180176D-01-0.000000000000D+00-0.300000000000D+01 + -0.875882324219D+04 0.334451389313D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 4 15 0.0-0.174243003130D-03-0.909494701773D-12 0.144000000000D+05 + -0.137318002930D+05-0.277807998657D+01 0.931322574616D-09 0.000000000000D+00 + 0.612168115234D+04-0.540392875671D+00 0.186264514923D-08 0.300000000000D+01 + -0.205999667969D+05 0.169040203095D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 4 15 0.0-0.629583373666D-04-0.000000000000D+00 0.144000000000D+05 + 0.768029394531D+04-0.282898235321D+01 0.000000000000D+00 0.000000000000D+00 + 0.116024145508D+05-0.787985801697D+00 0.372529029846D-08 0.200000000000D+01 + -0.213749604492D+05-0.143923377991D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 4 15 0.0-0.269656069577D-03-0.272848410532D-11 0.144000000000D+05 + 0.232546357422D+05-0.861710548401D+00-0.000000000000D+00 0.000000000000D+00 + 0.797377783203D+04-0.410851478577D+00 0.186264514923D-08 0.400000000000D+01 + -0.675258593750D+04-0.345115852356D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 4 15 0.0-0.157744623721D-03-0.181898940355D-11 0.144000000000D+05 + 0.236510878906D+05 0.134052658081D+01-0.931322574616D-09 0.000000000000D+00 + 0.749072265625D+03 0.533533096314D-01 0.000000000000D+00-0.300000000000D+01 + 0.964597998047D+04-0.327971076965D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 4 15 0.0-0.265939161181D-04-0.909494701773D-12 0.144000000000D+05 + 0.751276367188D+04 0.306029319763D+01-0.931322574616D-09 0.000000000000D+00 + -0.876250244141D+04 0.609550476074D+00-0.279396772385D-08 0.300000000000D+01 + 0.227465190430D+05-0.775542259216D+00-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 4 15 0.0 0.754306092858D-04 0.181898940355D-11 0.157800000000D+05 + -0.851634765625D+04 0.277766132355D+01-0.000000000000D+00 0.000000000000D+00 + -0.118870952148D+05 0.686119079590D+00-0.372529029846D-08 0.200000000000D+01 + 0.209270566406D+05 0.152060031891D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 4 45 0.0 0.718049705028D-05-0.000000000000D+00 0.162000000000D+05 + 0.151840053711D+05-0.434747695923D+00-0.186264514923D-08 0.000000000000D+00 + -0.200457622070D+05 0.413232803345D+00-0.931322574616D-09 0.100000000000D+01 + 0.424924707031D+04 0.350232124329D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 4 45 0.0 0.506323762238D-03 0.909494701773D-12 0.162000000000D+05 + 0.504865332031D+04 0.104382514954D+01-0.931322574616D-09 0.000000000000D+00 + -0.200091293945D+05-0.178067016602D+01-0.000000000000D+00-0.400000000000D+01 + -0.150008374023D+05 0.271318244934D+01 0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 4 45 0.0 0.498481094837D-04 0.000000000000D+00 0.162000000000D+05 + -0.781630322266D+04 0.173826694489D+01 0.000000000000D+00 0.000000000000D+00 + -0.787713525391D+04-0.262775230408D+01 0.931322574616D-09 0.500000000000D+01 + -0.229300942383D+05 0.304169654846D+00 0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 4 45 0.0 0.123282894492D-03 0.181898940355D-11 0.162900000000D+05 + -0.159152587891D+05 0.147500133514D+01 0.931322574616D-09 0.000000000000D+00 + 0.857056396484D+04-0.198097705841D+01 0.186264514923D-08 0.600000000000D+01 + -0.179667685547D+05-0.224959945679D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 4 45 0.0 0.858725979924D-04 0.000000000000D+00 0.162300000000D+05 + -0.153553315430D+05 0.384870529175D+00 0.931322574616D-09 0.000000000000D+00 + 0.201230458984D+05-0.241732597351D+00 0.186264514923D-08 0.100000000000D+01 + -0.302687646484D+04-0.355255699158D+01-0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 4 45 0.0-0.267680734396D-04-0.181898940355D-11 0.162000000000D+05 + -0.353207714844D+04-0.106757736206D+01 0.931322574616D-09 0.000000000000D+00 + 0.197260771484D+05 0.188081169128D+01 0.000000000000D+00-0.400000000000D+01 + 0.157529516602D+05-0.259482097626D+01-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 4 45 0.0 0.306256115437D-04 0.000000000000D+00 0.162000000000D+05 + 0.835969824219D+04-0.174658012390D+01-0.000000000000D+00 0.000000000000D+00 + 0.715421191406D+04 0.261679458618D+01-0.000000000000D+00 0.500000000000D+01 + 0.230459208984D+05-0.183688163757D+00-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 4 45 0.0-0.630374997854D-04 0.000000000000D+00 0.162000000000D+05 + 0.160236699219D+05-0.145136165619D+01-0.931322574616D-09 0.000000000000D+00 + -0.862169580078D+04 0.196942520142D+01-0.931322574616D-09 0.600000000000D+01 + 0.179429819336D+05 0.224628639221D+01-0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 4 45 0.0 0.338237732649D-04 0.272848410532D-11 0.162000000000D+05 + 0.754943652344D+04-0.394774436951D+00 0.000000000000D+00 0.000000000000D+00 + 0.231059506836D+05-0.100217437744D+01 0.186264514923D-08-0.200000000000D+01 + 0.786072900391D+04 0.332415008545D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 4 45 0.0-0.788643956184D-04-0.000000000000D+00 0.162000000000D+05 + 0.143740346680D+05 0.644276618957D+00 0.000000000000D+00 0.000000000000D+00 + 0.192972211914D+05 0.956337928772D+00 0.279396772385D-08-0.700000000000D+01 + -0.836025732422D+04 0.332711982727D+01-0.931322574616D-09 0.000000000000D+00 +11 22 1 13 4 45 0.0 0.473111867905D-04-0.909494701773D-12 0.162000000000D+05 + 0.134484521484D+05 0.138940334320D+01 0.000000000000D+00 0.000000000000D+00 + 0.417465966797D+04 0.256161403656D+01 0.279396772385D-08 0.000000000000D+00 + -0.212640502930D+05 0.138033390045D+01 0.000000000000D+00 0.000000000000D+00 +12 22 1 13 4 45 0.0 0.294487923384D-03 0.272848410532D-11 0.162000000000D+05 + 0.420620214844D+04 0.122426319122D+01-0.000000000000D+00 0.000000000000D+00 + -0.139449208984D+05 0.259604644775D+01 0.931322574616D-09-0.100000000000D+01 + -0.209268315430D+05-0.147997283936D+01 0.186264514923D-08 0.000000000000D+00 +13 22 1 13 4 45 0.0-0.169845297933D-04-0.000000000000D+00 0.162000000000D+05 + -0.692140625000D+04 0.431529998779D+00-0.931322574616D-09 0.000000000000D+00 + -0.228744453125D+05 0.115162181854D+01-0.186264514923D-08-0.200000000000D+01 + -0.894770166016D+04-0.328439998627D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 4 45 0.0 0.731498003006D-04 0.000000000000D+00 0.162000000000D+05 + -0.145585429688D+05-0.701435089111D+00-0.931322574616D-09 0.000000000000D+00 + -0.186191225586D+05-0.111557865143D+01-0.372529029846D-08-0.700000000000D+01 + 0.958933154297D+04-0.323390865326D+01 0.186264514923D-08 0.000000000000D+00 +15 22 1 13 4 45 0.0 0.989325344563D-04 0.000000000000D+00 0.171000000000D+05 + -0.132740126953D+05-0.135745716095D+01 0.000000000000D+00 0.000000000000D+00 + -0.355051611328D+04-0.260865306854D+01-0.279396772385D-08 0.000000000000D+00 + 0.215066103516D+05-0.126885032654D+01-0.931322574616D-09 0.000000000000D+00 +17 22 1 13 4 45 0.0 0.492130406201D-03 0.363797880709D-11 0.171000000000D+05 + -0.200526625977D+05 0.187018394470D+01 0.000000000000D+00 0.000000000000D+00 + -0.769982666016D+04 0.316120147705D+00-0.372529029846D-08 0.400000000000D+01 + 0.138017109375D+05 0.289319992065D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 4 45 0.0 0.103668309748D-03 0.000000000000D+00 0.184200000000D+05 + -0.253432827148D+05-0.360404968262D+00 0.931322574616D-09 0.000000000000D+00 + -0.848192382812D+03 0.208807945252D+00-0.931322574616D-09-0.300000000000D+01 + -0.247683300781D+04 0.358995819092D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 4 45 0.0-0.174245797098D-03-0.909494701773D-12 0.162000000000D+05 + -0.183553159180D+05-0.231211757660D+01 0.931322574616D-09 0.000000000000D+00 + 0.559622216797D+04-0.609445571899D-01 0.931322574616D-09 0.300000000000D+01 + -0.167998344727D+05 0.250452899933D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 4 45 0.0-0.629592686892D-04-0.000000000000D+00 0.162000000000D+05 + 0.225319238281D+04-0.315216350555D+01 0.931322574616D-09 0.000000000000D+00 + 0.105445849609D+05-0.367306709290D+00 0.279396772385D-08 0.200000000000D+01 + -0.231053505859D+05-0.470801353455D+00 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 4 45 0.0-0.269661657512D-03-0.272848410532D-11 0.162000000000D+05 + 0.209219062500D+05-0.171339321136D+01 0.000000000000D+00 0.000000000000D+00 + 0.727220361328D+04-0.327337265015D+00 0.279396772385D-08 0.400000000000D+01 + -0.126227607422D+05-0.302883434296D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 4 45 0.0-0.157747417688D-03-0.909494701773D-12 0.162000000000D+05 + 0.253192612305D+05 0.492093086243D+00-0.931322574616D-09 0.000000000000D+00 + 0.570681640625D+03-0.213327407837D+00 0.931322574616D-09-0.300000000000D+01 + 0.344746044922D+04-0.356302928925D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 4 45 0.0-0.265967100859D-04-0.909494701773D-12 0.162000000000D+05 + 0.128430512695D+05 0.281052589416D+01-0.931322574616D-09 0.000000000000D+00 + -0.811822753906D+04 0.113652229309D+00-0.186264514923D-08 0.300000000000D+01 + 0.204893364258D+05-0.171615505218D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 4 45 0.0 0.754343345761D-04 0.181898940355D-11 0.162000000000D+05 + -0.317847900391D+04 0.310598373413D+01-0.931322574616D-09 0.000000000000D+00 + -0.109917841797D+05 0.289240837097D+00-0.372529029846D-08 0.200000000000D+01 + 0.228220561523D+05 0.571328163147D+00-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 5 15 0.0 0.718049705028D-05-0.000000000000D+00 0.180000000000D+05 + 0.141116308594D+05-0.715147018433D+00-0.186264514923D-08 0.000000000000D+00 + -0.185757304688D+05 0.122416400909D+01-0.931322574616D-09 0.100000000000D+01 + 0.103075424805D+05 0.318538284302D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 5 15 0.0 0.506325624883D-03 0.909494701773D-12 0.180000000000D+05 + 0.637464453125D+04 0.445297241211D+00-0.931322574616D-09 0.000000000000D+00 + -0.227764897461D+05-0.125260734558D+01-0.000000000000D+00-0.400000000000D+01 + -0.960077929688D+04 0.324783515930D+01 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 5 15 0.0 0.498490408063D-04 0.000000000000D+00 0.180000000000D+05 + -0.510958837891D+04 0.125177764893D+01 0.000000000000D+00 0.000000000000D+00 + -0.126934086914D+05-0.267521953583D+01 0.000000000000D+00 0.500000000000D+01 + -0.215002470703D+05 0.127399921417D+01 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 5 15 0.0 0.123284757137D-03 0.181898940355D-11 0.180000000000D+05 + -0.133219785156D+05 0.136543178558D+01 0.931322574616D-09 0.000000000000D+00 + 0.443871630859D+04-0.258185863495D+01 0.931322574616D-09 0.600000000000D+01 + -0.212672661133D+05-0.139373397827D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 5 15 0.0 0.858725979924D-04 0.000000000000D+00 0.180000000000D+05 + -0.143253095703D+05 0.718082427978D+00 0.186264514923D-08 0.000000000000D+00 + 0.189628393555D+05-0.105547904968D+01 0.186264514923D-08 0.100000000000D+01 + -0.922144677734D+04-0.328551292419D+01-0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 5 15 0.0-0.267727300525D-04-0.181898940355D-11 0.180000000000D+05 + -0.492097265625D+04-0.490443229675D+00 0.931322574616D-09 0.000000000000D+00 + 0.226852460938D+05 0.136513328552D+01 0.931322574616D-09-0.400000000000D+01 + 0.105324565430D+05-0.316798877716D+01-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 5 15 0.0 0.306265428662D-04 0.000000000000D+00 0.197700000000D+05 + 0.562346875000D+04-0.127495193481D+01 0.000000000000D+00 0.000000000000D+00 + 0.119783789062D+05 0.269560813904D+01-0.000000000000D+00 0.500000000000D+01 + 0.218315922852D+05-0.115701103210D+01-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 5 15 0.0-0.630374997854D-04 0.000000000000D+00 0.180000000000D+05 + 0.134708154297D+05-0.134474849701D+01-0.931322574616D-09 0.000000000000D+00 + -0.451661865234D+04 0.256416702270D+01-0.931322574616D-09 0.600000000000D+01 + 0.212446362305D+05 0.139859867096D+01-0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 5 15 0.0 0.338284298778D-04 0.272848410532D-11 0.180000000000D+05 + 0.633571972656D+04-0.971592903137D+00 0.000000000000D+00 0.000000000000D+00 + 0.207636757812D+05-0.156196594238D+01 0.931322574616D-09-0.200000000000D+01 + 0.134646875000D+05 0.286220550537D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 5 15 0.0-0.788662582636D-04-0.000000000000D+00 0.180000000000D+05 + 0.152470922852D+05 0.285594940186D+00 0.931322574616D-09 0.000000000000D+00 + 0.202882758789D+05 0.154870033264D+00 0.279396772385D-08-0.700000000000D+01 + -0.212453076172D+04 0.355629444122D+01-0.186264514923D-08 0.000000000000D+00 +11 22 1 13 5 15 0.0 0.473102554679D-04-0.909494701773D-12 0.180000000000D+05 + 0.160774467773D+05 0.149077701569D+01 0.931322574616D-09 0.000000000000D+00 + 0.827393505859D+04 0.196564388275D+01 0.279396772385D-08 0.000000000000D+00 + -0.179892812500D+05 0.223464298248D+01-0.000000000000D+00 0.000000000000D+00 +12 22 1 13 5 15 0.0 0.294493511319D-03 0.272848410532D-11 0.180000000000D+05 + 0.687675830078D+04 0.172801303864D+01 0.000000000000D+00 0.000000000000D+00 + -0.922722119141D+04 0.259720993042D+01 0.186264514923D-08-0.100000000000D+01 + -0.227462006836D+05-0.528315544128D+00 0.186264514923D-08 0.000000000000D+00 +13 22 1 13 5 15 0.0-0.169854611158D-04-0.000000000000D+00 0.180000000000D+05 + -0.562646679688D+04 0.102364730835D+01-0.000000000000D+00 0.000000000000D+00 + -0.202801157227D+05 0.169033908844D+01-0.000000000000D+00-0.200000000000D+01 + -0.144378823242D+05-0.277628993988D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 5 15 0.0 0.731507316232D-04 0.000000000000D+00 0.180000000000D+05 + -0.155681528320D+05-0.379537582398D+00-0.931322574616D-09 0.000000000000D+00 + -0.199011108398D+05-0.315731048584D+00-0.279396772385D-08-0.700000000000D+01 + 0.347267529297D+04-0.351823806763D+01 0.186264514923D-08 0.000000000000D+00 +15 22 1 13 5 15 0.0 0.989316031337D-04 0.000000000000D+00 0.189000000000D+05 + -0.158656313477D+05-0.148250865936D+01-0.931322574616D-09 0.000000000000D+00 + -0.775861132812D+04-0.203790664673D+01-0.372529029846D-08 0.000000000000D+00 + 0.184220947266D+05-0.213620185852D+01 0.000000000000D+00 0.000000000000D+00 +17 22 1 13 5 15 0.0 0.492136925459D-03 0.363797880709D-11 0.189000000000D+05 + -0.160659067383D+05 0.252273750305D+01-0.931322574616D-09 0.000000000000D+00 + -0.740045605469D+04-0.138025283813D-01-0.372529029846D-08 0.400000000000D+01 + 0.184102822266D+05 0.219427204132D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 5 15 0.0 0.103670172393D-03 0.000000000000D+00 0.189000000000D+05 + -0.251645180664D+05 0.557707786560D+00 0.000000000000D+00 0.000000000000D+00 + -0.437905273438D+03 0.204447746277D+00-0.186264514923D-08-0.300000000000D+01 + 0.399740869141D+04 0.355677700043D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 5 15 0.0-0.174248591065D-03-0.909494701773D-12 0.180000000000D+05 + -0.219066206055D+05-0.159943962097D+01 0.931322574616D-09 0.000000000000D+00 + 0.581056005859D+04 0.266614913940D+00 0.000000000000D+00 0.300000000000D+01 + -0.117002309570D+05 0.312488079071D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 5 15 0.0-0.629611313343D-04-0.000000000000D+00 0.180000000000D+05 + -0.347989257812D+04-0.316427230835D+01 0.931322574616D-09 0.000000000000D+00 + 0.103200634766D+05 0.119578361511D+00 0.279396772385D-08 0.200000000000D+01 + -0.230474667969D+05 0.534823417664D+00 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 5 15 0.0-0.269667245448D-03-0.272848410532D-11 0.180000000000D+05 + 0.171804145508D+05-0.240920734405D+01 0.000000000000D+00 0.000000000000D+00 + 0.693073974609D+04-0.195922851562D-01 0.279396772385D-08 0.400000000000D+01 + -0.175153212891D+05-0.237201023102D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 5 15 0.0-0.157751142979D-03-0.909494701773D-12 0.180000000000D+05 + 0.253821191406D+05-0.423483848572D+00-0.000000000000D+00 0.000000000000D+00 + 0.129443847656D+03-0.234289169312D+00 0.186264514923D-08-0.300000000000D+01 + -0.301611572266D+04-0.357236480713D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 5 15 0.0-0.265995040536D-04-0.909494701773D-12 0.180000000000D+05 + 0.174566816406D+05 0.227229595184D+01-0.931322574616D-09 0.000000000000D+00 + -0.829468505859D+04-0.285099029541D+00-0.931322574616D-09 0.300000000000D+01 + 0.166481655273D+05-0.252410602570D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 5 15 0.0 0.754380598664D-04 0.181898940355D-11 0.180000000000D+05 + 0.248371923828D+04 0.313338375092D+01-0.186264514923D-08 0.000000000000D+00 + -0.108846923828D+05-0.172038078308D+00-0.279396772385D-08 0.200000000000D+01 + 0.229572968750D+05-0.422065734863D+00-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 5 45 0.0 0.718049705028D-05-0.000000000000D+00 0.198000000000D+05 + 0.127622626953D+05-0.742434501648D+00-0.186264514923D-08 0.000000000000D+00 + -0.156590483398D+05 0.200136852264D+01-0.931322574616D-09 0.100000000000D+01 + 0.155684052734D+05 0.262205600738D+01-0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 5 45 0.0 0.506328418851D-03 0.909494701773D-12 0.198000000000D+05 + 0.673696386719D+04-0.121259689331D-01-0.186264514923D-08 0.000000000000D+00 + -0.243895541992D+05-0.510808944702D+00-0.000000000000D+00-0.400000000000D+01 + -0.345975341797D+04 0.353127384186D+01 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 5 45 0.0 0.498499721289D-04 0.000000000000D+00 0.204000000000D+05 + -0.333984423828D+04 0.715542793274D+00-0.000000000000D+00 0.000000000000D+00 + -0.173287944336D+05-0.242570877075D+01 0.000000000000D+00 0.500000000000D+01 + -0.184038344727D+05 0.214387416840D+01 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 5 45 0.0 0.123287551105D-03 0.181898940355D-11 0.198000000000D+05 + -0.111284829102D+05 0.104243278503D+01 0.931322574616D-09 0.000000000000D+00 + -0.595020996094D+03-0.296861743927D+01 0.931322574616D-09 0.600000000000D+01 + -0.229192221680D+05-0.429838180542D+00 0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 5 45 0.0 0.858735293150D-04 0.000000000000D+00 0.198000000000D+05 + -0.129254848633D+05 0.794105529785D+00 0.186264514923D-08 0.000000000000D+00 + 0.163293750000D+05-0.185907173157D+01 0.931322574616D-09 0.100000000000D+01 + -0.147016137695D+05-0.276395702362D+01 0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 5 45 0.0-0.267764553428D-04-0.181898940355D-11 0.210300000000D+05 + -0.537927050781D+04-0.483789443970D-01 0.186264514923D-08 0.000000000000D+00 + 0.245093232422D+05 0.631957054138D+00 0.931322574616D-09-0.400000000000D+01 + 0.449586425781D+04-0.349568367004D+01-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 5 45 0.0 0.306274741888D-04 0.000000000000D+00 0.199800000000D+05 + 0.380511035156D+04-0.744817733765D+00 0.000000000000D+00 0.000000000000D+00 + 0.166800244141D+05 0.247867488861D+01 0.000000000000D+00 0.500000000000D+01 + 0.189336777344D+05-0.204222297669D+01-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 5 45 0.0-0.630365684629D-04 0.000000000000D+00 0.198000000000D+05 + 0.113094619141D+05-0.102782630920D+01-0.931322574616D-09 0.000000000000D+00 + 0.482549316406D+03 0.294867038727D+01-0.931322574616D-09 0.600000000000D+01 + 0.229133842773D+05 0.443593978882D+00-0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 5 45 0.0 0.338330864906D-04 0.272848410532D-11 0.198000000000D+05 + 0.401792285156D+04-0.160423564911D+01 0.000000000000D+00 0.000000000000D+00 + 0.176358671875D+05-0.186818504334D+01 0.000000000000D+00-0.200000000000D+01 + 0.180320102539D+05 0.217972755432D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 5 45 0.0-0.788671895862D-04-0.000000000000D+00 0.198000000000D+05 + 0.152727050781D+05-0.287526130676D+00 0.931322574616D-09 0.000000000000D+00 + 0.199230776367D+05-0.533467292786D+00 0.186264514923D-08-0.700000000000D+01 + 0.427621435547D+04 0.350922393799D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 5 45 0.0 0.473083928227D-04-0.909494701773D-12 0.198000000000D+05 + 0.186569555664D+05 0.132982063293D+01 0.931322574616D-09 0.000000000000D+00 + 0.111839389648D+05 0.125840950012D+01 0.279396772385D-08 0.000000000000D+00 + -0.133233603516D+05 0.291602706909D+01-0.931322574616D-09 0.000000000000D+00 +12 22 1 13 5 45 0.0 0.294499099255D-03 0.272848410532D-11 0.202500000000D+05 + 0.103421987305D+05 0.209103202820D+01 0.000000000000D+00 0.000000000000D+00 + -0.475902685547D+04 0.232636070252D+01 0.186264514923D-08-0.100000000000D+01 + -0.228038627930D+05 0.464737892151D+00 0.931322574616D-09 0.000000000000D+00 +13 22 1 13 5 45 0.0-0.169863924384D-04-0.000000000000D+00 0.198000000000D+05 + -0.320889746094D+04 0.166098690033D+01-0.000000000000D+00 0.000000000000D+00 + -0.169490947266D+05 0.196408748627D+01 0.000000000000D+00-0.200000000000D+01 + -0.188135996094D+05-0.205415630341D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 5 45 0.0 0.731507316232D-04 0.000000000000D+00 0.198000000000D+05 + -0.157909487305D+05 0.164203643799D+00-0.931322574616D-09 0.000000000000D+00 + -0.198130898438D+05 0.389200210571D+00-0.186264514923D-08-0.700000000000D+01 + -0.291261132812D+04-0.353042316437D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 5 45 0.0 0.989316031337D-04 0.000000000000D+00 0.207000000000D+05 + -0.184561171875D+05-0.135080242157D+01-0.186264514923D-08 0.000000000000D+00 + -0.108131494141D+05-0.134464168549D+01-0.372529029846D-08 0.000000000000D+00 + 0.139153637695D+05-0.283884525299D+01 0.931322574616D-09 0.000000000000D+00 +17 22 1 13 5 45 0.0 0.492143444717D-03 0.363797880709D-11 0.201900000000D+05 + -0.111261420898D+05 0.291772842407D+01-0.186264514923D-08 0.000000000000D+00 + -0.783248779297D+04-0.481135368347D+00-0.279396772385D-08 0.400000000000D+01 + 0.215992382812D+05 0.132602787018D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 5 45 0.0 0.103671103716D-03 0.909494701773D-12 0.207000000000D+05 + -0.233768149414D+05 0.140739822388D+01 0.000000000000D+00 0.000000000000D+00 + -0.260616210938D+03-0.457572937012D-01-0.279396772385D-08-0.300000000000D+01 + 0.101617216797D+05 0.324791812897D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 5 45 0.0-0.174251385033D-03-0.909494701773D-12 0.202200000000D+05 + -0.240205922852D+05-0.733035087585D+00 0.931322574616D-09 0.000000000000D+00 + 0.642042822266D+04 0.369564056396D+00-0.931322574616D-09 0.300000000000D+01 + -0.569568652344D+04 0.350349235535D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 5 45 0.0-0.629620626569D-04-0.909494701773D-12 0.198000000000D+05 + -0.894831201172D+04-0.286154365540D+01 0.186264514923D-08 0.000000000000D+00 + 0.109473359375D+05 0.560517311096D+00 0.186264514923D-08 0.200000000000D+01 + -0.212044882812D+05 0.149970817566D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 5 45 0.0-0.269672833383D-03-0.272848410532D-11 0.198000000000D+05 + 0.123982646484D+05-0.285713863373D+01 0.931322574616D-09 0.000000000000D+00 + 0.729361425781D+04 0.440421104431D+00 0.279396772385D-08 0.400000000000D+01 + -0.210516308594D+05-0.153172016144D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 5 45 0.0-0.157753936946D-03-0.909494701773D-12 0.198000000000D+05 + 0.238265620117D+05-0.128627204895D+01 0.000000000000D+00 0.000000000000D+00 + -0.122513671875D+03-0.638961791992D-02 0.186264514923D-08-0.300000000000D+01 + -0.924787988281D+04-0.330711269379D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 5 45 0.0-0.266013666987D-04-0.909494701773D-12 0.198000000000D+05 + 0.208917333984D+05 0.151587486267D+01-0.931322574616D-09 0.000000000000D+00 + -0.903095703125D+04-0.495639801025D+00 0.000000000000D+00 0.300000000000D+01 + 0.115199277344D+05-0.313694286346D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 5 45 0.0 0.754417851567D-04 0.181898940355D-11 0.198000000000D+05 + 0.791752392578D+04 0.285537815094D+01-0.186264514923D-08 0.000000000000D+00 + -0.115847915039D+05-0.589690208435D+00-0.186264514923D-08 0.200000000000D+01 + 0.213220180664D+05-0.138320827484D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 6 15 0.0 0.717956572771D-05-0.000000000000D+00 0.216000000000D+05 + 0.115784257812D+05-0.538509368897D+00-0.186264514923D-08 0.000000000000D+00 + -0.114581088867D+05 0.263374900818D+01-0.931322574616D-09 0.100000000000D+01 + 0.196249941406D+05 0.185600948334D+01-0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 6 15 0.0 0.506331212819D-03 0.909494701773D-12 0.216000000000D+05 + 0.645965185547D+04-0.256472587585D+00-0.186264514923D-08 0.000000000000D+00 + -0.245416855469D+05 0.353144645691D+00-0.000000000000D+00-0.400000000000D+01 + 0.294799267578D+04 0.354239368439D+01 0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 6 15 0.0 0.498509034514D-04 0.000000000000D+00 0.216000000000D+05 + -0.249783544922D+04 0.238495826721D+00-0.931322574616D-09 0.000000000000D+00 + -0.212570966797D+05-0.189624881744D+01-0.000000000000D+00 0.500000000000D+01 + -0.138828125000D+05 0.284667873383D+01 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 6 15 0.0 0.123290345073D-03 0.181898940355D-11 0.216000000000D+05 + -0.964659521484D+04 0.591169357300D+00 0.000000000000D+00 0.000000000000D+00 + -0.607779980469D+04-0.307264518738D+01 0.000000000000D+00 0.600000000000D+01 + -0.227947397461D+05 0.567210197449D+00 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 6 15 0.0 0.858744606376D-04 0.000000000000D+00 0.216000000000D+05 + -0.116138173828D+05 0.626112937927D+00 0.186264514923D-08 0.000000000000D+00 + 0.123456801758D+05-0.253740978241D+01 0.931322574616D-09 0.100000000000D+01 + -0.190429770508D+05-0.202843856811D+01 0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 6 15 0.0-0.267811119556D-04-0.181898940355D-11 0.216000000000D+05 + -0.522045605469D+04 0.186155319214D+00 0.186264514923D-08 0.000000000000D+00 + 0.248833559570D+05-0.228709220886D+00 0.931322574616D-09-0.400000000000D+01 + -0.188906201172D+04-0.355252170563D+01-0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 6 15 0.0 0.306274741888D-04 0.000000000000D+00 0.216000000000D+05 + 0.291190332031D+04-0.264656066894D+00 0.931322574616D-09 0.000000000000D+00 + 0.207300732422D+05 0.197736072540D+01 0.000000000000D+00 0.500000000000D+01 + 0.145737788086D+05-0.277084541321D+01-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 6 15 0.0-0.630356371403D-04 0.000000000000D+00 0.216000000000D+05 + 0.984698046875D+04-0.584348678589D+00-0.000000000000D+00 0.000000000000D+00 + 0.593106689453D+04 0.305535984039D+01-0.000000000000D+00 0.600000000000D+01 + 0.228208994141D+05-0.545777320862D+00-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 6 15 0.0 0.338368117809D-04 0.272848410532D-11 0.216000000000D+05 + 0.591625000000D+03-0.218508529663D+01-0.000000000000D+00 0.000000000000D+00 + 0.142025737305D+05-0.190196800232D+01-0.931322574616D-09-0.200000000000D+01 + 0.212104277344D+05 0.132888698578D+01-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 6 15 0.0-0.788690522313D-04-0.000000000000D+00 0.216000000000D+05 + 0.141288920898D+05-0.998235702515D+00 0.931322574616D-09 0.000000000000D+00 + 0.184897255859D+05-0.101981925964D+01 0.931322574616D-09-0.700000000000D+01 + 0.103446054688D+05 0.318942928314D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 6 15 0.0 0.473074615002D-04-0.909494701773D-12 0.216000000000D+05 + 0.207036665039D+05 0.901850700378D+00 0.186264514923D-08 0.000000000000D+00 + 0.128052333984D+05 0.552715301514D+00 0.279396772385D-08 0.000000000000D+00 + -0.762727392578D+04 0.337185001373D+01-0.931322574616D-09 0.000000000000D+00 +12 22 1 13 6 15 0.0 0.294503755867D-03 0.272848410532D-11 0.216000000000D+05 + 0.142680664062D+05 0.222789382935D+01 0.931322574616D-09 0.000000000000D+00 + -0.977341796875D+03 0.184819030762D+01 0.279396772385D-08-0.100000000000D+01 + -0.210945805664D+05 0.142215347290D+01 0.000000000000D+00 0.000000000000D+00 +13 22 1 13 6 15 0.0-0.169863924384D-04-0.000000000000D+00 0.216000000000D+05 + 0.313965332031D+03 0.223333930969D+01 0.000000000000D+00 0.000000000000D+00 + -0.133775336914D+05 0.195914745331D+01 0.931322574616D-09-0.200000000000D+01 + -0.217376523438D+05-0.117377567291D+01 0.186264514923D-08 0.000000000000D+00 +14 22 1 13 6 15 0.0 0.731516629458D-04 0.000000000000D+00 0.216000000000D+05 + -0.148854838867D+05 0.859514236450D+00-0.931322574616D-09 0.000000000000D+00 + -0.186129199219D+05 0.907063484192D+00-0.931322574616D-09-0.700000000000D+01 + -0.907250341797D+04-0.326941490173D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 6 15 0.0 0.989316031337D-04 0.000000000000D+00 0.225000000000D+05 + -0.205676162109D+05-0.952457427978D+00-0.186264514923D-08 0.000000000000D+00 + -0.125924204102D+05-0.639785766602D+00-0.279396772385D-08 0.000000000000D+00 + 0.833400439453D+04-0.332246875763D+01 0.186264514923D-08 0.000000000000D+00 +17 22 1 13 6 15 0.0 0.492149963975D-03 0.272848410532D-11 0.216000000000D+05 + -0.574552636719D+04 0.300855922699D+01-0.186264514923D-08 0.000000000000D+00 + -0.915367675781D+04-0.983405113220D+00-0.279396772385D-08 0.400000000000D+01 + 0.231222592773D+05 0.355177879333D+00-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 6 15 0.0 0.103672966361D-03 0.909494701773D-12 0.225000000000D+05 + -0.202057446289D+05 0.207830142975D+01-0.931322574616D-09 0.000000000000D+00 + -0.722181152344D+03-0.493945121765D+00-0.279396772385D-08-0.300000000000D+01 + 0.155388090820D+05 0.268787384033D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 6 15 0.0-0.174254179001D-03-0.909494701773D-12 0.231900000000D+05 + -0.245228618164D+05 0.171026229858D+00 0.000000000000D+00 0.000000000000D+00 + 0.698637792969D+04 0.216414451599D+00-0.186264514923D-08 0.300000000000D+01 + 0.749336425781D+03 0.361113452911D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 6 15 0.0-0.629639253020D-04-0.909494701773D-12 0.216000000000D+05 + -0.136181279297D+05-0.228799152374D+01 0.186264514923D-08 0.000000000000D+00 + 0.122473720703D+05 0.851418495178D+00 0.931322574616D-09 0.200000000000D+01 + -0.177181235352D+05 0.234887599945D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 6 15 0.0-0.269678421318D-03-0.272848410532D-11 0.216000000000D+05 + 0.707683105469D+04-0.300312232971D+01 0.186264514923D-08 0.000000000000D+00 + 0.854755615234D+04 0.952219963074D+00 0.279396772385D-08 0.400000000000D+01 + -0.229583876953D+05-0.573191642761D+00 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 6 15 0.0-0.157757662237D-03-0.181898940355D-11 0.216000000000D+05 + 0.208507124023D+05-0.198465633392D+01 0.931322574616D-09 0.000000000000D+00 + 0.231113281250D+03 0.427988052368D+00 0.279396772385D-08-0.300000000000D+01 + -0.147686777344D+05-0.278743934631D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 6 15 0.0-0.266060233116D-04-0.909494701773D-12 0.216000000000D+05 + 0.228444062500D+05 0.644294738770D+00-0.000000000000D+00 0.000000000000D+00 + -0.993109716797D+04-0.461198806763D+00 0.931322574616D-09 0.300000000000D+01 + 0.550105615234D+04-0.350727176666D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 6 15 0.0 0.754455104470D-04 0.181898940355D-11 0.216000000000D+05 + 0.126038525391D+05 0.231332397461D+01-0.186264514923D-08 0.000000000000D+00 + -0.129205683594D+05-0.863168716431D+00-0.000000000000D+00 0.200000000000D+01 + 0.180416611328D+05-0.223809623718D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 6 45 0.0 0.717956572771D-05-0.000000000000D+00 0.234000000000D+05 + 0.109253828125D+05-0.166005134582D+00-0.931322574616D-09 0.000000000000D+00 + -0.631981005859D+04 0.303022003174D+01-0.000000000000D+00 0.100000000000D+01 + 0.221637856445D+05 0.946578025818D+00-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 6 45 0.0 0.506334006786D-03 0.909494701773D-12 0.234000000000D+05 + 0.596166162109D+04-0.255681037903D+00-0.186264514923D-08 0.000000000000D+00 + -0.231122866211D+05 0.122691249847D+01-0.000000000000D+00-0.400000000000D+01 + 0.912891162109D+04 0.328095722199D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 6 45 0.0 0.498518347740D-04 0.000000000000D+00 0.234000000000D+05 + -0.238817871094D+04-0.844373703003D-01-0.931322574616D-09 0.000000000000D+00 + -0.240230957031D+05-0.114762401581D+01-0.000000000000D+00 0.500000000000D+01 + -0.828867529297D+04 0.332859420776D+01 0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 6 45 0.0 0.123293139040D-03 0.181898940355D-11 0.234000000000D+05 + -0.901397167969D+04 0.117144584656D+00 0.000000000000D+00 0.000000000000D+00 + -0.114702695312D+05-0.286781501770D+01 0.000000000000D+00 0.600000000000D+01 + -0.209038906250D+05 0.152000141144D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 6 45 0.0 0.858753919601D-04 0.909494701773D-12 0.234000000000D+05 + -0.107848383789D+05 0.270308494568D+00 0.186264514923D-08 0.000000000000D+00 + 0.732944921875D+04-0.299228763580D+01 0.931322574616D-09 0.100000000000D+01 + -0.219096704102D+05-0.113612461090D+01 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 6 45 0.0-0.267848372459D-04-0.181898940355D-11 0.234000000000D+05 + -0.485446435547D+04 0.179952621460D+00 0.186264514923D-08 0.000000000000D+00 + 0.236764086914D+05-0.110518550873D+01 0.931322574616D-09-0.400000000000D+01 + -0.812772314453D+04-0.333426380158D+01-0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 6 45 0.0 0.306284055114D-04 0.000000000000D+00 0.234000000000D+05 + 0.276376660156D+04 0.688285827637D-01 0.186264514923D-08 0.000000000000D+00 + 0.236606401367D+05 0.124746799469D+01 0.000000000000D+00 0.500000000000D+01 + 0.908699023438D+04-0.328608703613D+01-0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 6 45 0.0-0.630356371403D-04 0.000000000000D+00 0.244200000000D+05 + 0.921957421875D+04-0.118002891541D+00 0.000000000000D+00 0.000000000000D+00 + 0.112983515625D+05 0.285778427124D+01-0.000000000000D+00 0.600000000000D+01 + 0.209733984375D+05-0.149389266968D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 6 45 0.0 0.338414683938D-04 0.181898940355D-11 0.234000000000D+05 + -0.375457812500D+04-0.261030578613D+01-0.000000000000D+00 0.000000000000D+00 + 0.109378583984D+05-0.168863391876D+01-0.186264514923D-08-0.200000000000D+01 + 0.227538955078D+05 0.374805450439D+00-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 6 45 0.0-0.788709148765D-04-0.000000000000D+00 0.234000000000D+05 + 0.116586406250D+05-0.174263477325D+01 0.931322574616D-09 0.000000000000D+00 + 0.164054008789D+05-0.125156307220D+01 0.000000000000D+00-0.700000000000D+01 + 0.156091694336D+05 0.262197208404D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 6 45 0.0 0.473065301776D-04-0.909494701773D-12 0.234000000000D+05 + 0.217669970703D+05 0.247514724731D+00 0.186264514923D-08 0.000000000000D+00 + 0.132398671875D+05-0.434932708740D-01 0.186264514923D-08 0.000000000000D+00 + -0.134154248047D+04 0.356694889069D+01-0.186264514923D-08 0.000000000000D+00 +12 22 1 13 6 45 0.0 0.294509343803D-03 0.272848410532D-11 0.234000000000D+05 + 0.181957221680D+05 0.208809757233D+01 0.186264514923D-08 0.000000000000D+00 + 0.182697070312D+04 0.125803184509D+01 0.279396772385D-08-0.100000000000D+01 + -0.177502944336D+05 0.226954936981D+01-0.000000000000D+00 0.000000000000D+00 +13 22 1 13 6 45 0.0-0.169873237610D-04-0.000000000000D+00 0.234000000000D+05 + 0.472927343750D+04 0.263674259186D+01 0.931322574616D-09 0.000000000000D+00 + -0.100453535156D+05 0.170689201355D+01 0.186264514923D-08-0.200000000000D+01 + -0.229847949219D+05-0.202965736389D+00 0.186264514923D-08 0.000000000000D+00 +14 22 1 13 6 45 0.0 0.731525942683D-04 0.000000000000D+00 0.234000000000D+05 + -0.126651176758D+05 0.160702037811D+01-0.931322574616D-09 0.000000000000D+00 + -0.166958266602D+05 0.117944240570D+01-0.000000000000D+00-0.700000000000D+01 + -0.145302958984D+05-0.275538539887D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 6 45 0.0 0.989306718111D-04 0.000000000000D+00 0.243000000000D+05 + -0.217453115234D+05-0.322718620300D+00-0.186264514923D-08 0.000000000000D+00 + -0.131748393555D+05-0.316743850708D-01-0.186264514923D-08 0.000000000000D+00 + 0.210880175781D+04-0.354954910278D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 6 45 0.0 0.492156483233D-03 0.272848410532D-11 0.234000000000D+05 + -0.478898437500D+03 0.279500961304D+01-0.279396772385D-08 0.000000000000D+00 + -0.113269970703D+05-0.140978431702D+01-0.186264514923D-08 0.400000000000D+01 + 0.228610571289D+05-0.643630981445D+00-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 6 45 0.0 0.103675760329D-03 0.909494701773D-12 0.243000000000D+05 + -0.160520122070D+05 0.248881340027D+01-0.186264514923D-08 0.000000000000D+00 + -0.210697900391D+04-0.105496120453D+01-0.279396772385D-08-0.300000000000D+01 + 0.197134042969D+05 0.192059040070D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 6 45 0.0-0.174256972969D-03-0.909494701773D-12 0.237000000000D+05 + -0.234548901367D+05 0.992274284363D+00-0.931322574616D-09 0.000000000000D+00 + 0.705411474609D+04-0.177566528320D+00-0.279396772385D-08 0.300000000000D+01 + 0.713644189453D+04 0.343957710266D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 6 45 0.0-0.629657879472D-04-0.909494701773D-12 0.234000000000D+05 + -0.170728598633D+05-0.152831363678D+01 0.931322574616D-09 0.000000000000D+00 + 0.138748227539D+05 0.914305686951D+00-0.000000000000D+00 0.200000000000D+01 + -0.128579208984D+05 0.301620388031D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 6 45 0.0-0.269684009254D-03-0.272848410532D-11 0.234000000000D+05 + 0.177373242188D+04-0.283953285217D+01 0.186264514923D-08 0.000000000000D+00 + 0.106854750977D+05 0.140406799316D+01 0.186264514923D-08 0.400000000000D+01 + -0.230886845703D+05 0.429286956787D+00 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 6 45 0.0-0.157760456204D-03-0.181898940355D-11 0.234000000000D+05 + 0.168326406250D+05-0.243297958374D+01 0.186264514923D-08 0.000000000000D+00 + 0.149410937500D+04 0.987956047058D+00 0.279396772385D-08-0.300000000000D+01 + -0.191533544922D+05-0.205281066894D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 6 45 0.0-0.266078859568D-04-0.909494701773D-12 0.234000000000D+05 + 0.232136743164D+05-0.223255157471D+00 0.000000000000D+00 0.000000000000D+00 + -0.105357851562D+05-0.169199943543D+00 0.186264514923D-08 0.300000000000D+01 + -0.943129394531D+03-0.360644149780D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 6 45 0.0 0.754427164793D-04 0.181898940355D-11 0.234000000000D+05 + 0.161348569336D+05 0.158776760101D+01-0.931322574616D-09 0.000000000000D+00 + -0.145594541016D+05-0.916697502136D+00 0.000000000000D+00 0.200000000000D+01 + 0.133684956055D+05-0.292071533203D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 7 15 0.0 0.717956572771D-05-0.000000000000D+00 0.252000000000D+05 + 0.110268442383D+05 0.282760620117D+00-0.931322574616D-09 0.000000000000D+00 + -0.725523925781D+03 0.313450527191D+01-0.000000000000D+00 0.100000000000D+01 + 0.229887490234D+05-0.358753204346D-01-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 7 15 0.0 0.506337732077D-03 0.909494701773D-12 0.252000000000D+05 + 0.567947216797D+04-0.225458145142D-01-0.186264514923D-08 0.000000000000D+00 + -0.201883588867D+05 0.199527454376D+01-0.000000000000D+00-0.400000000000D+01 + 0.146077963867D+05 0.276742458343D+01-0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 7 15 0.0 0.498536974192D-04 0.909494701773D-12 0.252000000000D+05 + -0.266955908203D+04-0.188340187073D+00-0.186264514923D-08 0.000000000000D+00 + -0.253135717773D+05-0.274689674377D+00-0.000000000000D+00 0.500000000000D+01 + -0.205471435547D+04 0.355311393738D+01 0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 7 15 0.0 0.123295933008D-03 0.181898940355D-11 0.252000000000D+05 + -0.917365136719D+04-0.272068977356D+00-0.000000000000D+00 0.000000000000D+00 + -0.162282421875D+05-0.237498950958D+01 0.000000000000D+00 0.600000000000D+01 + -0.173937998047D+05 0.235466289520D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 7 15 0.0 0.858763232827D-04 0.909494701773D-12 0.252000000000D+05 + -0.107002221680D+05-0.184094429016D+00 0.931322574616D-09 0.000000000000D+00 + 0.174758886719D+04-0.315840530395D+01 0.000000000000D+00 0.100000000000D+01 + -0.230803657227D+05-0.156270027161D+00 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 7 15 0.0-0.267885625362D-04-0.181898940355D-11 0.252000000000D+05 + -0.471108447266D+04-0.555696487427D-01 0.279396772385D-08 0.000000000000D+00 + 0.209647373047D+05-0.188208389282D+01 0.000000000000D+00-0.400000000000D+01 + -0.137372617188D+05-0.285809707642D+01 0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 7 15 0.0 0.306284055114D-04 0.000000000000D+00 0.252000000000D+05 + 0.302979345703D+04 0.187191009522D+00 0.186264514923D-08 0.000000000000D+00 + 0.251384438477D+05 0.380784988403D+00 0.000000000000D+00 0.500000000000D+01 + 0.289662500000D+04-0.354738903046D+01-0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 7 15 0.0-0.630356371403D-04 0.000000000000D+00 0.252000000000D+05 + 0.937196923828D+04 0.265209197998D+00 0.931322574616D-09 0.000000000000D+00 + 0.160472172852D+05 0.237521743774D+01-0.000000000000D+00 0.600000000000D+01 + 0.175114394531D+05-0.232803249359D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 7 15 0.0 0.338396057486D-04 0.181898940355D-11 0.252000000000D+05 + -0.866171337891D+04-0.279711246491D+01-0.931322574616D-09 0.000000000000D+00 + 0.823348388672D+04-0.129293823242D+01-0.186264514923D-08-0.200000000000D+01 + 0.225416684570D+05-0.609285354614D+00-0.931322574616D-09 0.000000000000D+00 +10 22 1 13 7 15 0.0-0.788718461990D-04-0.000000000000D+00 0.252000000000D+05 + 0.790365771484D+04-0.240692234039D+01 0.000000000000D+00 0.000000000000D+00 + 0.141427031250D+05-0.122050189972D+01-0.000000000000D+00-0.700000000000D+01 + 0.196615141602D+05 0.185139465332D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 7 15 0.0 0.473055988550D-04-0.909494701773D-12 0.252000000000D+05 + 0.215062753906D+05-0.553323745727D+00 0.186264514923D-08 0.000000000000D+00 + 0.127663178711D+05-0.444843292236D+00 0.931322574616D-09 0.000000000000D+00 + 0.504788769531D+04 0.348634243012D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 7 15 0.0 0.294514000416D-03 0.272848410532D-11 0.252000000000D+05 + 0.216143916016D+05 0.166515064239D+01 0.186264514923D-08 0.000000000000D+00 + 0.355032519531D+04 0.665651321411D+00 0.186264514923D-08-0.100000000000D+01 + -0.130300170898D+05 0.294105148315D+01-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 7 15 0.0-0.169873237610D-04-0.000000000000D+00 0.252000000000D+05 + 0.965588427734D+04 0.279081821442D+01 0.186264514923D-08 0.000000000000D+00 + -0.733931103516D+04 0.127790355682D+01 0.186264514923D-08-0.200000000000D+01 + -0.224588647461D+05 0.783577919006D+00 0.931322574616D-09 0.000000000000D+00 +14 22 1 13 7 15 0.0 0.731525942683D-04 0.000000000000D+00 0.252000000000D+05 + -0.913637939453D+04 0.229443550110D+01-0.000000000000D+00 0.000000000000D+00 + -0.145244672852D+05 0.119077968597D+01 0.000000000000D+00-0.700000000000D+01 + -0.188636376953D+05-0.202815628052D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 7 15 0.0 0.989306718111D-04 0.000000000000D+00 0.261000000000D+05 + -0.216351464844D+05 0.463155746460D+00-0.186264514923D-08 0.000000000000D+00 + -0.128179877930D+05 0.391716957092D+00-0.931322574616D-09 0.000000000000D+00 + -0.427935302734D+04-0.350231170654D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 7 15 0.0 0.492163002491D-03 0.272848410532D-11 0.252000000000D+05 + 0.416065332031D+04 0.232300853729D+01-0.186264514923D-08 0.000000000000D+00 + -0.141226245117D+05-0.165998363495D+01-0.931322574616D-09 0.400000000000D+01 + 0.208346562500D+05-0.159344577789D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 7 15 0.0 0.103677622974D-03 0.909494701773D-12 0.267000000000D+05 + -0.114272407227D+05 0.259889698029D+01-0.279396772385D-08 0.000000000000D+00 + -0.452307763672D+04-0.162088584900D+01-0.186264514923D-08-0.300000000000D+01 + 0.223642343750D+05 0.100584697723D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 7 15 0.0-0.174258835614D-03-0.909494701773D-12 0.252000000000D+05 + -0.210643095703D+05 0.162521266937D+01-0.186264514923D-08 0.000000000000D+00 + 0.623774316406D+04-0.752695083618D+00-0.279396772385D-08 0.300000000000D+01 + 0.129719106445D+05 0.300220584869D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 7 15 0.0-0.629676505923D-04-0.909494701773D-12 0.252000000000D+05 + -0.190751381836D+05-0.694060325623D+00 0.000000000000D+00 0.000000000000D+00 + 0.153782226562D+05 0.711033821106D+00-0.931322574616D-09 0.200000000000D+01 + -0.700046826172D+04 0.344968032837D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 7 15 0.0-0.269689597189D-03-0.272848410532D-11 0.252000000000D+05 + -0.298270800781D+04-0.240601825714D+01 0.279396772385D-08 0.000000000000D+00 + 0.135034047852D+05 0.169213008881D+01 0.931322574616D-09 0.400000000000D+01 + -0.214331811523D+05 0.139814758301D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 7 15 0.0-0.157764181495D-03-0.181898940355D-11 0.252000000000D+05 + 0.122707177734D+05-0.258500003815D+01 0.279396772385D-08 0.000000000000D+00 + 0.379966064453D+04 0.156751251221D+01 0.186264514923D-08-0.300000000000D+01 + -0.220631171875D+05-0.115913581848D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 7 15 0.0-0.266106799245D-04-0.909494701773D-12 0.252000000000D+05 + 0.221132236328D+05-0.970652580261D+00 0.931322574616D-09 0.000000000000D+00 + -0.104053046875D+05 0.346247673035D+00 0.186264514923D-08 0.300000000000D+01 + -0.731440234375D+04-0.342678546905D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 7 15 0.0 0.754464417696D-04 0.181898940355D-11 0.252000000000D+05 + 0.182735595703D+05 0.785416603088D+00-0.000000000000D+00 0.000000000000D+00 + -0.160651376953D+05-0.712520599365D+00 0.931322574616D-09 0.200000000000D+01 + 0.766256103516D+04-0.337811946869D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 7 45 0.0 0.718049705028D-05-0.000000000000D+00 0.270000000000D+05 + 0.119253837891D+05 0.701786994934D+00-0.000000000000D+00 0.000000000000D+00 + 0.478073437500D+04 0.293422508240D+01-0.000000000000D+00 0.100000000000D+01 + 0.220363925781D+05-0.101540660858D+01-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 7 45 0.0 0.506340526044D-03 0.909494701773D-12 0.270000000000D+05 + 0.598748242188D+04 0.387778282165D+00-0.186264514923D-08 0.000000000000D+00 + -0.160530717773D+05 0.255840682983D+01-0.000000000000D+00-0.400000000000D+01 + 0.189637524414D+05 0.204128551483D+01-0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 7 45 0.0 0.498555600643D-04 0.909494701773D-12 0.270000000000D+05 + -0.291942968750D+04-0.491409301758D-01-0.186264514923D-08 0.000000000000D+00 + -0.250056196289D+05 0.608817100525D+00 0.000000000000D+00 0.500000000000D+01 + 0.433772314453D+04 0.350365734100D+01 0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 7 45 0.0 0.123298726976D-03 0.181898940355D-11 0.273000000000D+05 + -0.988721582031D+04-0.485370635986D+00-0.931322574616D-09 0.000000000000D+00 + -0.198855263672D+05-0.165879249573D+01 0.000000000000D+00 0.600000000000D+01 + -0.125370190430D+05 0.300661659241D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 7 45 0.0 0.858772546053D-04 0.909494701773D-12 0.270000000000D+05 + -0.114427041016D+05-0.630407333374D+00 0.931322574616D-09 0.000000000000D+00 + -0.385350146484D+04-0.301393985748D+01 0.000000000000D+00 0.100000000000D+01 + -0.224652529297D+05 0.835208892822D+00 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 7 45 0.0-0.267932191491D-04-0.181898940355D-11 0.270000000000D+05 + -0.516047363281D+04-0.466565132141D+00 0.186264514923D-08 0.000000000000D+00 + 0.170216079102D+05-0.245886707306D+01 0.000000000000D+00-0.400000000000D+01 + -0.182840937500D+05-0.216120338440D+01 0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 7 45 0.0 0.306284055114D-04-0.000000000000D+00 0.270000000000D+05 + 0.329064257812D+04 0.620727539062D-01 0.186264514923D-08 0.000000000000D+00 + 0.250172924805D+05-0.509407043457D+00 0.000000000000D+00 0.500000000000D+01 + -0.351824023438D+04-0.353379726410D+01-0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 7 45 0.0-0.630356371403D-04-0.000000000000D+00 0.270000000000D+05 + 0.100696367188D+05 0.475000381470D+00 0.931322574616D-09 0.000000000000D+00 + 0.197148237305D+05 0.166993331909D+01-0.000000000000D+00 0.600000000000D+01 + 0.126997700195D+05-0.298380565643D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 7 45 0.0 0.338433310389D-04 0.181898940355D-11 0.279000000000D+05 + -0.136516157227D+05-0.269778347015D+01-0.279396772385D-08 0.000000000000D+00 + 0.633716308594D+04-0.807856559753D+00-0.186264514923D-08-0.200000000000D+01 + 0.205880390625D+05-0.154752826691D+01 0.000000000000D+00 0.000000000000D+00 +10 22 1 13 7 45 0.0-0.788699835539D-04-0.000000000000D+00 0.270000000000D+05 + 0.310522607422D+04-0.288626289368D+01-0.931322574616D-09 0.000000000000D+00 + 0.121479399414D+05-0.963493347168D+00-0.931322574616D-09-0.700000000000D+01 + 0.221883129883D+05 0.938054084778D+00-0.186264514923D-08 0.000000000000D+00 +11 22 1 13 7 45 0.0 0.473046675324D-04-0.909494701773D-12 0.270000000000D+05 + 0.197507958984D+05-0.139423465729D+01 0.931322574616D-09 0.000000000000D+00 + 0.117856127930D+05-0.602394104004D+00 0.000000000000D+00 0.000000000000D+00 + 0.110472519531D+05 0.313637351990D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 7 45 0.0 0.294519588351D-03 0.272848410532D-11 0.270000000000D+05 + 0.240431245117D+05 0.998330116272D+00 0.279396772385D-08 0.000000000000D+00 + 0.428581250000D+04 0.176684379578D+00 0.186264514923D-08-0.100000000000D+01 + -0.729970703125D+04 0.338449382782D+01-0.186264514923D-08 0.000000000000D+00 +13 22 1 13 7 45 0.0-0.169882550836D-04-0.000000000000D+00 0.287100000000D+05 + 0.145997612305D+05 0.265231895447D+01 0.186264514923D-08 0.000000000000D+00 + -0.549264501953D+04 0.769689559937D+00 0.186264514923D-08-0.200000000000D+01 + -0.202001191406D+05 0.170993518829D+01 0.000000000000D+00 0.000000000000D+00 +14 22 1 13 7 45 0.0 0.731535255909D-04 0.000000000000D+00 0.270000000000D+05 + -0.450574804688D+04 0.281497383118D+01 0.000000000000D+00 0.000000000000D+00 + -0.125488974609D+05 0.970322608948D+00 0.931322574616D-09-0.700000000000D+01 + -0.217373012695D+05-0.114407348633D+01 0.186264514923D-08 0.000000000000D+00 +15 22 1 13 7 45 0.0 0.989297404885D-04 0.000000000000D+00 0.270000000000D+05 + -0.200461152344D+05 0.130169486999D+01-0.931322574616D-09 0.000000000000D+00 + -0.119083281250D+05 0.577129364014D+00 0.000000000000D+00 0.000000000000D+00 + -0.103365610352D+05-0.318418407440D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 7 45 0.0 0.492168590426D-03 0.272848410532D-11 0.270000000000D+05 + 0.777897265625D+04 0.167690658569D+01-0.186264514923D-08 0.000000000000D+00 + -0.171529062500D+05-0.166150951386D+01 0.000000000000D+00 0.400000000000D+01 + 0.171982812500D+05-0.242085742950D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 7 45 0.0 0.103680416942D-03 0.909494701773D-12 0.270000000000D+05 + -0.687273632812D+04 0.241593647003D+01-0.279396772385D-08 0.000000000000D+00 + -0.787737939453D+04-0.207932472229D+01-0.931322574616D-09-0.300000000000D+01 + 0.232883754883D+05 0.144929885864D-01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 7 45 0.0-0.174261629581D-03-0.909494701773D-12 0.280800000000D+05 + -0.177622080078D+05 0.199615383148D+01-0.279396772385D-08 0.000000000000D+00 + 0.429190576172D+04-0.141468906403D+01-0.186264514923D-08 0.300000000000D+01 + 0.178048837891D+05 0.233293914795D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 7 45 0.0-0.629695132375D-04-0.909494701773D-12 0.270300000000D+05 + -0.195990258789D+05 0.946578979492D-01 0.000000000000D+00 0.000000000000D+00 + 0.162791376953D+05 0.250309944153D+00-0.186264514923D-08 0.200000000000D+01 + -0.600045410156D+03 0.361557197571D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 7 45 0.0-0.269694253802D-03-0.272848410532D-11 0.270000000000D+05 + -0.677343554688D+04-0.178258609772D+01 0.186264514923D-08 0.000000000000D+00 + 0.166312197266D+05 0.173832225800D+01 0.000000000000D+00 0.400000000000D+01 + -0.181206015625D+05 0.225854682922D+01 0.931322574616D-09 0.000000000000D+00 +22 22 1 13 7 45 0.0-0.157768838108D-03-0.181898940355D-11 0.270000000000D+05 + 0.770518701172D+04-0.244101238251D+01 0.279396772385D-08 0.000000000000D+00 + 0.708058935547D+04 0.205318641663D+01 0.931322574616D-09-0.300000000000D+01 + -0.232716376953D+05-0.174699783325D+00 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 7 45 0.0-0.266125425696D-04-0.909494701773D-12 0.270000000000D+05 + 0.198487880859D+05-0.150353050232D+01 0.186264514923D-08 0.000000000000D+00 + -0.919989746094D+04 0.100970458984D+01 0.186264514923D-08 0.300000000000D+01 + -0.131201962891D+05-0.298221778870D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 7 45 0.0 0.754492357373D-04 0.181898940355D-11 0.270000000000D+05 + 0.189858144531D+05 0.218248367310D-01 0.000000000000D+00 0.000000000000D+00 + -0.169733442383D+05-0.257884025574D+00 0.931322574616D-09 0.200000000000D+01 + 0.136423388672D+04-0.357458782196D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 8 15 0.0 0.717956572771D-05-0.000000000000D+00 0.294300000000D+05 + 0.134741391602D+05 0.989666938782D+00 0.000000000000D+00 0.000000000000D+00 + 0.967418603516D+04 0.246281528473D+01-0.000000000000D+00 0.100000000000D+01 + 0.193805673828D+05-0.191634082794D+01-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 8 15 0.0 0.506344251335D-03 0.909494701773D-12 0.288000000000D+05 + 0.712931933594D+04 0.887230873108D+00-0.186264514923D-08 0.000000000000D+00 + -0.111432998047D+05 0.284762001038D+01-0.000000000000D+00-0.400000000000D+01 + 0.218619272461D+05 0.115805244446D+01-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 8 15 0.0 0.498583540320D-04 0.909494701773D-12 0.288000000000D+05 + -0.271218945312D+04 0.312451362610D+00-0.186264514923D-08 0.000000000000D+00 + -0.231845180664D+05 0.138843727112D+01 0.000000000000D+00 0.500000000000D+01 + 0.103962939453D+05 0.318462944031D+01 0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 8 15 0.0 0.123301520944D-03 0.181898940355D-11 0.288000000000D+05 + -0.107792182617D+05-0.463837623596D+00-0.186264514923D-08 0.000000000000D+00 + -0.221242856445D+05-0.817176818848D+00 0.000000000000D+00 0.600000000000D+01 + -0.671013427734D+04 0.342559432983D+01 0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 8 15 0.0 0.858781859279D-04 0.909494701773D-12 0.288000000000D+05 + -0.129010039062D+05-0.962898254395D+00 0.000000000000D+00 0.000000000000D+00 + -0.893042431641D+04-0.258397674560D+01 0.000000000000D+00 0.100000000000D+01 + -0.201127470703D+05 0.176164054871D+01 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 8 15 0.0-0.267969444394D-04-0.181898940355D-11 0.288000000000D+05 + -0.644422070312D+04-0.966113090515D+00 0.186264514923D-08 0.000000000000D+00 + 0.122755146484D+05-0.276591014862D+01 0.000000000000D+00-0.400000000000D+01 + -0.214174008789D+05-0.129774856567D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 8 15 0.0 0.306284055114D-04-0.000000000000D+00 0.288000000000D+05 + 0.311639013672D+04-0.289705276489D+00 0.279396772385D-08 0.000000000000D+00 + 0.233607436523D+05-0.130651950836D+01-0.000000000000D+00 0.500000000000D+01 + -0.965998095703D+04-0.324582672119D+01-0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 8 15 0.0-0.630356371403D-04-0.000000000000D+00 0.288000000000D+05 + 0.109416489258D+05 0.452341079712D+00 0.186264514923D-08 0.000000000000D+00 + 0.219823295898D+05 0.837521553040D+00-0.000000000000D+00 0.600000000000D+01 + 0.690777343750D+04-0.341010570526D+01-0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 8 15 0.0 0.338470563292D-04 0.181898940355D-11 0.297000000000D+05 + -0.181985854492D+05-0.230806446075D+01-0.279396772385D-08 0.000000000000D+00 + 0.531552832031D+04-0.338702201843D+00-0.186264514923D-08-0.200000000000D+01 + 0.170418750000D+05-0.236721801758D+01 0.000000000000D+00 0.000000000000D+00 +10 22 1 13 8 15 0.0-0.788718461990D-04-0.000000000000D+00 0.288000000000D+05 + -0.232801464844D+04-0.310214900971D+01-0.186264514923D-08 0.000000000000D+00 + 0.107652470703D+05-0.555456161499D+00-0.931322574616D-09-0.700000000000D+01 + 0.229954975586D+05-0.467958450317D-01-0.186264514923D-08 0.000000000000D+00 +11 22 1 13 8 15 0.0 0.473037362099D-04-0.909494701773D-12 0.288000000000D+05 + 0.165327080078D+05-0.215933132172D+01 0.931322574616D-09 0.000000000000D+00 + 0.107477631836D+05-0.511349678040D+00-0.000000000000D+00 0.000000000000D+00 + 0.161931186523D+05 0.254417705536D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 8 15 0.0 0.294524244964D-03 0.272848410532D-11 0.289200000000D+05 + 0.251090004883D+05 0.166808128357D+00 0.279396772385D-08 0.000000000000D+00 + 0.429940673828D+04-0.125144004822D+00 0.931322574616D-09-0.100000000000D+01 + -0.100368750000D+04 0.356554889679D+01-0.186264514923D-08 0.000000000000D+00 +13 22 1 13 8 15 0.0-0.169882550836D-04-0.000000000000D+00 0.290700000000D+05 + 0.190286298828D+05 0.222268104553D+01 0.279396772385D-08 0.000000000000D+00 + -0.455113085938D+04 0.290031433106D+00 0.186264514923D-08-0.200000000000D+01 + -0.163822543945D+05 0.250470447540D+01-0.000000000000D+00 0.000000000000D+00 +14 22 1 13 8 15 0.0 0.731535255909D-04 0.000000000000D+00 0.288000000000D+05 + 0.846422851562D+03 0.308492851257D+01 0.186264514923D-08 0.000000000000D+00 + -0.111298447266D+05 0.586558341980D+00 0.931322574616D-09-0.700000000000D+01 + -0.229291347656D+05-0.171606063843D+00 0.931322574616D-09 0.000000000000D+00 +15 22 1 13 8 15 0.0 0.989288091659D-04-0.000000000000D+00 0.288000000000D+05 + -0.169866513672D+05 0.207779407501D+01-0.931322574616D-09 0.000000000000D+00 + -0.108900234375D+05 0.514553070068D+00 0.000000000000D+00 0.000000000000D+00 + -0.155941533203D+05-0.261959743500D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 8 15 0.0 0.492176041007D-03 0.272848410532D-11 0.288000000000D+05 + 0.101581875000D+05 0.965474128723D+00-0.931322574616D-09 0.000000000000D+00 + -0.199347421875D+05-0.138242816925D+01 0.000000000000D+00 0.400000000000D+01 + 0.122318217773D+05-0.306166172028D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 8 15 0.0 0.103683210909D-03 0.909494701773D-12 0.288000000000D+05 + -0.287473144531D+04 0.199276542664D+01-0.279396772385D-08 0.000000000000D+00 + -0.118847905273D+05-0.233239555359D+01-0.000000000000D+00-0.300000000000D+01 + 0.224162915039D+05-0.977066993713D+00-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 8 15 0.0-0.174264423549D-03-0.909494701773D-12 0.297000000000D+05 + -0.140545444336D+05 0.207473468780D+01-0.372529029846D-08 0.000000000000D+00 + 0.116068750000D+04-0.205030441284D+01-0.186264514923D-08 0.300000000000D+01 + 0.212621445312D+05 0.148355960846D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 8 15 0.0-0.629713758826D-04 0.000000000000D+00 0.288300000000D+05 + -0.188276713867D+05 0.728851318359D+00-0.186264514923D-08 0.000000000000D+00 + 0.161569013672D+05-0.413211822510D+00-0.186264514923D-08 0.200000000000D+01 + 0.584694189453D+04 0.350111865997D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 8 15 0.0-0.269699841738D-03-0.272848410532D-11 0.288000000000D+05 + -0.935000976562D+04-0.107611274719D+01 0.931322574616D-09 0.000000000000D+00 + 0.195920429688D+05 0.150388813019D+01-0.000000000000D+00 0.400000000000D+01 + -0.134075839844D+05 0.294411659241D+01 0.931322574616D-09 0.000000000000D+00 +22 22 1 13 8 15 0.0-0.157772563398D-03-0.181898940355D-11 0.288000000000D+05 + 0.363360888672D+04-0.204743194580D+01 0.279396772385D-08 0.000000000000D+00 + 0.110730825195D+05 0.234309577942D+01 0.000000000000D+00-0.300000000000D+01 + -0.226829208984D+05 0.824872016907D+00 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 8 15 0.0-0.266153365374D-04-0.909494701773D-12 0.288000000000D+05 + 0.168647119141D+05-0.176429939270D+01 0.279396772385D-08 0.000000000000D+00 + -0.674399658203D+04 0.171671390533D+01 0.186264514923D-08 0.300000000000D+01 + -0.179117197266D+05-0.230714035034D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 8 15 0.0 0.754520297051D-04 0.181898940355D-11 0.288000000000D+05 + 0.184393066406D+05-0.597392082214D+00 0.931322574616D-09 0.000000000000D+00 + -0.168735410156D+05 0.395474433899D+00 0.186264514923D-08 0.200000000000D+01 + -0.503959570312D+04-0.349451541901D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 8 45 0.0 0.717956572771D-05-0.000000000000D+00 0.306000000000D+05 + 0.153611723633D+05 0.106708335876D+01 0.931322574616D-09 0.000000000000D+00 + 0.135278334961D+05 0.179396915436D+01-0.000000000000D+00 0.100000000000D+01 + 0.152267075195D+05-0.266909313202D+01-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 8 45 0.0 0.506347045302D-03 0.909494701773D-12 0.306000000000D+05 + 0.917133447266D+04 0.136971092224D+01-0.931322574616D-09 0.000000000000D+00 + -0.598304003906D+04 0.283606052399D+01-0.000000000000D+00-0.400000000000D+01 + 0.230787934570D+05 0.185141563416D+00-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 8 45 0.0 0.498602166772D-04 0.909494701773D-12 0.306000000000D+05 + -0.169772412109D+04 0.834461212158D+00-0.186264514923D-08 0.000000000000D+00 + -0.201284711914D+05 0.196712684631D+01 0.000000000000D+00 0.500000000000D+01 + 0.156551992188D+05 0.262090682983D+01-0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 8 45 0.0 0.123304314911D-03 0.181898940355D-11 0.306000000000D+05 + -0.114053027344D+05-0.190972328186D+00-0.186264514923D-08 0.000000000000D+00 + -0.228213803711D+05 0.344505310059D-01 0.000000000000D+00 0.600000000000D+01 + -0.364340820312D+03 0.357949733734D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 8 45 0.0 0.858791172504D-04 0.909494701773D-12 0.306000000000D+05 + -0.147882138672D+05-0.109501457214D+01-0.000000000000D+00 0.000000000000D+00 + -0.130245200195D+05-0.193625450134D+01 0.000000000000D+00 0.100000000000D+01 + -0.162054975586D+05 0.255151271820D+01 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 8 45 0.0-0.268006697297D-04-0.181898940355D-11 0.306000000000D+05 + -0.862855712891D+04-0.144911384582D+01 0.931322574616D-09 0.000000000000D+00 + 0.724379931641D+04-0.277539920807D+01-0.000000000000D+00-0.400000000000D+01 + -0.228960166016D+05-0.334594726562D+00 0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 8 45 0.0 0.306274741888D-04-0.000000000000D+00 0.306000000000D+05 + 0.214668798828D+04-0.808731079102D+00 0.279396772385D-08 0.000000000000D+00 + 0.204309458008D+05-0.190950870514D+01-0.000000000000D+00 0.500000000000D+01 + -0.150514511719D+05-0.270558643341D+01 0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 8 45 0.0-0.630356371403D-04-0.000000000000D+00 0.306000000000D+05 + 0.115472802734D+05 0.179915428162D+00 0.186264514923D-08 0.000000000000D+00 + 0.227218666992D+05-0.828742980957D-02-0.000000000000D+00 0.600000000000D+01 + 0.581726074219D+03-0.357319355011D+01-0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 8 45 0.0 0.338498502970D-04 0.181898940355D-11 0.315000000000D+05 + -0.218099663086D+05-0.166852951050D+01-0.372529029846D-08 0.000000000000D+00 + 0.504801660156D+04 0.148611068726D-01-0.931322574616D-09-0.200000000000D+01 + 0.121758027344D+05-0.300446128845D+01 0.931322574616D-09 0.000000000000D+00 +10 22 1 13 8 45 0.0-0.788718461990D-04-0.000000000000D+00 0.306000000000D+05 + -0.787996582031D+04-0.301534938812D+01-0.279396772385D-08 0.000000000000D+00 + 0.101792836914D+05-0.960245132446D-01-0.931322574616D-09-0.700000000000D+01 + 0.220228320312D+05-0.102669715881D+01-0.931322574616D-09 0.000000000000D+00 +11 22 1 13 8 45 0.0 0.473028048873D-04-0.909494701773D-12 0.306000000000D+05 + 0.120872207031D+05-0.274196529388D+01-0.000000000000D+00 0.000000000000D+00 + 0.100708833008D+05-0.211359977722D+00-0.000000000000D+00 0.000000000000D+00 + 0.200881215820D+05 0.175554180145D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 8 45 0.0 0.294529832900D-03 0.272848410532D-11 0.306000000000D+05 + 0.246085126953D+05-0.722902297974D+00 0.186264514923D-08 0.000000000000D+00 + 0.397715429688D+04-0.192028045654D+00 0.000000000000D+00-0.100000000000D+01 + 0.537011572266D+04 0.347040939331D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 8 45 0.0-0.169891864061D-04-0.000000000000D+00 0.306000000000D+05 + 0.224538051758D+05 0.154832267761D+01 0.279396772385D-08 0.000000000000D+00 + -0.437103662109D+04-0.613203048706D-01 0.931322574616D-09-0.200000000000D+01 + -0.112992968750D+05 0.310644912720D+01-0.931322574616D-09 0.000000000000D+00 +14 22 1 13 8 45 0.0 0.731544569135D-04 0.000000000000D+00 0.321900000000D+05 + 0.642064794922D+04 0.305749416351D+01 0.186264514923D-08 0.000000000000D+00 + -0.104781069336D+05 0.135121345520D+00 0.931322574616D-09-0.700000000000D+01 + -0.223471894531D+05 0.813984870911D+00 0.931322574616D-09 0.000000000000D+00 +15 22 1 13 8 45 0.0 0.989278778434D-04-0.000000000000D+00 0.306000000000D+05 + -0.126689067383D+05 0.268331146240D+01 0.000000000000D+00 0.000000000000D+00 + -0.101848105469D+05 0.238489151001D+00 0.000000000000D+00 0.000000000000D+00 + -0.196450610352D+05-0.185212516785D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 8 45 0.0 0.492181628943D-03 0.272848410532D-11 0.306000000000D+05 + 0.112848398438D+05 0.303877830505D+00 0.000000000000D+00 0.000000000000D+00 + -0.219692099609D+05-0.837482452393D+00 0.931322574616D-09 0.400000000000D+01 + 0.631861279297D+04-0.346589183807D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 8 45 0.0 0.103686004877D-03 0.909494701773D-12 0.306000000000D+05 + 0.210023437500D+03 0.141831302643D+01-0.186264514923D-08 0.000000000000D+00 + -0.161098330078D+05-0.231329441071D+01 0.000000000000D+00-0.300000000000D+01 + 0.198166162109D+05-0.189268970489D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 8 45 0.0-0.174266286194D-03-0.909494701773D-12 0.322800000000D+05 + -0.104591186523D+05 0.187822437286D+01-0.372529029846D-08 0.000000000000D+00 + -0.300496386719D+04-0.254618358612D+01-0.931322574616D-09 0.300000000000D+01 + 0.230768188477D+05 0.519677162170D+00-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 8 45 0.0-0.629723072052D-04 0.000000000000D+00 0.332400000000D+05 + -0.171168144531D+05 0.112836837769D+01-0.279396772385D-08 0.000000000000D+00 + 0.147245795898D+05-0.118763446808D+01-0.186264514923D-08 0.200000000000D+01 + 0.118408608398D+05 0.311550617218D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 8 45 0.0-0.269704498351D-03-0.272848410532D-11 0.306000000000D+05 + -0.106673886719D+05-0.402539253235D+00 0.000000000000D+00 0.000000000000D+00 + 0.218804877930D+05 0.996460914612D+00-0.931322574616D-09 0.400000000000D+01 + -0.765871923828D+04 0.340205001831D+01 0.931322574616D-09 0.000000000000D+00 +22 22 1 13 8 45 0.0-0.157778151333D-03-0.272848410532D-11 0.306000000000D+05 + 0.433922851562D+03-0.148884868622D+01 0.279396772385D-08 0.000000000000D+00 + 0.153536796875D+05 0.236433601379D+01-0.000000000000D+00-0.300000000000D+01 + -0.203394736328D+05 0.176229572296D+01 0.931322574616D-09 0.000000000000D+00 +23 22 1 13 8 45 0.0-0.266181305051D-04-0.909494701773D-12 0.306000000000D+05 + 0.136686928711D+05-0.174102783203D+01 0.279396772385D-08 0.000000000000D+00 + -0.306349951172D+04 0.235102176666D+01 0.931322574616D-09 0.300000000000D+01 + -0.213186396484D+05-0.145375728607D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 8 45 0.0 0.754548236728D-04 0.181898940355D-11 0.306000000000D+05 + 0.169697607422D+05-0.993698120117D+00 0.186264514923D-08 0.000000000000D+00 + -0.154828925781D+05 0.115931129456D+01 0.931322574616D-09 0.200000000000D+01 + -0.110531835938D+05-0.314375686645D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 9 15 0.0 0.717956572771D-05-0.000000000000D+00 0.324000000000D+05 + 0.171624375000D+05 0.890652656555D+00 0.931322574616D-09 0.000000000000D+00 + 0.160748784180D+05 0.102951717377D+01-0.000000000000D+00 0.100000000000D+01 + 0.989595068359D+04-0.321551990509D+01-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 9 15 0.0 0.506349839270D-03 0.909494701773D-12 0.324000000000D+05 + 0.119859702148D+05 0.172894859314D+01-0.000000000000D+00 0.000000000000D+00 + -0.110342626953D+04 0.254270744324D+01-0.000000000000D+00-0.400000000000D+01 + 0.225192270508D+05-0.803070068359D+00-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 9 15 0.0 0.498630106449D-04 0.909494701773D-12 0.324000000000D+05 + 0.332692382812D+03 0.142397880554D+01-0.186264514923D-08 0.000000000000D+00 + -0.162629501953D+05 0.228044605255D+01 0.000000000000D+00 0.500000000000D+01 + 0.197104384766D+05 0.185582923889D+01-0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 9 15 0.0 0.123308040202D-03 0.181898940355D-11 0.324000000000D+05 + -0.113324897461D+05 0.304214477539D+00-0.186264514923D-08 0.000000000000D+00 + -0.220631728516D+05 0.781896591187D+00 0.931322574616D-09 0.600000000000D+01 + 0.600966943359D+04 0.345680904388D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 9 15 0.0 0.858809798956D-04 0.909494701773D-12 0.346800000000D+05 + -0.166905263672D+05-0.974394798279D+00-0.931322574616D-09 0.000000000000D+00 + -0.158294897461D+05-0.116996383667D+01 0.931322574616D-09 0.100000000000D+01 + -0.110460683594D+05 0.314395618439D+01 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 9 15 0.0-0.268034636974D-04-0.181898940355D-11 0.324000000000D+05 + -0.115875258789D+05-0.181020832062D+01 0.931322574616D-09 0.000000000000D+00 + 0.245267675781D+04-0.250532913208D+01-0.000000000000D+00-0.400000000000D+01 + -0.226067006836D+05 0.653899192810D+00 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 9 15 0.0 0.306274741888D-04-0.000000000000D+00 0.324000000000D+05 + 0.159523925781D+03-0.140289783478D+01 0.186264514923D-08 0.000000000000D+00 + 0.166453813477D+05-0.224917316437D+01-0.000000000000D+00 0.500000000000D+01 + -0.192736103516D+05-0.195510292053D+01 0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 9 15 0.0-0.630365684629D-04-0.000000000000D+00 0.324000000000D+05 + 0.114554204102D+05-0.314312934875D+00 0.186264514923D-08 0.000000000000D+00 + 0.220129907227D+05-0.753901481628D+00-0.931322574616D-09 0.600000000000D+01 + -0.578925488281D+04-0.345956230164D+01-0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 9 15 0.0 0.338526442647D-04 0.181898940355D-11 0.333000000000D+05 + -0.241024853516D+05-0.858546257019D+00-0.372529029846D-08 0.000000000000D+00 + 0.525278955078D+04 0.175922393799D+00 0.000000000000D+00-0.200000000000D+01 + 0.636563623047D+04-0.340927600861D+01 0.186264514923D-08 0.000000000000D+00 +10 22 1 13 9 15 0.0-0.788727775216D-04 0.000000000000D+00 0.333000000000D+05 + -0.130046250000D+05-0.263232326508D+01-0.372529029846D-08 0.000000000000D+00 + 0.103859658203D+05 0.307797431946D+00-0.931322574616D-09-0.700000000000D+01 + 0.193478940430D+05-0.192597675324D+01 0.000000000000D+00 0.000000000000D+00 +11 22 1 13 9 15 0.0 0.473018735647D-04-0.909494701773D-12 0.344400000000D+05 + 0.682003027344D+04-0.306194114685D+01-0.931322574616D-09 0.100000000000D+01 + 0.100665429688D+05 0.220605850220D+00-0.931322574616D-09 0.000000000000D+00 + 0.224315253906D+05 0.831349372864D+00-0.186264514923D-08 0.000000000000D+00 +12 22 1 13 9 15 0.0 0.294535420835D-03 0.272848410532D-11 0.324000000000D+05 + 0.225418549805D+05-0.155406665802D+01 0.931322574616D-09 0.000000000000D+00 + 0.375239111328D+04-0.199413299561D-01 0.000000000000D+00-0.100000000000D+01 + 0.113282919922D+05 0.310681343079D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 9 15 0.0-0.169901177287D-04-0.000000000000D+00 0.324000000000D+05 + 0.245058662109D+05 0.713593482971D+00 0.279396772385D-08 0.000000000000D+00 + -0.464927539062D+04-0.209611892700D+00 0.931322574616D-09-0.200000000000D+01 + -0.534325341797D+04 0.346845436096D+01-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 9 15 0.0 0.731544569135D-04 0.000000000000D+00 0.324000000000D+05 + 0.116727792969D+05 0.273056030273D+01 0.279396772385D-08 0.000000000000D+00 + -0.106201401367D+05-0.277798652649D+00 0.931322574616D-09-0.700000000000D+01 + -0.200367529297D+05 0.173646831512D+01 0.000000000000D+00 0.000000000000D+00 +15 22 1 13 9 15 0.0 0.989269465208D-04-0.909494701773D-12 0.324000000000D+05 + -0.748003955078D+04 0.303460884094D+01 0.931322574616D-09 0.000000000000D+00 + -0.101164047852D+05-0.178273200989D+00 0.000000000000D+00 0.000000000000D+00 + -0.221754624023D+05-0.941125869751D+00 0.186264514923D-08 0.000000000000D+00 +17 22 1 13 9 15 0.0 0.492187216878D-03 0.272848410532D-11 0.324000000000D+05 + 0.113441450195D+05-0.205399513245D+00 0.931322574616D-09 0.000000000000D+00 + -0.228253012695D+05-0.864610671997D-01 0.931322574616D-09 0.400000000000D+01 + -0.839936523438D+02-0.360183238983D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 9 15 0.0 0.103690661490D-03 0.909494701773D-12 0.324000000000D+05 + 0.220631005859D+04 0.802470207214D+00-0.931322574616D-09 0.000000000000D+00 + -0.200338222656D+05-0.199774646759D+01 0.931322574616D-09-0.300000000000D+01 + 0.156904980469D+05-0.266220474243D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 9 15 0.0-0.174269080162D-03-0.909494701773D-12 0.324000000000D+05 + -0.742186132812D+04 0.146797275543D+01-0.279396772385D-08 0.000000000000D+00 + -0.786429785156D+04-0.280778598785D+01 0.000000000000D+00 0.300000000000D+01 + 0.231088466797D+05-0.484325408936D+00-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 9 15 0.0-0.629741698503D-04 0.000000000000D+00 0.324000000000D+05 + -0.149304301758D+05 0.125468540192D+01-0.372529029846D-08 0.000000000000D+00 + 0.118832875977D+05-0.195881748199D+01-0.931322574616D-09 0.200000000000D+01 + 0.169178847656D+05 0.248905181885D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 9 15 0.0-0.269710086286D-03-0.272848410532D-11 0.324000000000D+05 + -0.108829482422D+05 0.132308959961D+00-0.931322574616D-09 0.000000000000D+00 + 0.230467753906D+05 0.269433975220D+00-0.931322574616D-09 0.400000000000D+01 + -0.131831982422D+04 0.359714126587D+01 0.931322574616D-09 0.000000000000D+00 +22 22 1 13 9 15 0.0-0.157782807946D-03-0.272848410532D-11 0.324000000000D+05 + -0.169222802734D+04-0.873760223389D+00 0.186264514923D-08 0.000000000000D+00 + 0.194038500977D+05 0.208580207825D+01-0.931322574616D-09-0.300000000000D+01 + -0.164199423828D+05 0.256458187103D+01 0.931322574616D-09 0.000000000000D+00 +23 22 1 13 9 15 0.0-0.266199931502D-04-0.909494701773D-12 0.324000000000D+05 + 0.107472612305D+05-0.146871662140D+01 0.372529029846D-08 0.000000000000D+00 + 0.161025976562D+04 0.280382919311D+01 0.000000000000D+00 0.300000000000D+01 + -0.230776586914D+05-0.488018989563D+00 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 9 15 0.0 0.754566863179D-04 0.181898940355D-11 0.324000000000D+05 + 0.150199711914D+05-0.112826156616D+01 0.279396772385D-08 0.000000000000D+00 + -0.126999785156D+05 0.192307186127D+01 0.931322574616D-09 0.200000000000D+01 + -0.162105004883D+05-0.254927158356D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 9 45 0.0 0.717956572771D-05-0.000000000000D+00 0.342000000000D+05 + 0.184145488281D+05 0.460775375366D+00 0.186264514923D-08 0.000000000000D+00 + 0.172448515625D+05 0.282752037048D+00-0.931322574616D-09 0.100000000000D+01 + 0.380034472656D+04-0.351341247559D+01-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 9 45 0.0 0.506352633238D-03 0.909494701773D-12 0.342000000000D+05 + 0.152678803711D+05 0.187653064728D+01 0.000000000000D+00 0.000000000000D+00 + 0.303783740234D+04 0.202900409699D+01-0.000000000000D+00-0.400000000000D+01 + 0.202241528320D+05-0.173073577881D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 9 45 0.0 0.498658046126D-04 0.909494701773D-12 0.342000000000D+05 + 0.340399365234D+04 0.197256946564D+01-0.931322574616D-09 0.000000000000D+00 + -0.120922187500D+05 0.230638504028D+01 0.000000000000D+00 0.500000000000D+01 + 0.222502602539D+05 0.947909355164D+00-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 9 45 0.0 0.123311765492D-03 0.181898940355D-11 0.342000000000D+05 + -0.102181074219D+05 0.951767921448D+00-0.186264514923D-08 0.000000000000D+00 + -0.201265483398D+05 0.133052730560D+01 0.931322574616D-09 0.600000000000D+01 + 0.119197084961D+05 0.306737422943D+01 0.000000000000D+00 0.000000000000D+00 + 5 22 1 13 9 45 0.0 0.858819112182D-04 0.909494701773D-12 0.351000000000D+05 + -0.181381362305D+05-0.592159271240D+00-0.931322574616D-09 0.000000000000D+00 + -0.172339370117D+05-0.399467468262D+00 0.931322574616D-09 0.100000000000D+01 + -0.503343603516D+04 0.349339962006D+01 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 9 45 0.0-0.268071889877D-04-0.181898940355D-11 0.342000000000D+05 + -0.150189003906D+05-0.196173954010D+01 0.000000000000D+00 0.000000000000D+00 + -0.164318359375D+04-0.201600933075D+01 0.000000000000D+00-0.400000000000D+01 + -0.205725473633D+05 0.159158134460D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 9 45 0.0 0.306265428662D-04-0.000000000000D+00 0.342000000000D+05 + -0.288324951172D+04-0.196295452118D+01 0.186264514923D-08 0.000000000000D+00 + 0.125087094727D+05-0.229911231995D+01-0.000000000000D+00 0.500000000000D+01 + -0.219985908203D+05-0.105296993256D+01 0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 9 45 0.0-0.630384311080D-04-0.000000000000D+00 0.342000000000D+05 + 0.103235698242D+05-0.961153984070D+00 0.186264514923D-08 0.000000000000D+00 + 0.201256811523D+05-0.130423736572D+01-0.931322574616D-09 0.600000000000D+01 + -0.117110698242D+05-0.307726478577D+01-0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 9 45 0.0 0.338554382324D-04 0.181898940355D-11 0.342000000000D+05 + -0.248616572266D+05 0.162458419800D-01-0.279396772385D-08 0.000000000000D+00 + 0.554046240234D+04 0.103669166565D+00-0.000000000000D+00-0.200000000000D+01 + 0.614638671875D+02-0.354969692230D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 9 45 0.0-0.788727775216D-04 0.000000000000D+00 0.351000000000D+05 + -0.172084916992D+05-0.200413417816D+01-0.372529029846D-08 0.000000000000D+00 + 0.111958613281D+05 0.560067176819D+00 0.000000000000D+00-0.700000000000D+01 + 0.151793769531D+05-0.267555332184D+01 0.931322574616D-09 0.000000000000D+00 +11 22 1 13 9 45 0.0 0.473000109196D-04-0.909494701773D-12 0.345600000000D+05 + 0.124727294922D+04-0.307840824127D+01-0.186264514923D-08 0.100000000000D+01 + 0.108838198242D+05 0.683605194092D+00 0.000000000000D+00 0.000000000000D+00 + 0.230423193359D+05-0.157111167908D+00-0.186264514923D-08 0.000000000000D+00 +12 22 1 13 9 45 0.0 0.294541008771D-03 0.272848410532D-11 0.342000000000D+05 + 0.191144335938D+05-0.221850967407D+01 0.931322574616D-09 0.000000000000D+00 + 0.402539892578D+04 0.350934982300D+00-0.000000000000D+00-0.100000000000D+01 + 0.164103291016D+05 0.250331497192D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 9 45 0.0-0.169910490513D-04-0.000000000000D+00 0.342000000000D+05 + 0.249917587891D+05-0.172529220581D+00 0.279396772385D-08 0.000000000000D+00 + -0.498072412109D+04-0.117886543274D+00 0.000000000000D+00-0.200000000000D+01 + 0.102589111328D+04 0.356241130829D+01-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 9 45 0.0 0.731553882360D-04 0.000000000000D+00 0.342000000000D+05 + 0.160960190430D+05 0.214718532562D+01 0.279396772385D-08 0.000000000000D+00 + -0.113955078125D+05-0.553768157959D+00 0.000000000000D+00-0.700000000000D+01 + -0.161767666016D+05 0.252453041077D+01-0.000000000000D+00 0.000000000000D+00 +15 22 1 13 9 45 0.0 0.989269465208D-04-0.909494701773D-12 0.342300000000D+05 + -0.192513037109D+04 0.308609199524D+01 0.186264514923D-08 0.000000000000D+00 + -0.108521391602D+05-0.637318611145D+00 0.000000000000D+00 0.000000000000D+00 + -0.229892011719D+05 0.428705215454D-01 0.931322574616D-09 0.000000000000D+00 +17 22 1 13 9 45 0.0 0.492192804813D-03 0.272848410532D-11 0.342000000000D+05 + 0.106813452148D+05-0.489521980286D+00 0.186264514923D-08 0.000000000000D+00 + -0.222137875977D+05 0.774996757507D+00 0.931322574616D-09 0.400000000000D+01 + -0.648005615234D+04-0.345862674713D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 9 45 0.0 0.103693455458D-03 0.909494701773D-12 0.342000000000D+05 + 0.314187402344D+04 0.257673263550D+00-0.000000000000D+00 0.000000000000D+00 + -0.231366254883D+05-0.140853595734D+01 0.931322574616D-09-0.300000000000D+01 + 0.103560361328D+05-0.322664928436D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 9 45 0.0-0.174271874130D-03-0.909494701773D-12 0.342000000000D+05 + -0.524623046875D+04 0.938611030579D+00-0.186264514923D-08 0.000000000000D+00 + -0.129356181641D+05-0.277529430389D+01 0.931322574616D-09 0.300000000000D+01 + 0.213557055664D+05-0.145099163055D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 9 45 0.0-0.629751011729D-04 0.000000000000D+00 0.351600000000D+05 + -0.127594985352D+05 0.111661434174D+01-0.372529029846D-08 0.000000000000D+00 + 0.774568164062D+04-0.260912799835D+01 0.000000000000D+00 0.200000000000D+01 + 0.206860029297D+05 0.167068290710D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 9 45 0.0-0.269714742899D-03-0.272848410532D-11 0.342000000000D+05 + -0.103221293945D+05 0.450104713440D+00-0.186264514923D-08 0.000000000000D+00 + 0.227727812500D+05-0.586252212524D+00-0.931322574616D-09 0.400000000000D+01 + 0.512389453125D+04 0.351446437836D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 9 45 0.0-0.157788395882D-03-0.272848410532D-11 0.342000000000D+05 + -0.274645263672D+04-0.316267967224D+00 0.000000000000D+00 0.000000000000D+00 + 0.226918027344D+05 0.152422523499D+01-0.931322574616D-09-0.300000000000D+01 + -0.112261083984D+05 0.316882991791D+01 0.931322574616D-09 0.000000000000D+00 +23 22 1 13 9 45 0.0-0.266227871180D-04-0.909494701773D-12 0.342000000000D+05 + 0.848602783203D+04-0.102280712128D+01 0.279396772385D-08 0.000000000000D+00 + 0.687063476562D+04 0.299189853668D+01-0.000000000000D+00 0.300000000000D+01 + -0.230527978516D+05 0.515472412109D+00 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 9 45 0.0 0.754594802856D-04 0.909494701773D-12 0.358500000000D+05 + 0.130618701172D+05-0.100790309906D+01 0.372529029846D-08 0.000000000000D+00 + -0.862907177734D+04 0.257193565369D+01 0.000000000000D+00 0.200000000000D+01 + -0.201116196289D+05-0.175707054138D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 10 15 0.0 0.717956572771D-05-0.000000000000D+00 0.360000000000D+05 + 0.186952446289D+05-0.177799224853D+00 0.186264514923D-08 0.000000000000D+00 + 0.171676489258D+05-0.340065956116D+00-0.931322574616D-09 0.100000000000D+01 + -0.258898242188D+04-0.353976154327D+01-0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 10 15 0.0 0.506354495883D-03 0.909494701773D-12 0.360000000000D+05 + 0.185804340820D+05 0.175716972351D+01 0.000000000000D+00 0.000000000000D+00 + 0.612464453125D+04 0.138863086700D+01-0.000000000000D+00-0.400000000000D+01 + 0.163680913086D+05-0.252620697022D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 10 15 0.0 0.498676672578D-04 0.909494701773D-12 0.360000000000D+05 + 0.734539746094D+04 0.237427043915D+01-0.931322574616D-09 0.000000000000D+00 + -0.811921191406D+04 0.206830501556D+01 0.000000000000D+00 0.500000000000D+01 + 0.230786967773D+05-0.335111618042D-01-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 10 15 0.0 0.123315490782D-03 0.181898940355D-11 0.360000000000D+05 + -0.787413232422D+04 0.165219497681D+01-0.186264514923D-08 0.000000000000D+00 + -0.174295112305D+05 0.162025356293D+01 0.931322574616D-09 0.600000000000D+01 + 0.169100053711D+05 0.244152927399D+01-0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 10 15 0.0 0.858837738633D-04 0.909494701773D-12 0.374100000000D+05 + -0.186865043945D+05 0.150403976440D-01-0.186264514923D-08 0.000000000000D+00 + -0.173320058594D+05 0.264369010925D+00 0.931322574616D-09 0.100000000000D+01 + 0.136787011719D+04 0.357304382324D+01 0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 10 15 0.0-0.268109142780D-04-0.181898940355D-11 0.360000000000D+05 + -0.184902460938D+05-0.184881305695D+01-0.000000000000D+00 0.000000000000D+00 + -0.472843066406D+04-0.139970588684D+01 0.000000000000D+00-0.400000000000D+01 + -0.169509799805D+05 0.240629577637D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 10 15 0.0 0.306265428662D-04-0.000000000000D+00 0.360000000000D+05 + -0.682153125000D+04-0.238105010986D+01 0.931322574616D-09 0.000000000000D+00 + 0.853119287109D+04-0.207947158814D+01-0.000000000000D+00 0.500000000000D+01 + -0.230154565430D+05-0.696372985840D-01 0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 10 15 0.0-0.630393624306D-04-0.000000000000D+00 0.360000000000D+05 + 0.796300195312D+04-0.166130065918D+01 0.186264514923D-08 0.000000000000D+00 + 0.174725083008D+05-0.159810447693D+01-0.931322574616D-09 0.600000000000D+01 + -0.167233476562D+05-0.245550727844D+01 0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 10 15 0.0 0.338582322001D-04 0.181898940355D-11 0.369600000000D+05 + -0.240742597656D+05 0.840922355652D+00-0.186264514923D-08 0.000000000000D+00 + 0.548664697266D+04-0.199364662170D+00 0.000000000000D+00-0.200000000000D+01 + -0.624744042969D+04-0.341446304321D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 10 15 0.0-0.788727775216D-04 0.000000000000D+00 0.360000000000D+05 + -0.201243579102D+05-0.121811199188D+01-0.372529029846D-08 0.000000000000D+00 + 0.122694018555D+05 0.592329978943D+00 0.000000000000D+00-0.700000000000D+01 + 0.984042382812D+04-0.321813678742D+01 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 10 15 0.0 0.472990795970D-04-0.909494701773D-12 0.369000000000D+05 + -0.408220214844D+04-0.279633998871D+01-0.279396772385D-08 0.100000000000D+01 + 0.124813583984D+05 0.107006835938D+01 0.000000000000D+00 0.000000000000D+00 + 0.218731250000D+05-0.113360023499D+01-0.931322574616D-09 0.000000000000D+00 +12 22 1 13 10 15 0.0 0.294546596706D-03 0.363797880709D-11 0.360000000000D+05 + 0.147054350586D+05-0.263391590118D+01-0.000000000000D+00 0.000000000000D+00 + 0.508911083984D+04 0.843254089355D+00-0.000000000000D+00-0.100000000000D+01 + 0.202241992188D+05 0.170693302155D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 10 15 0.0-0.169919803739D-04-0.000000000000D+00 0.360000000000D+05 + 0.239238745117D+05-0.993852615356D+00 0.186264514923D-08 0.000000000000D+00 + -0.493316992188D+04 0.206524848938D+00-0.000000000000D+00-0.200000000000D+01 + 0.731560742188D+04 0.338072395325D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 10 15 0.0 0.731563195586D-04 0.000000000000D+00 0.360000000000D+05 + 0.192970625000D+05 0.138868999481D+01 0.372529029846D-08 0.000000000000D+00 + -0.124865537109D+05-0.619094848633D+00 0.000000000000D+00-0.700000000000D+01 + -0.110659365234D+05 0.311727905273D+01-0.931322574616D-09 0.000000000000D+00 +15 22 1 13 10 15 0.0 0.989260151982D-04-0.909494701773D-12 0.372300000000D+05 + 0.344894335938D+04 0.283753299713D+01 0.279396772385D-08 0.000000000000D+00 + -0.123716938477D+05-0.103160476685D+01 0.000000000000D+00 0.000000000000D+00 + -0.220230742188D+05 0.102363300324D+01 0.931322574616D-09 0.000000000000D+00 +17 22 1 13 10 15 0.0 0.492198392749D-03 0.272848410532D-11 0.360000000000D+05 + 0.973664648438D+04-0.517261505127D+00 0.279396772385D-08 0.000000000000D+00 + -0.200387441406D+05 0.163067817688D+01 0.000000000000D+00 0.400000000000D+01 + -0.123737138672D+05-0.304723358154D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 10 15 0.0 0.103697180748D-03 0.909494701773D-12 0.360000000000D+05 + 0.323497705078D+04-0.119810104370D+00 0.000000000000D+00 0.000000000000D+00 + -0.249796972656D+05-0.612380027771D+00 0.931322574616D-09-0.300000000000D+01 + 0.422399462891D+04-0.354267406464D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 10 15 0.0-0.174270011485D-03-0.909494701773D-12 0.360000000000D+05 + -0.404728710938D+04 0.401806831360D+00-0.931322574616D-09 0.000000000000D+00 + -0.176690209961D+05-0.243387222290D+01 0.931322574616D-09 0.300000000000D+01 + 0.179526176758D+05-0.230570983887D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 10 15 0.0-0.629760324955D-04 0.000000000000D+00 0.369000000000D+05 + -0.110376318359D+05 0.768052101135D+00-0.372529029846D-08 0.000000000000D+00 + 0.262468945312D+04-0.303676891327D+01 0.000000000000D+00 0.200000000000D+01 + 0.228551577148D+05 0.723993301392D+00 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 10 15 0.0-0.269719399512D-03-0.272848410532D-11 0.360000000000D+05 + -0.941660595703D+04 0.512745857239D+00-0.279396772385D-08 0.000000000000D+00 + 0.209273979492D+05-0.145655441284D+01-0.931322574616D-09 0.400000000000D+01 + 0.111705532227D+05 0.316049385071D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 10 15 0.0-0.157793983817D-03-0.272848410532D-11 0.360000000000D+05 + -0.292619824219D+04 0.832633972168D-01-0.000000000000D+00 0.000000000000D+00 + 0.247580937500D+05 0.742271423340D+00-0.931322574616D-09-0.300000000000D+01 + -0.515997656250D+04 0.352735614777D+01 0.931322574616D-09 0.000000000000D+00 +23 22 1 13 10 15 0.0-0.266246497631D-04-0.909494701773D-12 0.360000000000D+05 + 0.710788769531D+04-0.506004333496D+00 0.279396772385D-08 0.000000000000D+00 + 0.121950644531D+05 0.287152004242D+01-0.931322574616D-09 0.300000000000D+01 + -0.212458774414D+05 0.147919940949D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 10 15 0.0 0.754622742534D-04 0.909494701773D-12 0.360000000000D+05 + 0.115147353516D+05-0.683371543884D+00 0.372529029846D-08 0.000000000000D+00 + -0.357070263672D+04 0.300570678711D+01-0.000000000000D+00 0.200000000000D+01 + -0.224539941406D+05-0.828627586365D+00 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 10 45 0.0 0.717956572771D-05-0.000000000000D+00 0.378000000000D+05 + 0.176981044922D+05-0.942824363708D+00 0.186264514923D-08 0.000000000000D+00 + 0.161449389648D+05-0.756949424744D+00-0.186264514923D-08 0.100000000000D+01 + -0.877823535156D+04-0.329255390167D+01-0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 10 45 0.0 0.506353564560D-03 0.909494701773D-12 0.378000000000D+05 + 0.214252822266D+05 0.135869598389D+01 0.931322574616D-09 0.000000000000D+00 + 0.802718408203D+04 0.731997489929D+00-0.931322574616D-09-0.400000000000D+01 + 0.112465834961D+05-0.312754631043D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 10 45 0.0 0.498695299029D-04 0.909494701773D-12 0.378000000000D+05 + 0.118110517578D+05 0.254329299927D+01-0.000000000000D+00 0.000000000000D+00 + -0.476673437500D+04 0.163055610657D+01-0.000000000000D+00 0.500000000000D+01 + 0.221305595703D+05-0.101336097717D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 10 45 0.0 0.123318284750D-03 0.181898940355D-11 0.378000000000D+05 + -0.430620800781D+04 0.229280090332D+01-0.931322574616D-09 0.000000000000D+00 + -0.144597202148D+05 0.163472557068D+01 0.931322574616D-09 0.600000000000D+01 + 0.205961118164D+05 0.162766265869D+01-0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 10 45 0.0 0.858856365085D-04 0.909494701773D-12 0.378000000000D+05 + -0.179944370117D+05 0.770551681519D+00-0.186264514923D-08 0.000000000000D+00 + -0.164005683594D+05 0.732155799866D+00 0.186264514923D-08 0.100000000000D+01 + 0.766360937500D+04 0.337688159943D+01 0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 10 45 0.0-0.268137082458D-04-0.181898940355D-11 0.378000000000D+05 + -0.215075634766D+05-0.145898532867D+01-0.000000000000D+00 0.000000000000D+00 + -0.667120751953D+04-0.765151023865D+00 0.931322574616D-09-0.400000000000D+01 + -0.120215693359D+05 0.303534507752D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 10 45 0.0 0.306265428662D-04-0.000000000000D+00 0.378000000000D+05 + -0.113162949219D+05-0.256910610199D+01 0.931322574616D-09 0.000000000000D+00 + 0.514742871094D+04-0.165286827087D+01-0.000000000000D+00 0.500000000000D+01 + -0.222465473633D+05 0.918253898621D+00 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 10 45 0.0-0.630412250757D-04-0.000000000000D+00 0.378000000000D+05 + 0.437884130859D+04-0.230169963837D+01 0.186264514923D-08 0.000000000000D+00 + 0.145378657227D+05-0.161793518066D+01-0.931322574616D-09 0.600000000000D+01 + -0.204358295898D+05-0.164248466492D+01 0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 10 45 0.0 0.338610261679D-04 0.181898940355D-11 0.378000000000D+05 + -0.219283061523D+05 0.150988006592D+01-0.931322574616D-09 0.000000000000D+00 + 0.471099951172D+04-0.687435150147D+00 0.000000000000D+00-0.200000000000D+01 + -0.120710576172D+05-0.301402473450D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 10 45 0.0-0.788727775216D-04 0.000000000000D+00 0.378000000000D+05 + -0.215645693359D+05-0.383699417114D+00-0.372529029846D-08 0.000000000000D+00 + 0.131778129883D+05 0.375379562378D+00 0.000000000000D+00-0.700000000000D+01 + 0.374342138672D+04-0.351247024536D+01 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 10 45 0.0 0.472981482744D-04-0.909494701773D-12 0.383400000000D+05 + -0.866957910156D+04-0.226556110382D+01-0.372529029846D-08 0.100000000000D+01 + 0.146321259766D+05 0.128426647186D+01 0.000000000000D+00 0.000000000000D+00 + 0.190138984375D+05-0.202274513245D+01 0.000000000000D+00 0.000000000000D+00 +12 22 1 13 10 45 0.0 0.294555909932D-03 0.363797880709D-11 0.378000000000D+05 + 0.980878466797D+04-0.275678634644D+01-0.931322574616D-09 0.000000000000D+00 + 0.707336230469D+04 0.135576343536D+01-0.000000000000D+00-0.100000000000D+01 + 0.224763867188D+05 0.779380798340D+00-0.186264514923D-08 0.000000000000D+00 +13 22 1 13 10 45 0.0-0.169929116964D-04-0.000000000000D+00 0.378000000000D+05 + 0.215162011719D+05-0.164561843872D+01 0.931322574616D-09 0.000000000000D+00 + -0.412730029297D+04 0.712967872620D+00-0.000000000000D+00-0.200000000000D+01 + 0.130389814453D+05 0.293719959259D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 10 45 0.0 0.731572508812D-04 0.000000000000D+00 0.378000000000D+05 + 0.210539243164D+05 0.561494827271D+00 0.279396772385D-08 0.000000000000D+00 + -0.134752856445D+05-0.437650680542D+00-0.000000000000D+00-0.700000000000D+01 + -0.509958642578D+04 0.346894931793D+01-0.186264514923D-08 0.000000000000D+00 +15 22 1 13 10 45 0.0 0.989250838757D-04-0.909494701773D-12 0.378000000000D+05 + 0.813619531250D+04 0.233393383026D+01 0.279396772385D-08 0.000000000000D+00 + -0.144681992188D+05-0.126393890381D+01-0.000000000000D+00 0.000000000000D+00 + -0.193517934570D+05 0.192515373230D+01-0.000000000000D+00 0.000000000000D+00 +17 22 1 13 10 45 0.0 0.492203980684D-03 0.272848410532D-11 0.378000000000D+05 + 0.896474218750D+04-0.303916931152D+00 0.279396772385D-08 0.000000000000D+00 + -0.164179458008D+05 0.236261940002D+01-0.000000000000D+00 0.400000000000D+01 + -0.173079809570D+05-0.239960384369D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 10 45 0.0 0.103699974716D-03 0.909494701773D-12 0.378000000000D+05 + 0.285029687500D+04-0.265849113464D+00 0.186264514923D-08 0.000000000000D+00 + -0.252768745117D+05 0.290366172790D+00 0.000000000000D+00-0.300000000000D+01 + -0.223344775391D+04-0.358582973480D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 10 45 0.0-0.174272805452D-03-0.909494701773D-12 0.378000000000D+05 + -0.373794335938D+04-0.327291488648D-01-0.000000000000D+00 0.000000000000D+00 + -0.215311669922D+05-0.181659317017D+01 0.931322574616D-09 0.300000000000D+01 + 0.131622231445D+05-0.298243236542D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 10 45 0.0-0.629769638181D-04-0.000000000000D+00 0.395100000000D+05 + -0.100676738281D+05 0.298221588135D+00-0.279396772385D-08 0.000000000000D+00 + -0.301061621094D+04-0.317244815826D+01 0.931322574616D-09 0.200000000000D+01 + 0.232592475586D+05-0.277783393860D+00-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 10 45 0.0-0.269724056125D-03-0.272848410532D-11 0.394200000000D+05 + -0.862539111328D+04 0.328319549561D+00-0.372529029846D-08 0.000000000000D+00 + 0.175920195312D+05-0.222261714935D+01 0.000000000000D+00 0.400000000000D+01 + 0.163549223633D+05 0.256257247925D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 10 45 0.0-0.157799571752D-03-0.272848410532D-11 0.378000000000D+05 + -0.258408789062D+04 0.255275726318D+00-0.186264514923D-08 0.000000000000D+00 + 0.252907016602D+05-0.161116600037D+00-0.931322574616D-09-0.300000000000D+01 + 0.130742431641D+04 0.361169815064D+01 0.931322574616D-09 0.000000000000D+00 +23 22 1 13 10 45 0.0-0.266274437308D-04-0.909494701773D-12 0.378000000000D+05 + 0.663935742188D+04-0.306339263916D-01 0.186264514923D-08 0.000000000000D+00 + 0.170240859375D+05 0.244603538513D+01-0.931322574616D-09 0.300000000000D+01 + -0.177964047852D+05 0.232868099213D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 10 45 0.0 0.754641368985D-04 0.181898940355D-11 0.378000000000D+05 + 0.106733081055D+05-0.240221977234D+00 0.279396772385D-08 0.000000000000D+00 + 0.202012890625D+04 0.315535449982D+01-0.931322574616D-09 0.200000000000D+01 + -0.230560971680D+05 0.163950920105D+00 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 11 15 0.0 0.717956572771D-05-0.000000000000D+00 0.396000000000D+05 + 0.152890190430D+05-0.172770214081D+01 0.186264514923D-08 0.000000000000D+00 + 0.145938330078D+05-0.923334121704D+00-0.186264514923D-08 0.100000000000D+01 + -0.142891206055D+05-0.279091548920D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 11 15 0.0 0.506355427206D-03 0.000000000000D+00 0.396000000000D+05 + 0.233237709961D+05 0.715018272400D+00 0.931322574616D-09 0.000000000000D+00 + 0.881576953125D+04 0.167918205261D+00-0.931322574616D-09-0.400000000000D+01 + 0.525416113281D+04-0.348745155335D+01-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 11 15 0.0 0.498713925481D-04 0.000000000000D+00 0.396000000000D+05 + 0.163301191406D+05 0.242858314514D+01 0.000000000000D+00 0.000000000000D+00 + -0.231269824219D+04 0.108747291565D+01-0.000000000000D+00 0.500000000000D+01 + 0.194768037109D+05-0.191638469696D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 11 15 0.0 0.123322010040D-03 0.181898940355D-11 0.396000000000D+05 + 0.279168945312D+03 0.276622200012D+01-0.931322574616D-09 0.000000000000D+00 + -0.116928813477D+05 0.140325927734D+01 0.931322574616D-09 0.600000000000D+01 + 0.226941982422D+05 0.688461303711D+00-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 11 15 0.0 0.858865678310D-04 0.909494701773D-12 0.396000000000D+05 + -0.158859799805D+05 0.156992244720D+01-0.931322574616D-09 0.000000000000D+00 + -0.148469252930D+05 0.950309753418D+00 0.186264514923D-08 0.100000000000D+01 + 0.133678823242D+05 0.292012405395D+01-0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 11 15 0.0-0.268174335361D-04-0.181898940355D-11 0.409800000000D+05 + -0.235952700195D+05-0.824995994568D+00-0.931322574616D-09 0.000000000000D+00 + -0.753668066406D+04-0.219449043274D+00 0.931322574616D-09-0.400000000000D+01 + -0.616458203125D+04 0.343025875092D+01 0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 11 15 0.0 0.306265428662D-04 0.000000000000D+00 0.396300000000D+05 + -0.158994467773D+05-0.247388458252D+01 0.000000000000D+00 0.000000000000D+00 + 0.264898339844D+04-0.111324882507D+01 0.000000000000D+00 0.500000000000D+01 + -0.197531723633D+05 0.183396148682D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 11 15 0.0-0.630430877209D-04-0.909494701773D-12 0.396000000000D+05 + -0.222039062500D+03-0.277442836762D+01 0.931322574616D-09 0.000000000000D+00 + 0.117961381836D+05-0.139215373993D+01-0.931322574616D-09 0.600000000000D+01 + -0.225593657227D+05-0.701594352722D+00 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 11 15 0.0 0.338638201356D-04 0.181898940355D-11 0.396000000000D+05 + -0.187804599609D+05 0.194388103485D+01-0.000000000000D+00 0.000000000000D+00 + 0.294925244141D+04-0.127913856506D+01 0.000000000000D+00-0.200000000000D+01 + -0.169572446289D+05-0.237972927093D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 11 15 0.0-0.788718461990D-04 0.000000000000D+00 0.396300000000D+05 + -0.215452846680D+05 0.385166168213D+00-0.279396772385D-08 0.000000000000D+00 + 0.134795244141D+05-0.755376815796D-01 0.931322574616D-09-0.700000000000D+01 + -0.264176611328D+04-0.353633022308D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 11 15 0.0 0.472972169518D-04-0.909494701773D-12 0.396000000000D+05 + -0.121403759766D+05-0.157246398926D+01-0.372529029846D-08 0.000000000000D+00 + 0.169601396484D+05 0.125887203217D+01 0.931322574616D-09 0.000000000000D+00 + 0.146851279297D+05-0.275581645966D+01 0.931322574616D-09 0.000000000000D+00 +12 22 1 13 11 15 0.0 0.294561497867D-03 0.363797880709D-11 0.396000000000D+05 + 0.495632714844D+04-0.258896541596D+01-0.279396772385D-08 0.000000000000D+00 + 0.991697802734D+04 0.178053665161D+01 0.000000000000D+00-0.100000000000D+01 + 0.229941347656D+05-0.207749366760D+00-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 11 15 0.0-0.169947743416D-04-0.000000000000D+00 0.396000000000D+05 + 0.181481289062D+05-0.205128860474D+01 0.000000000000D+00 0.000000000000D+00 + -0.230824951172D+04 0.131576347351D+01-0.000000000000D+00-0.200000000000D+01 + 0.177525903320D+05 0.226604747772D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 11 15 0.0 0.731581822038D-04 0.000000000000D+00 0.396000000000D+05 + 0.213460087891D+05-0.220122337341D+00 0.279396772385D-08 0.000000000000D+00 + -0.139179306641D+05-0.174036026001D-01-0.931322574616D-09-0.700000000000D+01 + 0.126095263672D+04 0.355243968964D+01-0.186264514923D-08 0.000000000000D+00 +15 22 1 13 11 15 0.0 0.989241525531D-04-0.000000000000D+00 0.396000000000D+05 + 0.117471982422D+05 0.165797233582D+01 0.279396772385D-08 0.000000000000D+00 + -0.167816152344D+05-0.126384162903D+01-0.000000000000D+00 0.000000000000D+00 + -0.151822226562D+05 0.267756462097D+01-0.000000000000D+00 0.000000000000D+00 +17 22 1 13 11 15 0.0 0.492209568620D-03 0.272848410532D-11 0.396000000000D+05 + 0.875256640625D+04 0.916690826416D-01 0.279396772385D-08 0.000000000000D+00 + -0.116685693359D+05 0.287028217316D+01-0.931322574616D-09 0.400000000000D+01 + -0.209005004883D+05-0.156615066528D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 11 15 0.0 0.103702768683D-03 0.909494701773D-12 0.396000000000D+05 + 0.243054052734D+04-0.159096717834D+00 0.186264514923D-08 0.000000000000D+00 + -0.239413544922D+05 0.118151950836D+01 0.000000000000D+00-0.300000000000D+01 + -0.851865576172D+04-0.335247325897D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 11 15 0.0-0.174274668097D-03-0.909494701773D-12 0.396000000000D+05 + -0.404954833984D+04-0.275876998901D+00 0.000000000000D+00 0.000000000000D+00 + -0.240882470703D+05-0.999518394470D+00 0.931322574616D-09 0.300000000000D+01 + 0.735445019531D+04-0.342877197266D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 11 15 0.0-0.629769638181D-04-0.000000000000D+00 0.396000000000D+05 + -0.997140673828D+04-0.183863639832D+00-0.186264514923D-08 0.000000000000D+00 + -0.860469042969D+04-0.299070358276D+01 0.186264514923D-08 0.200000000000D+01 + 0.218684506836D+05-0.125743579864D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 11 15 0.0-0.269729644060D-03-0.272848410532D-11 0.396000000000D+05 + -0.835209814453D+04-0.506458282471D-01-0.372529029846D-08 0.000000000000D+00 + 0.130518857422D+05-0.277988624573D+01 0.931322574616D-09 0.400000000000D+01 + 0.202767875977D+05 0.176678752899D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 11 15 0.0-0.157805159688D-03-0.272848410532D-11 0.396000000000D+05 + -0.216081152344D+04 0.172885894775D+00-0.279396772385D-08 0.000000000000D+00 + 0.241768027344D+05-0.106658840179D+01-0.000000000000D+00-0.300000000000D+01 + 0.767310058594D+04 0.341506385803D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 11 15 0.0-0.266302376985D-04-0.909494701773D-12 0.396000000000D+05 + 0.691054492188D+04 0.300559043884D+00 0.000000000000D+00 0.000000000000D+00 + 0.208470693359D+05 0.176567459106D+01-0.931322574616D-09 0.300000000000D+01 + -0.129708754883D+05 0.299821376800D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 11 15 0.0 0.754669308662D-04 0.181898940355D-11 0.396000000000D+05 + 0.106579052734D+05 0.216155052185D+00 0.186264514923D-08 0.000000000000D+00 + 0.760128125000D+04 0.299444770813D+01-0.186264514923D-08 0.200000000000D+01 + -0.218715551758D+05 0.114359283447D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 11 45 0.0 0.717956572771D-05-0.000000000000D+00 0.414000000000D+05 + 0.115349208984D+05-0.241872501373D+01 0.931322574616D-09 0.000000000000D+00 + 0.129723037109D+05-0.838925361633D+00-0.186264514923D-08 0.100000000000D+01 + -0.186957978516D+05-0.207361793518D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 11 45 0.0 0.506356358528D-03 0.000000000000D+00 0.414000000000D+05 + 0.238968012695D+05-0.985603332520D-01 0.931322574616D-09 0.000000000000D+00 + 0.874137744141D+04-0.214528083801D+00-0.186264514923D-08-0.400000000000D+01 + -0.114570361328D+04-0.357717227936D+01-0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 11 45 0.0 0.498732551932D-04 0.000000000000D+00 0.414000000000D+05 + 0.203784169922D+05 0.202287483215D+01 0.000000000000D+00 0.000000000000D+00 + -0.849897949219D+03 0.547472953796D+00-0.931322574616D-09 0.500000000000D+01 + 0.153197646484D+05-0.267279052734D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 11 45 0.0 0.123325735331D-03 0.181898940355D-11 0.414000000000D+05 + 0.550053320312D+04 0.298813915253D+01-0.000000000000D+00 0.000000000000D+00 + -0.951447558594D+04 0.995265007019D+00 0.000000000000D+00 0.600000000000D+01 + 0.230426088867D+05-0.303874969482D+00-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 11 45 0.0 0.858884304762D-04 0.909494701773D-12 0.414000000000D+05 + -0.123858056641D+05 0.229774284363D+01-0.931322574616D-09 0.000000000000D+00 + -0.131358266602D+05 0.909377098083D+00 0.186264514923D-08 0.100000000000D+01 + 0.180404838867D+05 0.223799800873D+01-0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 11 45 0.0-0.268211588264D-04-0.181898940355D-11 0.414000000000D+05 + -0.243743935547D+05-0.200691223144D-01-0.931322574616D-09 0.000000000000D+00 + -0.756787255859D+04 0.149654388428D+00 0.186264514923D-08-0.400000000000D+01 + 0.168148925781D+03 0.356050395966D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 11 45 0.0 0.306265428662D-04 0.000000000000D+00 0.414000000000D+05 + -0.200458872070D+05-0.208624172211D+01-0.000000000000D+00 0.000000000000D+00 + 0.114205957031D+04-0.569629669189D+00 0.931322574616D-09 0.500000000000D+01 + -0.157303144531D+05 0.260667228699D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 11 45 0.0-0.630440190435D-04-0.000000000000D+00 0.414000000000D+05 + -0.545694873047D+04-0.299485206604D+01 0.931322574616D-09 0.000000000000D+00 + 0.963265869141D+04-0.989758491516D+00-0.931322574616D-09 0.600000000000D+01 + -0.229288818359D+05 0.293631553650D+00 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 11 45 0.0 0.338666141033D-04 0.181898940355D-11 0.414000000000D+05 + -0.150964067383D+05 0.210262107849D+01 0.931322574616D-09 0.000000000000D+00 + 0.105860351562D+03-0.187108898163D+01 0.000000000000D+00-0.200000000000D+01 + -0.205272861328D+05-0.156125450134D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 11 45 0.0-0.788718461990D-04 0.000000000000D+00 0.416100000000D+05 + -0.202780341797D+05 0.988267898560D+00-0.931322574616D-09 0.000000000000D+00 + 0.127993413086D+05-0.702886581421D+00 0.931322574616D-09-0.700000000000D+01 + -0.882369482422D+04-0.328810024262D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 11 45 0.0 0.472962856293D-04-0.909494701773D-12 0.414000000000D+05 + -0.142982036133D+05-0.825776100159D+00-0.372529029846D-08 0.000000000000D+00 + 0.190031557617D+05 0.966842651367D+00 0.931322574616D-09 0.000000000000D+00 + 0.922100097656D+04-0.327603530884D+01 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 11 45 0.0 0.294568017125D-03 0.363797880709D-11 0.414000000000D+05 + 0.635891113281D+03-0.217674255371D+01-0.279396772385D-08 0.000000000000D+00 + 0.133723125000D+05 0.202129745483D+01 0.931322574616D-09-0.100000000000D+01 + 0.217383554688D+05-0.117848014832D+01-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 11 45 0.0-0.169957056642D-04-0.000000000000D+00 0.414000000000D+05 + 0.143018857422D+05-0.217464065552D+01-0.931322574616D-09 0.000000000000D+00 + 0.603456054688D+03 0.190847301483D+01-0.000000000000D+00-0.200000000000D+01 + 0.210910781250D+05 0.141923332214D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 11 45 0.0 0.731581822038D-04 0.000000000000D+00 0.414000000000D+05 + 0.203515864258D+05-0.852496147156D+00 0.186264514923D-08 0.000000000000D+00 + -0.134246596680D+05 0.590448379517D+00-0.931322574616D-09-0.700000000000D+01 + 0.752405224609D+04 0.336140346527D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 11 45 0.0 0.989232212305D-04-0.000000000000D+00 0.414000000000D+05 + 0.140653291016D+05 0.916281700134D+00 0.279396772385D-08 0.000000000000D+00 + -0.188588510742D+05-0.999979972839D+00-0.931322574616D-09 0.000000000000D+00 + -0.983732226562D+04 0.322258567810D+01-0.931322574616D-09 0.000000000000D+00 +17 22 1 13 11 45 0.0 0.492215156555D-03 0.272848410532D-11 0.426300000000D+05 + 0.934922851562D+04 0.577466011047D+00 0.279396772385D-08 0.000000000000D+00 + -0.626048828125D+04 0.308685970306D+01-0.186264514923D-08 0.400000000000D+01 + -0.228733530273D+05-0.611727714539D+00 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 11 45 0.0 0.103705562651D-03 0.909494701773D-12 0.414000000000D+05 + 0.241510009766D+04 0.175471305847D+00 0.279396772385D-08 0.000000000000D+00 + -0.211010903320D+05 0.194386672974D+01-0.000000000000D+00-0.300000000000D+01 + -0.141465659180D+05-0.286014747620D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 11 45 0.0-0.174277462065D-03-0.909494701773D-12 0.414000000000D+05 + -0.458333300781D+04-0.274274826050D+00 0.931322574616D-09 0.000000000000D+00 + -0.250735053711D+05-0.897016525269D-01 0.000000000000D+00 0.300000000000D+01 + 0.978069824219D+03-0.361009120941D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 11 45 0.0-0.629778951407D-04-0.000000000000D+00 0.414000000000D+05 + -0.106705336914D+05-0.568133354187D+00-0.931322574616D-09 0.000000000000D+00 + -0.135992973633D+05-0.251404857636D+01 0.186264514923D-08 0.200000000000D+01 + 0.187910922852D+05-0.213965320587D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 11 45 0.0-0.269734300673D-03-0.272848410532D-11 0.414000000000D+05 + -0.887217480469D+04-0.536354064941D+00-0.279396772385D-08 0.000000000000D+00 + 0.775465234375D+04-0.305490589142D+01 0.186264514923D-08 0.400000000000D+01 + 0.226332045898D+05 0.834435462952D+00-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 11 45 0.0-0.157810747623D-03-0.272848410532D-11 0.414000000000D+05 + -0.210303808594D+04-0.143583297729D+00-0.279396772385D-08 0.000000000000D+00 + 0.215223208008D+05-0.185379695892D+01 0.000000000000D+00-0.300000000000D+01 + 0.134419365234D+05 0.295294094086D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 11 45 0.0-0.266321003437D-04-0.909494701773D-12 0.414000000000D+05 + 0.758868847656D+04 0.411982536316D+00-0.000000000000D+00 0.000000000000D+00 + 0.232806401367D+05 0.919700622559D+00-0.931322574616D-09 0.300000000000D+01 + -0.714227148438D+04 0.343595790863D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 11 45 0.0 0.754697248340D-04 0.181898940355D-11 0.414000000000D+05 + 0.113948803711D+05 0.578798294067D+00 0.931322574616D-09 0.000000000000D+00 + 0.126252651367D+05 0.254356670380D+01-0.186264514923D-08 0.200000000000D+01 + -0.189926655273D+05 0.203430271149D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 12 15 0.0 0.717956572771D-05-0.000000000000D+00 0.432000000000D+05 + 0.669988867188D+04-0.291357040405D+01 0.931322574616D-09 0.000000000000D+00 + 0.116988530273D+05-0.547150611877D+00-0.186264514923D-08 0.100000000000D+01 + -0.216577407227D+05-0.119606494904D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 12 15 0.0 0.506357289851D-03 0.000000000000D+00 0.432000000000D+05 + 0.229297426758D+05-0.977053642273D+00 0.931322574616D-09 0.000000000000D+00 + 0.818600097656D+04-0.360909461975D+00-0.186264514923D-08-0.400000000000D+01 + -0.745650292969D+04-0.338903903961D+01-0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 12 15 0.0 0.498741865158D-04 0.000000000000D+00 0.432000000000D+05 + 0.234601625977D+05 0.136469364166D+01 0.000000000000D+00 0.000000000000D+00 + -0.276952636719D+03 0.114804267883D+00-0.931322574616D-09 0.500000000000D+01 + 0.997841064453D+04-0.322363281250D+01-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 12 15 0.0 0.123329460621D-03 0.181898940355D-11 0.432000000000D+05 + 0.108562832031D+05 0.291127872467D+01-0.000000000000D+00 0.000000000000D+00 + -0.815765087891D+04 0.508115768433D+00-0.000000000000D+00 0.600000000000D+01 + 0.216141616211D+05-0.127307796478D+01-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 12 15 0.0 0.858902931213D-04 0.909494701773D-12 0.432000000000D+05 + -0.772214599609D+04 0.284635543823D+01-0.931322574616D-09 0.000000000000D+00 + -0.117080146484D+05 0.645011901855D+00 0.931322574616D-09 0.100000000000D+01 + 0.213207001953D+05 0.138303089142D+01-0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 12 15 0.0-0.268248841167D-04-0.181898940355D-11 0.433800000000D+05 + -0.236260874023D+05 0.853473663330D+00-0.931322574616D-09 0.000000000000D+00 + -0.713710644531D+04 0.288237571716D+00 0.186264514923D-08-0.400000000000D+01 + 0.648787402344D+04 0.341586780548D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 12 15 0.0 0.306274741888D-04 0.000000000000D+00 0.432000000000D+05 + -0.232557583008D+05-0.144304180145D+01-0.000000000000D+00 0.000000000000D+00 + 0.536991210938D+03-0.127493858337D+00 0.931322574616D-09 0.500000000000D+01 + -0.104909204102D+05 0.317697238922D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 12 15 0.0-0.630458816886D-04-0.000000000000D+00 0.432000000000D+05 + -0.108229838867D+05-0.291596126556D+01 0.000000000000D+00 0.000000000000D+00 + 0.828083886719D+04-0.508028984070D+00-0.000000000000D+00 0.600000000000D+01 + -0.215163803711D+05 0.126544570923D+01 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 12 15 0.0 0.338703393936D-04 0.181898940355D-11 0.432000000000D+05 + -0.113743066406D+05 0.199069690704D+01 0.186264514923D-08 0.000000000000D+00 + -0.372178076172D+04-0.235536289215D+01-0.000000000000D+00-0.200000000000D+01 + -0.225054438477D+05-0.622560501099D+00 0.186264514923D-08 0.000000000000D+00 +10 22 1 13 12 15 0.0-0.788709148765D-04 0.000000000000D+00 0.432000000000D+05 + -0.181301821289D+05 0.135497760773D+01-0.000000000000D+00 0.000000000000D+00 + 0.108973085938D+05-0.141597843170D+01 0.931322574616D-09-0.700000000000D+01 + -0.143267153320D+05-0.278683090210D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 12 15 0.0 0.472925603390D-04-0.909494701773D-12 0.432300000000D+05 + -0.151492177734D+05-0.138688087463D+00-0.279396772385D-08 0.100000000000D+01 + 0.202909882812D+05 0.426641464233D+00 0.186264514923D-08 0.000000000000D+00 + 0.304374414062D+04-0.354300594330D+01 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 12 15 0.0 0.294574536383D-03 0.363797880709D-11 0.432000000000D+05 + -0.278242236328D+04-0.160274124145D+01-0.372529029846D-08 0.000000000000D+00 + 0.170415004883D+05 0.200983810425D+01 0.186264514923D-08-0.100000000000D+01 + 0.188063588867D+05-0.205819416046D+01 0.000000000000D+00 0.000000000000D+00 +13 22 1 13 12 15 0.0-0.169975683093D-04-0.000000000000D+00 0.432000000000D+05 + 0.104840170898D+05-0.202514648438D+01-0.186264514923D-08 0.000000000000D+00 + 0.449027783203D+04 0.238175392151D+01 0.000000000000D+00-0.200000000000D+01 + 0.227956630859D+05 0.462428092957D+00-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 12 15 0.0 0.731591135263D-04 0.000000000000D+00 0.432000000000D+05 + 0.184132260742D+05-0.125920200348D+01 0.931322574616D-09 0.000000000000D+00 + -0.117311054688D+05 0.129960346222D+01-0.931322574616D-09-0.700000000000D+01 + 0.132058037109D+05 0.291070556641D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 12 15 0.0 0.989222899079D-04-0.000000000000D+00 0.432000000000D+05 + 0.150744028320D+05 0.221825599670D+00 0.279396772385D-08 0.000000000000D+00 + -0.202306157227D+05-0.486049652100D+00-0.931322574616D-09 0.000000000000D+00 + -0.373102734375D+04 0.351807498932D+01-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 12 15 0.0 0.492220744491D-03 0.272848410532D-11 0.432000000000D+05 + 0.108199833984D+05 0.104421615601D+01 0.186264514923D-08 0.000000000000D+00 + -0.744922363281D+03 0.298998165131D+01-0.186264514923D-08 0.400000000000D+01 + -0.230745605469D+05 0.389513015747D+00 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 12 15 0.0 0.103707425296D-03 0.909494701773D-12 0.432000000000D+05 + 0.315927880859D+04 0.670721054077D+00 0.279396772385D-08 0.000000000000D+00 + -0.170799477539D+05 0.247989082336D+01-0.931322574616D-09-0.300000000000D+01 + -0.186819648438D+05-0.214636421204D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 12 15 0.0-0.174280256033D-03-0.909494701773D-12 0.432000000000D+05 + -0.488408105469D+04-0.194950103760D-01 0.186264514923D-08 0.000000000000D+00 + -0.244281962891D+05 0.791933059692D+00-0.000000000000D+00 0.300000000000D+01 + -0.547393505859D+04-0.351223373413D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 12 15 0.0-0.629778951407D-04-0.000000000000D+00 0.432000000000D+05 + -0.119019135742D+05-0.762250900269D+00-0.000000000000D+00 0.000000000000D+00 + -0.175177524414D+05-0.180934524536D+01 0.931322574616D-09 0.200000000000D+01 + 0.142650527344D+05-0.285670566559D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 12 15 0.0-0.269739888609D-03-0.272848410532D-11 0.432000000000D+05 + -0.102822412109D+05-0.102078342438D+01-0.279396772385D-08 0.000000000000D+00 + 0.224292041016D+04-0.301705265045D+01 0.186264514923D-08 0.400000000000D+01 + 0.232418066406D+05-0.162661552429D+00-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 12 15 0.0-0.157816335559D-03-0.272848410532D-11 0.432000000000D+05 + -0.278045751953D+04-0.630187988281D+00-0.372529029846D-08 0.000000000000D+00 + 0.176358535156D+05-0.242099571228D+01 0.186264514923D-08-0.300000000000D+01 + 0.181659179688D+05 0.226180553436D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 12 15 0.0-0.266348943114D-04-0.909494701773D-12 0.432000000000D+05 + 0.823961767578D+04 0.268212318420D+00-0.186264514923D-08 0.000000000000D+00 + 0.241269038086D+05 0.221338272095D-01-0.000000000000D+00 0.300000000000D+01 + -0.761272460938D+03 0.360798740387D+01 0.931322574616D-09 0.000000000000D+00 +24 22 1 13 12 15 0.0 0.754725188017D-04 0.181898940355D-11 0.432000000000D+05 + 0.126306552734D+05 0.757676124573D+00 0.000000000000D+00 0.000000000000D+00 + 0.166220175781D+05 0.186697196960D+01-0.186264514923D-08 0.200000000000D+01 + -0.146430634766D+05 0.276710128784D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 12 45 0.0 0.717863440514D-05-0.000000000000D+00 0.450000000000D+05 + 0.120914794922D+04-0.313798332214D+01 0.000000000000D+00 0.000000000000D+00 + 0.110798520508D+05-0.127309799194D+00-0.931322574616D-09 0.100000000000D+01 + -0.229459882812D+05-0.226013183594D+00 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 12 45 0.0 0.506358221173D-03 0.000000000000D+00 0.450000000000D+05 + 0.204112846680D+05-0.180263423920D+01 0.931322574616D-09 0.000000000000D+00 + 0.759109228516D+04-0.260519027710D+00-0.279396772385D-08-0.400000000000D+01 + -0.131876450195D+05-0.293727970123D+01-0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 12 45 0.0 0.498732551932D-04 0.000000000000D+00 0.450000000000D+05 + 0.251865346680D+05 0.532835006714D+00 0.000000000000D+00 0.000000000000D+00 + -0.322011230469D+03-0.128101348877D+00-0.186264514923D-08 0.500000000000D+01 + 0.386444873047D+04-0.352551746368D+01-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 12 45 0.0 0.123333185911D-03 0.181898940355D-11 0.450000000000D+05 + 0.157996274414D+05 0.253346157074D+01-0.000000000000D+00 0.000000000000D+00 + -0.766741259766D+04 0.504636764526D-01-0.000000000000D+00 0.600000000000D+01 + 0.185183129883D+05-0.214455413818D+01-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 12 45 0.0 0.858912244439D-04 0.909494701773D-12 0.450000000000D+05 + -0.229668017578D+04 0.313335990906D+01-0.000000000000D+00 0.000000000000D+00 + -0.109038251953D+05 0.231451988220D+00 0.931322574616D-09 0.100000000000D+01 + 0.229550102539D+05 0.421036720276D+00-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 12 45 0.0-0.268286094070D-04-0.181898940355D-11 0.459000000000D+05 + -0.213299389648D+05 0.168045425415D+01-0.931322574616D-09 0.000000000000D+00 + -0.667621044922D+04 0.185007095337D+00 0.186264514923D-08-0.400000000000D+01 + 0.123065009766D+05 0.300729942322D+01 0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 12 45 0.0 0.306284055114D-04 0.000000000000D+00 0.450000000000D+05 + -0.251332441406D+05-0.621474266052D+00-0.000000000000D+00 0.000000000000D+00 + 0.570985839844D+03 0.129019737244D+00 0.186264514923D-08 0.500000000000D+01 + -0.444115625000D+04 0.350134849548D+01 0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 12 45 0.0-0.630468130112D-04-0.000000000000D+00 0.450000000000D+05 + -0.157730795898D+05-0.253642654419D+01 0.000000000000D+00 0.000000000000D+00 + 0.778607275391D+04-0.555105209351D-01 0.000000000000D+00 0.600000000000D+01 + -0.184329213867D+05 0.213812446594D+01 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 12 45 0.0 0.338731333613D-04 0.181898940355D-11 0.450000000000D+05 + -0.806424853516D+04 0.165602874756D+01 0.186264514923D-08 0.000000000000D+00 + -0.825169628906D+04-0.263773250580D+01-0.931322574616D-09-0.200000000000D+01 + -0.227401533203D+05 0.363270759582D+00 0.931322574616D-09 0.000000000000D+00 +10 22 1 13 12 45 0.0-0.788709148765D-04 0.000000000000D+00 0.450000000000D+05 + -0.155609130859D+05 0.145521545410D+01 0.000000000000D+00 0.000000000000D+00 + 0.771589208984D+04-0.210558986664D+01 0.000000000000D+00-0.700000000000D+01 + -0.187270898438D+05-0.207078838348D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 12 45 0.0 0.472916290164D-04-0.909494701773D-12 0.463200000000D+05 + -0.148933134766D+05 0.389709472656D+00-0.186264514923D-08 0.000000000000D+00 + 0.204264462891D+05-0.300117492676D+00 0.186264514923D-08 0.000000000000D+00 + -0.336895507812D+04-0.353591537476D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 12 45 0.0 0.294580124319D-03 0.363797880709D-11 0.458100000000D+05 + -0.509981298828D+04-0.971966743469D+00-0.279396772385D-08 0.000000000000D+00 + 0.204384926758D+05 0.171785831451D+01 0.186264514923D-08-0.100000000000D+01 + 0.144242866211D+05-0.277926635742D+01 0.931322574616D-09 0.000000000000D+00 +13 22 1 13 12 45 0.0-0.169984996319D-04-0.000000000000D+00 0.450000000000D+05 + 0.714399609375D+04-0.165574550629D+01-0.186264514923D-08 0.000000000000D+00 + 0.904919042969D+04 0.264180850983D+01 0.931322574616D-09-0.200000000000D+01 + 0.227343100586D+05-0.530135154724D+00-0.931322574616D-09 0.000000000000D+00 +14 22 1 13 12 45 0.0 0.731600448489D-04 0.000000000000D+00 0.450000000000D+05 + 0.159770600586D+05-0.140309715271D+01 0.000000000000D+00 0.000000000000D+00 + -0.874966943359D+04 0.200267124176D+01-0.000000000000D+00-0.700000000000D+01 + 0.178673774414D+05 0.223523426056D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 12 45 0.0 0.989222899079D-04-0.000000000000D+00 0.450000000000D+05 + 0.149532636719D+05-0.324668884277D+00 0.186264514923D-08 0.000000000000D+00 + -0.204921367188D+05 0.220865249634D+00-0.186264514923D-08 0.000000000000D+00 + 0.266396582031D+04 0.354130935669D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 12 45 0.0 0.492226332426D-03 0.272848410532D-11 0.450000000000D+05 + 0.130319809570D+05 0.138427448273D+01 0.931322574616D-09 0.000000000000D+00 + 0.432973193359D+04 0.260505104065D+01-0.186264514923D-08 0.400000000000D+01 + -0.214896352539D+05 0.135995960236D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 12 45 0.0 0.103709287941D-03 0.909494701773D-12 0.450000000000D+05 + 0.486757519531D+04 0.122861099243D+01 0.279396772385D-08 0.000000000000D+00 + -0.123475458984D+05 0.272734546661D+01-0.186264514923D-08-0.300000000000D+01 + -0.217731723633D+05-0.126584720612D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 12 45 0.0-0.174283050001D-03-0.909494701773D-12 0.450000000000D+05 + -0.452366259766D+04 0.450333595276D+00 0.279396772385D-08 0.000000000000D+00 + -0.223091606445D+05 0.153003025055D+01-0.931322574616D-09 0.300000000000D+01 + -0.115025253906D+05-0.314267635346D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 12 45 0.0-0.629788264632D-04-0.000000000000D+00 0.450000000000D+05 + -0.132643896484D+05-0.707441329956D+00 0.931322574616D-09 0.000000000000D+00 + -0.200360112305D+05-0.977012634277D+00 0.931322574616D-09 0.200000000000D+01 + 0.863942724609D+04-0.335353183746D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 12 45 0.0-0.269744545221D-03-0.272848410532D-11 0.459000000000D+05 + -0.124800087891D+05-0.139414787293D+01-0.931322574616D-09 0.000000000000D+00 + -0.292828564453D+04-0.268329906464D+01 0.186264514923D-08 0.400000000000D+01 + 0.220549243164D+05-0.114761829376D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 12 45 0.0-0.157820992172D-03-0.272848410532D-11 0.450000000000D+05 + -0.441623632812D+04-0.119045257568D+01-0.279396772385D-08 0.000000000000D+00 + 0.129797124023D+05-0.270144653320D+01 0.186264514923D-08-0.300000000000D+01 + 0.214793925781D+05 0.139604949951D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 12 45 0.0-0.266376882791D-04-0.909494701773D-12 0.450000000000D+05 + 0.840682519531D+04-0.119740486145D+00-0.279396772385D-08 0.000000000000D+00 + 0.234018833008D+05-0.806606292725D+00 0.000000000000D+00 0.300000000000D+01 + 0.567860888672D+04 0.350095462799D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 12 45 0.0 0.754753127694D-04 0.181898940355D-11 0.450000000000D+05 + 0.139768916016D+05 0.695300102234D+00-0.000000000000D+00 0.000000000000D+00 + 0.192690434570D+05 0.106209468842D+01-0.931322574616D-09 0.200000000000D+01 + -0.916014697266D+04 0.328538894653D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 13 15 0.0 0.717863440514D-05-0.000000000000D+00 0.468000000000D+05 + -0.441321679688D+04-0.305789661408D+01 0.000000000000D+00 0.000000000000D+00 + 0.112566508789D+05 0.319231033325D+00-0.931322574616D-09 0.100000000000D+01 + -0.224607968750D+05 0.761643409729D+00 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 13 15 0.0 0.506359152496D-03 0.000000000000D+00 0.468000000000D+05 + 0.165394106445D+05-0.246379661560D+01 0.000000000000D+00 0.000000000000D+00 + 0.737621093750D+04 0.520019531250D-01-0.186264514923D-08-0.400000000000D+01 + -0.178932402344D+05-0.225701522827D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 13 15 0.0 0.498741865158D-04 0.000000000000D+00 0.468000000000D+05 + 0.253380341797D+05-0.365875244141D+00 0.000000000000D+00 0.000000000000D+00 + -0.595447753906D+03-0.134721755981D+00-0.279396772385D-08 0.500000000000D+01 + -0.254911132812D+04-0.355421924591D+01-0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 13 15 0.0 0.123334117234D-03 0.181898940355D-11 0.468000000000D+05 + 0.198215688477D+05 0.189835166931D+01 0.000000000000D+00 0.000000000000D+00 + -0.789694140625D+04-0.276288032532D+00-0.931322574616D-09 0.600000000000D+01 + 0.139930190430D+05-0.285103416443D+01-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 13 15 0.0 0.858930870891D-04 0.909494701773D-12 0.468000000000D+05 + 0.337382568359D+04 0.311505222321D+01-0.000000000000D+00 0.000000000000D+00 + -0.109045292969D+05-0.231389045715D+00 0.000000000000D+00 0.100000000000D+01 + 0.228166215820D+05-0.573885917664D+00-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 13 15 0.0-0.268323346973D-04-0.181898940355D-11 0.477000000000D+05 + -0.176706298828D+05 0.235126113892D+01 0.000000000000D+00 0.000000000000D+00 + -0.659714892578D+04-0.127025604248D+00 0.186264514923D-08-0.400000000000D+01 + 0.171742192383D+05 0.236611938477D+01 0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 13 15 0.0 0.306293368340D-04 0.000000000000D+00 0.481800000000D+05 + -0.254489516602D+05 0.273235321045D+00 0.000000000000D+00 0.000000000000D+00 + 0.859969726562D+03 0.151189804077D+00 0.186264514923D-08 0.500000000000D+01 + 0.195131542969D+04 0.355539703369D+01 0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 13 15 0.0-0.630486756563D-04-0.000000000000D+00 0.468000000000D+05 + -0.197998100586D+05-0.190103530884D+01 0.000000000000D+00 0.000000000000D+00 + 0.800237500000D+04 0.266815185547D+00 0.931322574616D-09 0.600000000000D+01 + -0.139194931641D+05 0.284398460388D+01 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 13 15 0.0 0.338768586516D-04 0.181898940355D-11 0.468000000000D+05 + -0.549718017578D+04 0.118108081818D+01 0.186264514923D-08 0.000000000000D+00 + -0.130568837891D+05-0.265359687805D+01-0.186264514923D-08-0.200000000000D+01 + -0.212152895508D+05 0.131982421875D+01 0.000000000000D+00 0.000000000000D+00 +10 22 1 13 13 15 0.0-0.788699835539D-04 0.000000000000D+00 0.468000000000D+05 + -0.130432729492D+05 0.130376625061D+01 0.931322574616D-09 0.000000000000D+00 + 0.339763427734D+04-0.266170406342D+01-0.000000000000D+00-0.700000000000D+01 + -0.216852353516D+05-0.119463157654D+01 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 13 15 0.0 0.472897663712D-04-0.909494701773D-12 0.468000000000D+05 + -0.138836113281D+05 0.690599441528D+00-0.931322574616D-09 0.000000000000D+00 + 0.191553530273D+05-0.111827754974D+01 0.931322574616D-09 0.000000000000D+00 + -0.952094921875D+04-0.325520515442D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 13 15 0.0 0.294586643577D-03 0.363797880709D-11 0.468000000000D+05 + -0.631363134766D+04-0.394287109375D+00-0.279396772385D-08 0.000000000000D+00 + 0.230667602539D+05 0.116229629517D+01 0.279396772385D-08-0.100000000000D+01 + 0.892983300781D+04-0.328616714477D+01 0.186264514923D-08 0.000000000000D+00 +13 22 1 13 13 15 0.0-0.170003622770D-04-0.000000000000D+00 0.468000000000D+05 + 0.460348779297D+04-0.115339946747D+01-0.279396772385D-08 0.000000000000D+00 + 0.138346059570D+05 0.262633228302D+01 0.186264514923D-08-0.200000000000D+01 + 0.209119843750D+05-0.148146438599D+01-0.000000000000D+00 0.000000000000D+00 +14 22 1 13 13 15 0.0 0.731609761715D-04 0.000000000000D+00 0.468000000000D+05 + 0.135160678711D+05-0.129179382324D+01-0.931322574616D-09 0.000000000000D+00 + -0.459198046875D+04 0.258875656128D+01 0.000000000000D+00-0.700000000000D+01 + 0.211488242188D+05 0.138717365265D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 13 15 0.0 0.989213585854D-04-0.000000000000D+00 0.468000000000D+05 + 0.140384301758D+05-0.651137351990D+00 0.931322574616D-09 0.000000000000D+00 + -0.193742275391D+05 0.102923011780D+01-0.931322574616D-09 0.000000000000D+00 + 0.885296142578D+04 0.329071807861D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 13 15 0.0 0.492232851684D-03 0.272848410532D-11 0.468000000000D+05 + 0.156741455078D+05 0.151015567780D+01 0.000000000000D+00 0.000000000000D+00 + 0.850097558594D+04 0.200072097778D+01-0.186264514923D-08 0.400000000000D+01 + -0.182423715820D+05 0.222457885742D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 13 15 0.0 0.103712081909D-03 0.909494701773D-12 0.485400000000D+05 + 0.755230810547D+04 0.173670768738D+01 0.186264514923D-08 0.000000000000D+00 + -0.744588769531D+04 0.266911220550D+01-0.279396772385D-08-0.300000000000D+01 + -0.231795156250D+05-0.286431312561D+00 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 13 15 0.0-0.174285843968D-03-0.909494701773D-12 0.468000000000D+05 + -0.318037353516D+04 0.105674552918D+01 0.279396772385D-08 0.000000000000D+00 + -0.190617646484D+05 0.203331184387D+01-0.186264514923D-08 0.300000000000D+01 + -0.166412973633D+05-0.252996826172D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 13 15 0.0-0.629788264632D-04-0.000000000000D+00 0.468000000000D+05 + -0.142894140625D+05-0.388738632202D+00 0.931322574616D-09 0.000000000000D+00 + -0.210291215820D+05-0.134825706482D+00 0.000000000000D+00 0.200000000000D+01 + 0.234781250000D+04-0.359189891815D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 13 15 0.0-0.269750133157D-03-0.272848410532D-11 0.477000000000D+05 + -0.151782109375D+05-0.156370258331D+01 0.000000000000D+00 0.000000000000D+00 + -0.727565966797D+04-0.211522102356D+01 0.186264514923D-08 0.400000000000D+01 + 0.191634482422D+05-0.204435443878D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 13 15 0.0-0.157826580107D-03-0.181898940355D-11 0.468000000000D+05 + -0.704277539062D+04-0.171197700501D+01-0.186264514923D-08 0.000000000000D+00 + 0.809645605469D+04-0.267385578155D+01 0.279396772385D-08-0.300000000000D+01 + 0.231274438477D+05 0.423471450806D+00-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 13 15 0.0-0.266395509243D-04-0.909494701773D-12 0.468000000000D+05 + 0.769485449219D+04-0.695969581604D+00-0.279396772385D-08 0.000000000000D+00 + 0.213294868164D+05-0.145909976959D+01 0.931322574616D-09 0.300000000000D+01 + 0.116792753906D+05 0.312314701080D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 13 15 0.0 0.754781067371D-04 0.181898940355D-11 0.468000000000D+05 + 0.149791494141D+05 0.376832008362D+00-0.186264514923D-08 0.000000000000D+00 + 0.204374287109D+05 0.243618965149D+00-0.000000000000D+00 0.200000000000D+01 + -0.296868066406D+04 0.354931640625D+01 0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 13 45 0.0 0.717863440514D-05-0.000000000000D+00 0.486000000000D+05 + -0.962270703125D+04-0.268501472473D+01 0.000000000000D+00 0.000000000000D+00 + 0.121812617188D+05 0.686532020569D+00-0.000000000000D+00 0.100000000000D+01 + -0.202393730469D+05 0.169061470032D+01 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 13 45 0.0 0.506360083818D-03 0.000000000000D+00 0.486000000000D+05 + 0.116932470703D+05-0.287355518341D+01 0.000000000000D+00 0.000000000000D+00 + 0.786181494141D+04 0.502821922302D+00-0.186264514923D-08-0.400000000000D+01 + -0.212074433594D+05-0.140145683289D+01 0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 13 45 0.0 0.498741865158D-04 0.000000000000D+00 0.486000000000D+05 + 0.239001098633D+05-0.121364784241D+01 0.000000000000D+00 0.000000000000D+00 + -0.662811035156D+03 0.974693298340D-01-0.279396772385D-08 0.500000000000D+01 + -0.876465380859D+04-0.330684280395D+01-0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 13 45 0.0 0.123336911202D-03 0.181898940355D-11 0.486000000000D+05 + 0.225284721680D+05 0.108876132965D+01 0.000000000000D+00 0.000000000000D+00 + -0.853664160156D+04-0.395313262939D+00-0.186264514923D-08 0.600000000000D+01 + 0.838679589844D+04-0.333774089813D+01-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 13 45 0.0 0.858940184116D-04 0.909494701773D-12 0.486000000000D+05 + 0.873469091797D+04 0.279360294342D+01-0.000000000000D+00 0.000000000000D+00 + -0.117011469727D+05-0.635109901428D+00 0.000000000000D+00 0.100000000000D+01 + 0.209153769531D+05-0.152498912811D+01-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 13 45 0.0-0.268360599876D-04-0.181898940355D-11 0.495000000000D+05 + -0.130117338867D+05 0.277943706513D+01 0.000000000000D+00 0.000000000000D+00 + -0.721638476562D+04-0.576301574707D+00 0.186264514923D-08-0.400000000000D+01 + 0.207142788086D+05 0.154166793823D+01-0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 13 45 0.0 0.306311994791D-04 0.000000000000D+00 0.495000000000D+05 + -0.241757309570D+05 0.112463760376D+01 0.000000000000D+00 0.000000000000D+00 + 0.970606933594D+03-0.662422180176D-01 0.279396772385D-08 0.500000000000D+01 + 0.819349707031D+04 0.333552360535D+01 0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 13 45 0.0-0.630486756563D-04-0.000000000000D+00 0.486000000000D+05 + -0.225129780273D+05-0.109345912933D+01 0.000000000000D+00 0.000000000000D+00 + 0.862210205078D+04 0.382926940918D+00 0.186264514923D-08 0.600000000000D+01 + -0.832762939453D+04 0.332868003845D+01 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 13 45 0.0 0.338805839419D-04 0.181898940355D-11 0.492300000000D+05 + -0.383501953125D+04 0.668448448181D+00 0.186264514923D-08 0.000000000000D+00 + -0.176294409180D+05-0.237898254394D+01-0.186264514923D-08-0.200000000000D+01 + -0.180507968750D+05 0.217330551148D+01-0.000000000000D+00 0.000000000000D+00 +10 22 1 13 13 45 0.0-0.788709148765D-04-0.000000000000D+00 0.486000000000D+05 + -0.109850454102D+05 0.957251548767D+00 0.931322574616D-09 0.000000000000D+00 + -0.172978759766D+04-0.299149990082D+01-0.931322574616D-09-0.700000000000D+01 + -0.229717573242D+05-0.225378990173D+00 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 13 45 0.0 0.472888350487D-04-0.909494701773D-12 0.486000000000D+05 + -0.125610776367D+05 0.736496925354D+00-0.000000000000D+00 0.000000000000D+00 + 0.164138745117D+05-0.191439723968D+01 0.931322574616D-09 0.000000000000D+00 + -0.149361459961D+05-0.272257423401D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 13 45 0.0 0.294592231512D-03 0.363797880709D-11 0.486000000000D+05 + -0.660960107422D+04 0.338001251221D-01-0.186264514923D-08 0.000000000000D+00 + 0.244999765625D+05 0.403251647949D+00 0.279396772385D-08-0.100000000000D+01 + 0.274649462891D+04-0.353970623016D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 13 45 0.0-0.170012935996D-04-0.000000000000D+00 0.486000000000D+05 + 0.300797949219D+04-0.624045372009D+00-0.279396772385D-08 0.000000000000D+00 + 0.183257060547D+05 0.231529235840D+01 0.279396772385D-08-0.200000000000D+01 + 0.174702045898D+05-0.231782054901D+01 0.000000000000D+00 0.000000000000D+00 +14 22 1 13 13 45 0.0 0.731619074941D-04 0.000000000000D+00 0.486000000000D+05 + 0.114501752930D+05-0.975681304932D+00-0.931322574616D-09 0.000000000000D+00 + 0.441335449219D+03 0.296168804169D+01 0.931322574616D-09-0.700000000000D+01 + 0.227967441406D+05 0.431962966919D+00-0.186264514923D-08 0.000000000000D+00 +15 22 1 13 13 45 0.0 0.989213585854D-04-0.000000000000D+00 0.486000000000D+05 + 0.127612226562D+05-0.725709915161D+00 0.000000000000D+00 0.000000000000D+00 + -0.167927773438D+05 0.182775306702D+01-0.931322574616D-09 0.000000000000D+00 + 0.143576416016D+05 0.278593826294D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 13 45 0.0 0.492238439619D-03 0.272848410532D-11 0.486000000000D+05 + 0.183078256836D+05 0.136969280243D+01-0.000000000000D+00 0.000000000000D+00 + 0.114605771484D+05 0.127731418610D+01-0.186264514923D-08 0.400000000000D+01 + -0.135849179688D+05 0.291670703888D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 13 45 0.0 0.103713013232D-03 0.909494701773D-12 0.486000000000D+05 + 0.110247216797D+05 0.208725738525D+01 0.931322574616D-09 0.000000000000D+00 + -0.290473828125D+04 0.233565521240D+01-0.279396772385D-08-0.300000000000D+01 + -0.227904086914D+05 0.716109275818D+00 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 13 45 0.0-0.174288637936D-03-0.909494701773D-12 0.486000000000D+05 + -0.700687500000D+03 0.169398307800D+01 0.186264514923D-08 0.000000000000D+00 + -0.151626987305D+05 0.224908161163D+01-0.279396772385D-08 0.300000000000D+01 + -0.204926582031D+05-0.172151756287D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 13 45 0.0-0.629797577858D-04-0.000000000000D+00 0.486000000000D+05 + -0.145237675781D+05 0.162007331848D+00 0.186264514923D-08 0.000000000000D+00 + -0.205855795898D+05 0.601060867310D+00-0.931322574616D-09 0.200000000000D+01 + -0.412481396484D+04-0.355332183838D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 13 45 0.0-0.269760377705D-03-0.272848410532D-11 0.495000000000D+05 + -0.179503852539D+05-0.146982574463D+01 0.000000000000D+00 0.000000000000D+00 + -0.104596596680D+05-0.140873527527D+01 0.186264514923D-08 0.400000000000D+01 + 0.147900805664D+05-0.278344154358D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 13 45 0.0-0.157831236720D-03-0.181898940355D-11 0.486000000000D+05 + -0.104902128906D+05-0.208567905426D+01-0.931322574616D-09 0.000000000000D+00 + 0.352338037109D+04-0.236514854431D+01 0.279396772385D-08-0.300000000000D+01 + 0.229851718750D+05-0.580237388611D+00-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 13 45 0.0-0.266423448920D-04-0.909494701773D-12 0.486000000000D+05 + 0.584296386719D+04-0.136878299713D+01-0.279396772385D-08 0.000000000000D+00 + 0.183020678711D+05-0.185812854767D+01 0.279396772385D-08 0.300000000000D+01 + 0.167766577148D+05 0.250383567810D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 13 45 0.0 0.754818320274D-04 0.181898940355D-11 0.486000000000D+05 + 0.151974453125D+05-0.166980743408D+00-0.186264514923D-08 0.000000000000D+00 + 0.202062783203D+05-0.475183486938D+00 0.000000000000D+00 0.200000000000D+01 + 0.345230664062D+04 0.353881835938D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 14 15 0.0 0.717863440514D-05-0.000000000000D+00 0.504000000000D+05 + -0.139362402344D+05-0.207493591309D+01 0.000000000000D+00 0.000000000000D+00 + 0.136246577148D+05 0.882327079773D+00 0.000000000000D+00 0.100000000000D+01 + -0.164530961914D+05 0.248907089233D+01 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 14 15 0.0 0.506361015141D-03 0.000000000000D+00 0.504000000000D+05 + 0.637541748047D+04-0.298354434967D+01 0.000000000000D+00 0.000000000000D+00 + 0.920945263672D+04 0.991812705994D+00-0.186264514923D-08-0.400000000000D+01 + -0.228733349609D+05-0.437596321106D+00 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 14 15 0.0 0.498741865158D-04 0.000000000000D+00 0.504000000000D+05 + 0.210658291016D+05-0.190075397491D+01 0.000000000000D+00 0.000000000000D+00 + -0.125763183594D+03 0.526370048523D+00-0.279396772385D-08 0.500000000000D+01 + -0.142990068359D+05-0.280225467682D+01 0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 14 15 0.0 0.123340636492D-03 0.181898940355D-11 0.504000000000D+05 + 0.237015483398D+05 0.213528633118D+00-0.000000000000D+00 0.000000000000D+00 + -0.917114160156D+04-0.267651557922D+00-0.186264514923D-08 0.600000000000D+01 + 0.213221240234D+04-0.356668472290D+01-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 14 15 0.0 0.858996063471D-04 0.909494701773D-12 0.504000000000D+05 + 0.132768061523D+05 0.221679401398D+01-0.000000000000D+00 0.000000000000D+00 + -0.130959423828D+05-0.881448745727D+00-0.000000000000D+00 0.100000000000D+01 + 0.173972534180D+05-0.235873603821D+01-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 14 15 0.0-0.268397852778D-04-0.181898940355D-11 0.520800000000D+05 + -0.784082958984D+04 0.291551399231D+01-0.000000000000D+00 0.000000000000D+00 + -0.869566259766D+04-0.106496334076D+01 0.931322574616D-09-0.400000000000D+01 + 0.226522265625D+05 0.597537994385D+00-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 14 15 0.0 0.306321308017D-04 0.000000000000D+00 0.513000000000D+05 + -0.214922187500D+05 0.182360458374D+01 0.000000000000D+00 0.000000000000D+00 + 0.500342285156D+03-0.484320640564D+00 0.279396772385D-08 0.500000000000D+01 + 0.138048442383D+05 0.285904598236D+01 0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 14 15 0.0-0.630496069789D-04 0.000000000000D+00 0.512100000000D+05 + -0.236981616211D+05-0.222641944885D+00 0.000000000000D+00 0.000000000000D+00 + 0.923346679688D+04 0.254812240601D+00 0.186264514923D-08 0.600000000000D+01 + -0.209147119141D+04 0.355534648895D+01 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 14 15 0.0 0.338852405548D-04 0.272848410532D-11 0.504000000000D+05 + -0.304997265625D+04 0.223282814026D+00 0.186264514923D-08 0.000000000000D+00 + -0.214586435547D+05-0.183482837677D+01-0.279396772385D-08-0.200000000000D+01 + -0.134928222656D+05 0.285817813873D+01-0.931322574616D-09 0.000000000000D+00 +10 22 1 13 14 15 0.0-0.788709148765D-04-0.000000000000D+00 0.504000000000D+05 + -0.966141650391D+04 0.504225730896D+00 0.186264514923D-08 0.000000000000D+00 + -0.719894335938D+04-0.303471374512D+01-0.186264514923D-08-0.700000000000D+01 + -0.224853540039D+05 0.762554168701D+00 0.931322574616D-09 0.000000000000D+00 +11 22 1 13 14 15 0.0 0.472869724035D-04-0.909494701773D-12 0.504000000000D+05 + -0.113753051758D+05 0.545348167419D+00 0.000000000000D+00 0.000000000000D+00 + 0.123451284180D+05-0.257513904571D+01-0.000000000000D+00 0.000000000000D+00 + -0.191954892578D+05-0.197928142548D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 14 15 0.0 0.294598750770D-03 0.363797880709D-11 0.504000000000D+05 + -0.632281787109D+04 0.245149612427D+00-0.931322574616D-09 0.000000000000D+00 + 0.244524350586D+05-0.465192794800D+00 0.186264514923D-08-0.100000000000D+01 + -0.364878613281D+04-0.352008152008D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 14 15 0.0-0.170031562448D-04-0.000000000000D+00 0.509700000000D+05 + 0.230871044922D+04-0.174458503723D+00-0.186264514923D-08 0.000000000000D+00 + 0.220070834961D+05 0.173473548889D+01 0.372529029846D-08-0.200000000000D+01 + 0.126759414062D+05-0.297446346283D+01 0.931322574616D-09 0.000000000000D+00 +14 22 1 13 14 15 0.0 0.731628388166D-04 0.000000000000D+00 0.504000000000D+05 + 0.100764174805D+05-0.538881301880D+00-0.186264514923D-08 0.000000000000D+00 + 0.590201708984D+04 0.305590152741D+01 0.186264514923D-08-0.700000000000D+01 + 0.226837504883D+05-0.556724548340D+00-0.931322574616D-09 0.000000000000D+00 +15 22 1 13 14 15 0.0 0.989204272628D-04-0.000000000000D+00 0.504000000000D+05 + 0.115700317383D+05-0.561452865601D+00-0.000000000000D+00 0.000000000000D+00 + -0.128683847656D+05 0.250339317322D+01-0.000000000000D+00 0.000000000000D+00 + 0.187530307617D+05 0.206619930267D+01-0.279396772385D-08 0.000000000000D+00 +17 22 1 13 14 15 0.0 0.492244958878D-03 0.272848410532D-11 0.504000000000D+05 + 0.204397299805D+05 0.955293655396D+00-0.931322574616D-09 0.000000000000D+00 + 0.130971235352D+05 0.550129890442D+00-0.000000000000D+00 0.400000000000D+01 + -0.787799023438D+04 0.338315010071D+01 0.931322574616D-09 0.000000000000D+00 +18 22 1 13 14 15 0.0 0.103714875877D-03 0.000000000000D+00 0.504000000000D+05 + 0.149203769531D+05 0.219566822052D+01 0.000000000000D+00 0.000000000000D+00 + 0.840326171875D+03 0.179963874817D+01-0.279396772385D-08-0.300000000000D+01 + -0.206344418945D+05 0.166392040253D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 14 15 0.0-0.174291431904D-03-0.909494701773D-12 0.504000000000D+05 + 0.286703759766D+04 0.224667358398D+01 0.931322574616D-09 0.000000000000D+00 + -0.111421538086D+05 0.217131328583D+01-0.279396772385D-08 0.300000000000D+01 + -0.227586469727D+05-0.779892921448D+00 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 14 15 0.0-0.629806891084D-04-0.000000000000D+00 0.504000000000D+05 + -0.136106840820D+05 0.870896339417D+00 0.186264514923D-08 0.000000000000D+00 + -0.189873251953D+05 0.113478660584D+01-0.186264514923D-08 0.200000000000D+01 + -0.102791899414D+05-0.324052524567D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 14 15 0.0-0.269765965641D-03-0.272848410532D-11 0.513000000000D+05 + -0.203010512695D+05-0.109666538239D+01 0.931322574616D-09 0.000000000000D+00 + -0.123326918945D+05-0.678209304810D+00 0.000000000000D+00 0.400000000000D+01 + 0.927241552734D+04-0.330749130249D+01-0.931322574616D-09 0.000000000000D+00 +22 22 1 13 14 15 0.0-0.157829374075D-03-0.181898940355D-11 0.513000000000D+05 + -0.144094746094D+05-0.222439002991D+01 0.000000000000D+00 0.000000000000D+00 + -0.290732421875D+03-0.184524536133D+01 0.279396772385D-08-0.300000000000D+01 + 0.210665561523D+05-0.153747272491D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 14 15 0.0-0.266451388597D-04-0.909494701773D-12 0.504000000000D+05 + 0.277663720703D+04-0.202586269379D+01-0.186264514923D-08 0.000000000000D+00 + 0.148141909180D+05-0.196882534027D+01 0.279396772385D-08 0.300000000000D+01 + 0.205766547852D+05 0.169097995758D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 14 15 0.0 0.754846259952D-04 0.181898940355D-11 0.504000000000D+05 + 0.142851791992D+05-0.864527702332D+00-0.186264514923D-08 0.000000000000D+00 + 0.188434135742D+05-0.100075435638D+01 0.186264514923D-08 0.200000000000D+01 + 0.960669042969D+04 0.325507259369D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 14 45 0.0 0.717863440514D-05-0.000000000000D+00 0.522000000000D+05 + -0.170040136719D+05-0.131810665131D+01 0.000000000000D+00 0.000000000000D+00 + 0.152163510742D+05 0.844028472900D+00 0.931322574616D-09 0.100000000000D+01 + -0.113944140625D+05 0.309519767761D+01 0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 14 45 0.0 0.506362877786D-03 0.000000000000D+00 0.522000000000D+05 + 0.113457421875D+04-0.279162597656D+01 0.000000000000D+00 0.000000000000D+00 + 0.113896210938D+05 0.140994548798D+01-0.931322574616D-09-0.400000000000D+01 + -0.227629555664D+05 0.559244155884D+00 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 14 45 0.0 0.498741865158D-04 0.000000000000D+00 0.522000000000D+05 + 0.172049248047D+05-0.234331512451D+01-0.000000000000D+00 0.000000000000D+00 + 0.130282128906D+04 0.107228279114D+01-0.279396772385D-08 0.500000000000D+01 + -0.187216357422D+05-0.207969093323D+01 0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 14 45 0.0 0.123343430460D-03 0.181898940355D-11 0.522000000000D+05 + 0.233284516602D+05-0.609929084778D+00-0.000000000000D+00 0.000000000000D+00 + -0.935480664062D+04 0.100803375244D+00-0.279396772385D-08 0.600000000000D+01 + -0.428729492188D+04-0.351972770691D+01-0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 14 45 0.0 0.859005376697D-04 0.909494701773D-12 0.522000000000D+05 + 0.166128505859D+05 0.147030544281D+01-0.000000000000D+00 0.000000000000D+00 + -0.147364506836D+05-0.899283409119D+00-0.931322574616D-09 0.100000000000D+01 + 0.125334160156D+05-0.301047325134D+01-0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 14 45 0.0-0.268444418907D-04-0.181898940355D-11 0.522000000000D+05 + -0.269488281250D+04 0.275479698181D+01-0.000000000000D+00 0.000000000000D+00 + -0.110092363281D+05-0.148556423187D+01 0.931322574616D-09-0.400000000000D+01 + 0.228373154297D+05-0.393298149109D+00-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 14 45 0.0 0.306339934468D-04 0.000000000000D+00 0.531000000000D+05 + -0.177537285156D+05 0.228556728363D+01 0.000000000000D+00 0.000000000000D+00 + -0.847892578125D+03-0.102646255493D+01 0.186264514923D-08 0.500000000000D+01 + 0.183538359375D+05 0.216275024414D+01-0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 14 45 0.0-0.630496069789D-04 0.000000000000D+00 0.529500000000D+05 + -0.233467021484D+05 0.594863891602D+00 0.000000000000D+00 0.000000000000D+00 + 0.939592333984D+04-0.110994338989D+00 0.279396772385D-08 0.600000000000D+01 + 0.430635156250D+04 0.350728607178D+01 0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 14 45 0.0 0.338889658451D-04 0.272848410532D-11 0.522000000000D+05 + -0.293616894531D+04-0.645303726196D-01 0.931322574616D-09 0.000000000000D+00 + -0.241101103516D+05-0.108390712738D+01-0.279396772385D-08-0.200000000000D+01 + -0.789427441406D+04 0.332209682465D+01-0.931322574616D-09 0.000000000000D+00 +10 22 1 13 14 45 0.0-0.788718461990D-04-0.000000000000D+00 0.522000000000D+05 + -0.917052978516D+04 0.499935150146D-01 0.931322574616D-09 0.000000000000D+00 + -0.124713618164D+05-0.277383899689D+01-0.279396772385D-08-0.700000000000D+01 + -0.202611772461D+05 0.169293022156D+01 0.000000000000D+00 0.000000000000D+00 +11 22 1 13 14 45 0.0 0.472860410810D-04-0.909494701773D-12 0.522000000000D+05 + -0.107047900391D+05 0.176988601685D+00 0.931322574616D-09 0.000000000000D+00 + 0.728217578125D+04-0.300570106506D+01-0.931322574616D-09 0.000000000000D+00 + -0.219694736328D+05-0.108291244507D+01 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 14 45 0.0 0.294604338706D-03 0.272848410532D-11 0.522000000000D+05 + -0.587405029297D+04 0.213040351868D+00-0.000000000000D+00 0.000000000000D+00 + 0.228275644531D+05-0.132979774475D+01 0.186264514923D-08-0.100000000000D+01 + -0.976219189453D+04-0.322848510742D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 14 45 0.0-0.170040875673D-04-0.000000000000D+00 0.522000000000D+05 + 0.227773828125D+04 0.105915069580D+00-0.931322574616D-09 0.000000000000D+00 + 0.244493935547D+05 0.952983856201D+00 0.372529029846D-08-0.200000000000D+01 + 0.690075195312D+04-0.340068435669D+01 0.186264514923D-08 0.000000000000D+00 +14 22 1 13 14 45 0.0 0.731665641069D-04 0.000000000000D+00 0.528600000000D+05 + 0.952069726562D+04-0.846357345581D-01-0.186264514923D-08 0.000000000000D+00 + 0.112599946289D+05 0.284736251831D+01 0.279396772385D-08-0.700000000000D+01 + 0.208182827148D+05-0.150260925293D+01-0.000000000000D+00 0.000000000000D+00 +15 22 1 13 14 45 0.0 0.989204272628D-04-0.000000000000D+00 0.522000000000D+05 + 0.108508222656D+05-0.213578224182D+00-0.931322574616D-09 0.000000000000D+00 + -0.791283886719D+04 0.295966720581D+01 0.000000000000D+00 0.000000000000D+00 + 0.217001665039D+05 0.118721294403D+01-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 14 45 0.0 0.492251478136D-03 0.363797880709D-11 0.522000000000D+05 + 0.216049877930D+05 0.305783271790D+00-0.186264514923D-08 0.000000000000D+00 + 0.135059907227D+05-0.695667266846D-01 0.000000000000D+00 0.400000000000D+01 + -0.156281396484D+04 0.358821105957D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 14 45 0.0 0.103716738522D-03 0.000000000000D+00 0.522000000000D+05 + 0.187548964844D+05 0.201527309418D+01-0.000000000000D+00 0.000000000000D+00 + 0.351366064453D+04 0.116354370117D+01-0.186264514923D-08-0.300000000000D+01 + -0.168776313477D+05 0.248317241669D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 14 45 0.0-0.174294225872D-03-0.909494701773D-12 0.522000000000D+05 + 0.727267480469D+04 0.260919952393D+01 0.931322574616D-09 0.000000000000D+00 + -0.749831396484D+04 0.184098720551D+01-0.279396772385D-08 0.300000000000D+01 + -0.232639941406D+05 0.222038269043D+00 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 14 45 0.0-0.629816204309D-04-0.000000000000D+00 0.522000000000D+05 + -0.113558833008D+05 0.163375473022D+01 0.186264514923D-08 0.000000000000D+00 + -0.166587163086D+05 0.140609550476D+01-0.279396772385D-08 0.200000000000D+01 + -0.156400595703D+05-0.267730236054D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 14 45 0.0-0.269771553576D-03-0.272848410532D-11 0.531000000000D+05 + -0.217487304688D+05-0.475541114807D+00 0.931322574616D-09 0.000000000000D+00 + -0.129555307617D+05-0.375566482544D-01 0.000000000000D+00 0.400000000000D+01 + 0.303702490234D+04-0.357568645477D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 14 45 0.0-0.157833099365D-03-0.181898940355D-11 0.531000000000D+05 + -0.183258549805D+05-0.207771205902D+01 0.000000000000D+00 0.000000000000D+00 + -0.305292480469D+04-0.121484375000D+01 0.186264514923D-08-0.300000000000D+01 + 0.175224687500D+05-0.237470245361D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 14 45 0.0-0.266442075372D-04-0.909494701773D-12 0.522000000000D+05 + -0.137178173828D+04-0.255292892456D+01-0.931322574616D-09 0.000000000000D+00 + 0.113806689453D+05-0.180375480652D+01 0.372529029846D-08 0.300000000000D+01 + 0.227855922852D+05 0.747474670410D+00-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 14 45 0.0 0.754883512855D-04 0.181898940355D-11 0.522000000000D+05 + 0.120531962891D+05-0.161488533020D+01-0.186264514923D-08 0.000000000000D+00 + 0.167557578125D+05-0.127415180206D+01 0.279396772385D-08 0.200000000000D+01 + 0.150195634766D+05 0.272030162811D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 15 15 0.0 0.717770308256D-05-0.000000000000D+00 0.540000000000D+05 + -0.186596665039D+05-0.525087356567D+00 0.931322574616D-09 0.000000000000D+00 + 0.165086865234D+05 0.549804687500D+00 0.186264514923D-08 0.100000000000D+01 + -0.545435302734D+04 0.346199989319D+01 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 15 15 0.0 0.506363809109D-03 0.909494701773D-12 0.540000000000D+05 + -0.351887939453D+04-0.234175300598D+01 0.000000000000D+00 0.000000000000D+00 + 0.141827573242D+05 0.165817070007D+01-0.000000000000D+00-0.400000000000D+01 + -0.208869101562D+05 0.151145935059D+01 0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 15 15 0.0 0.498751178384D-04 0.000000000000D+00 0.540000000000D+05 + 0.128043046875D+05-0.249676418304D+01-0.000000000000D+00 0.000000000000D+00 + 0.374253320312D+04 0.163144302368D+01-0.186264514923D-08 0.500000000000D+01 + -0.216887441406D+05-0.119563865662D+01 0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 15 15 0.0 0.123345293105D-03 0.181898940355D-11 0.540000000000D+05 + 0.216016831055D+05-0.127452850342D+01-0.000000000000D+00 0.000000000000D+00 + -0.869315917969D+04 0.659839630127D+00-0.279396772385D-08 0.600000000000D+01 + -0.103747954102D+05-0.320013427734D+01-0.000000000000D+00 0.000000000000D+00 + 5 22 1 13 15 15 0.0 0.859014689922D-04 0.909494701773D-12 0.540000000000D+05 + 0.185337670898D+05 0.663795471191D+00-0.000000000000D+00 0.000000000000D+00 + -0.161764624023D+05-0.657167434692D+00-0.186264514923D-08 0.100000000000D+01 + 0.669954394531D+04-0.342949581146D+01-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 15 15 0.0-0.268481671810D-04-0.181898940355D-11 0.540000000000D+05 + 0.192189941406D+04 0.233781528473D+01-0.000000000000D+00 0.000000000000D+00 + -0.139431596680D+05-0.173948860168D+01 0.000000000000D+00-0.400000000000D+01 + 0.212544082031D+05-0.135411548614D+01-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 15 15 0.0 0.306358560920D-04 0.000000000000D+00 0.549000000000D+05 + -0.134353925781D+05 0.246369361877D+01 0.000000000000D+00 0.000000000000D+00 + -0.320867968750D+04-0.159126186371D+01 0.186264514923D-08 0.500000000000D+01 + 0.214906425781D+05 0.130004215241D+01-0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 15 15 0.0-0.630486756563D-04 0.000000000000D+00 0.553800000000D+05 + -0.216524628906D+05 0.125362968445D+01 0.931322574616D-09 0.000000000000D+00 + 0.872060839844D+04-0.664401054382D+00 0.279396772385D-08 0.600000000000D+01 + 0.103722148438D+05 0.318901729584D+01 0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 15 15 0.0 0.338936224580D-04 0.272848410532D-11 0.540000000000D+05 + -0.315134521484D+04-0.135487556457D+00 0.931322574616D-09 0.000000000000D+00 + -0.252931391602D+05-0.220945358276D+00-0.279396772385D-08-0.200000000000D+01 + -0.168740673828D+04 0.352978992462D+01-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 15 15 0.0-0.788727775216D-04-0.000000000000D+00 0.555000000000D+05 + -0.941937060547D+04-0.301443099976D+00 0.931322574616D-09 0.000000000000D+00 + -0.170186000977D+05-0.223745250702D+01-0.279396772385D-08-0.700000000000D+01 + -0.164688710938D+05 0.249352359772D+01-0.000000000000D+00 0.000000000000D+00 +11 22 1 13 15 15 0.0 0.472841784358D-04-0.909494701773D-12 0.541500000000D+05 + -0.107901010742D+05-0.277434349060D+00 0.931322574616D-09 0.000000000000D+00 + 0.170011572266D+04-0.314523601532D+01-0.186264514923D-08 0.000000000000D+00 + -0.230436479492D+05-0.102883338928D+00 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 15 15 0.0 0.294609926641D-03 0.272848410532D-11 0.540000000000D+05 + -0.569197656250D+04-0.444946289062D-01 0.000000000000D+00 0.000000000000D+00 + 0.197364135742D+05-0.207623958588D+01 0.000000000000D+00-0.100000000000D+01 + -0.151210722656D+05-0.268710708618D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 15 15 0.0-0.170050188899D-04-0.000000000000D+00 0.540000000000D+05 + 0.255344384766D+04 0.160122871399D+00-0.000000000000D+00 0.000000000000D+00 + 0.253766704102D+05 0.698347091675D-01 0.279396772385D-08-0.200000000000D+01 + 0.591797363281D+03-0.356372737884D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 15 15 0.0 0.731674954295D-04 0.000000000000D+00 0.540000000000D+05 + 0.971909082031D+04 0.282446861267D+00-0.931322574616D-09 0.000000000000D+00 + 0.159830161133D+05 0.235775947571D+01 0.372529029846D-08-0.700000000000D+01 + 0.173440488281D+05-0.233263874054D+01 0.000000000000D+00 0.000000000000D+00 +15 22 1 13 15 15 0.0 0.989204272628D-04-0.000000000000D+00 0.540000000000D+05 + 0.108591435547D+05 0.230367660522D+00-0.931322574616D-09 0.000000000000D+00 + -0.238467041016D+04 0.313224029541D+01 0.186264514923D-08 0.000000000000D+00 + 0.229720185547D+05 0.216828346252D+00-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 15 15 0.0 0.492257997394D-03 0.363797880709D-11 0.540000000000D+05 + 0.214465415039D+05-0.499456405640D+00-0.186264514923D-08 0.000000000000D+00 + 0.129653491211D+05-0.492733955383D+00 0.186264514923D-08 0.400000000000D+01 + 0.487299365234D+04 0.351636123657D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 15 15 0.0 0.103717669845D-03 0.000000000000D+00 0.540000000000D+05 + 0.220009838867D+05 0.154585647583D+01-0.931322574616D-09 0.000000000000D+00 + 0.503814355469D+04 0.542311668396D+00-0.931322574616D-09-0.300000000000D+01 + -0.118108222656D+05 0.310992813110D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 15 15 0.0-0.174296088517D-03-0.909494701773D-12 0.545400000000D+05 + 0.120984052734D+05 0.270353317261D+01-0.000000000000D+00 0.000000000000D+00 + -0.461832470703D+04 0.133865928650D+01-0.279396772385D-08 0.300000000000D+01 + -0.219696616211D+05 0.120675849915D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 15 15 0.0-0.629825517535D-04-0.000000000000D+00 0.540000000000D+05 + -0.776743359375D+04 0.233302974701D+01 0.931322574616D-09 0.000000000000D+00 + -0.140929228516D+05 0.139976692200D+01-0.279396772385D-08 0.200000000000D+01 + -0.197927792969D+05-0.190678596497D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 15 15 0.0-0.269778072834D-03-0.272848410532D-11 0.549000000000D+05 + -0.219080009766D+05 0.319522857666D+00 0.931322574616D-09 0.000000000000D+00 + -0.125793232422D+05 0.418566703796D+00-0.931322574616D-09 0.400000000000D+01 + -0.343351708984D+04-0.356704235077D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 15 15 0.0-0.157836824656D-03-0.181898940355D-11 0.549000000000D+05 + -0.217141083984D+05-0.164074134827D+01 0.931322574616D-09 0.000000000000D+00 + -0.466662060547D+04-0.588342666626D+00 0.931322574616D-09-0.300000000000D+01 + 0.126282792969D+05-0.302799510956D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 15 15 0.0-0.266470015049D-04-0.909494701773D-12 0.540000000000D+05 + -0.627691064453D+04-0.285280895233D+01-0.000000000000D+00 0.000000000000D+00 + 0.845251269531D+04-0.142012691498D+01 0.372529029846D-08 0.300000000000D+01 + 0.232328540039D+05-0.253731727600D+00-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 15 15 0.0 0.754920765758D-04 0.181898940355D-11 0.540000000000D+05 + 0.850830175781D+04-0.230425453186D+01-0.931322574616D-09 0.000000000000D+00 + 0.144178540039D+05-0.128011703491D+01 0.372529029846D-08 0.200000000000D+01 + 0.192737045898D+05 0.197595024109D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 15 45 0.0 0.717770308256D-05 0.000000000000D+00 0.558300000000D+05 + -0.189405351562D+05 0.191473007202D+00 0.931322574616D-09 0.000000000000D+00 + 0.170551562500D+05 0.228490829468D-01 0.186264514923D-08 0.100000000000D+01 + 0.907680664062D+03 0.356099605560D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 15 45 0.0 0.506365671754D-03 0.909494701773D-12 0.558000000000D+05 + -0.718983007812D+04-0.171619701386D+01 0.000000000000D+00 0.000000000000D+00 + 0.172131264648D+05 0.166456317902D+01 0.000000000000D+00-0.400000000000D+01 + -0.173929165039D+05 0.234530830383D+01 0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 15 45 0.0 0.498751178384D-04 0.000000000000D+00 0.558000000000D+05 + 0.839001757812D+04-0.236268138886D+01-0.000000000000D+00 0.000000000000D+00 + 0.711763671875D+04 0.209373092651D+01-0.931322574616D-09 0.500000000000D+01 + -0.229703950195D+05-0.219260215759D+00 0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 15 45 0.0 0.123348087072D-03 0.181898940355D-11 0.558000000000D+05 + 0.188839946289D+05-0.170075416565D+01-0.931322574616D-09 0.000000000000D+00 + -0.691648193359D+04 0.132295989990D+01-0.279396772385D-08 0.600000000000D+01 + -0.156585180664D+05-0.263242721558D+01 0.000000000000D+00 0.000000000000D+00 + 5 22 1 13 15 45 0.0 0.859024003148D-04 0.909494701773D-12 0.558000000000D+05 + 0.190362143555D+05-0.869817733765D-01-0.931322574616D-09 0.000000000000D+00 + -0.169538823242D+05-0.169259071350D+00-0.186264514923D-08 0.100000000000D+01 + 0.346915039062D+03-0.358307743072D+01-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 15 45 0.0-0.268528237939D-04-0.181898940355D-11 0.558000000000D+05 + 0.561425097656D+04 0.174334812164D+01-0.000000000000D+00 0.000000000000D+00 + -0.171269565430D+05-0.175399780273D+01-0.000000000000D+00-0.400000000000D+01 + 0.180253920898D+05-0.221038341522D+01-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 15 45 0.0 0.306367874146D-04 0.000000000000D+00 0.562500000000D+05 + -0.905689892578D+04 0.235589027405D+01 0.000000000000D+00 0.000000000000D+00 + -0.652422509766D+04-0.206939697266D+01 0.931322574616D-09 0.500000000000D+01 + 0.229735893555D+05 0.336932182312D+00-0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 15 45 0.0-0.630486756563D-04 0.000000000000D+00 0.567000000000D+05 + -0.189762602539D+05 0.167604064941D+01 0.931322574616D-09 0.000000000000D+00 + 0.694232617188D+04-0.132000923157D+01 0.186264514923D-08 0.600000000000D+01 + 0.156393999023D+05 0.262568378449D+01 0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 15 45 0.0 0.338982790708D-04 0.272848410532D-11 0.558000000000D+05 + -0.328143212891D+04 0.295600891113D-01 0.000000000000D+00 0.000000000000D+00 + -0.249052919922D+05 0.642415046692D+00-0.186264514923D-08-0.200000000000D+01 + 0.464943212891D+04 0.346562099457D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 15 45 0.0-0.788737088442D-04-0.000000000000D+00 0.558300000000D+05 + -0.101424902344D+05-0.465312004089D+00 0.000000000000D+00 0.000000000000D+00 + -0.204025336914D+05-0.149601650238D+01-0.279396772385D-08-0.700000000000D+01 + -0.114001914062D+05 0.310178375244D+01-0.931322574616D-09 0.000000000000D+00 +11 22 1 13 15 45 0.0 0.472832471132D-04-0.909494701773D-12 0.572100000000D+05 + -0.116911391602D+05-0.711266517639D+00 0.931322574616D-09 0.000000000000D+00 + -0.385488281250D+04-0.297669982910D+01-0.279396772385D-08 0.000000000000D+00 + -0.223351538086D+05 0.884960174561D+00 0.931322574616D-09 0.000000000000D+00 +12 22 1 13 15 45 0.0 0.294615514576D-03 0.272848410532D-11 0.558000000000D+05 + -0.613435791016D+04-0.467841148376D+00 0.000000000000D+00 0.000000000000D+00 + 0.154829140625D+05-0.260744571686D+01-0.000000000000D+00-0.100000000000D+01 + -0.193105151367D+05-0.193750762939D+01 0.279396772385D-08 0.000000000000D+00 +13 22 1 13 15 45 0.0-0.170059502125D-04-0.000000000000D+00 0.558000000000D+05 + 0.270867724609D+04-0.266542434692D-01-0.000000000000D+00 0.000000000000D+00 + 0.247092031250D+05-0.799409866333D+00 0.279396772385D-08-0.200000000000D+01 + -0.576294140625D+04-0.345128059387D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 15 45 0.0 0.731684267521D-04 0.000000000000D+00 0.558000000000D+05 + 0.104317548828D+05 0.474361419678D+00-0.000000000000D+00 0.000000000000D+00 + 0.196171186523D+05 0.165123748779D+01 0.372529029846D-08-0.700000000000D+01 + 0.125291054688D+05-0.298259830475D+01 0.931322574616D-09 0.000000000000D+00 +15 22 1 13 15 45 0.0 0.989194959402D-04-0.000000000000D+00 0.558000000000D+05 + 0.116749389648D+05 0.665662765503D+00-0.931322574616D-09 0.000000000000D+00 + 0.317895800781D+04 0.299926090241D+01 0.279396772385D-08 0.000000000000D+00 + 0.224707543945D+05-0.770173072815D+00-0.931322574616D-09 0.000000000000D+00 +17 22 1 13 15 45 0.0 0.492265447974D-03 0.363797880709D-11 0.558000000000D+05 + 0.197777011719D+05-0.135306835175D+01-0.931322574616D-09 0.000000000000D+00 + 0.118824409180D+05-0.666941642761D+00 0.279396772385D-08 0.400000000000D+01 + 0.109329731445D+05 0.317335414887D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 15 45 0.0 0.103719532490D-03 0.000000000000D+00 0.558000000000D+05 + 0.241739575195D+05 0.834433555603D+00-0.931322574616D-09 0.000000000000D+00 + 0.554020849609D+04 0.439348220825D-01 0.000000000000D+00-0.300000000000D+01 + -0.582712060547D+04 0.349527168274D+01 0.931322574616D-09 0.000000000000D+00 +19 22 1 13 15 45 0.0-0.174298882484D-03-0.909494701773D-12 0.558000000000D+05 + 0.168212509766D+05 0.249257755279D+01-0.931322574616D-09 0.000000000000D+00 + -0.271877978516D+04 0.770520210266D+00-0.186264514923D-08 0.300000000000D+01 + -0.189758413086D+05 0.209809112549D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 15 45 0.0-0.629834830761D-04-0.000000000000D+00 0.558000000000D+05 + -0.306262939453D+04 0.285703945160D+01 0.000000000000D+00 0.000000000000D+00 + -0.117679082031D+05 0.114758396149D+01-0.372529029846D-08 0.200000000000D+01 + -0.224154238281D+05-0.988214492798D+00 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 15 45 0.0-0.269783660769D-03-0.272848410532D-11 0.567000000000D+05 + -0.205568398438D+05 0.118345260620D+01 0.931322574616D-09 0.000000000000D+00 + -0.115962309570D+05 0.630301475525D+00-0.186264514923D-08 0.400000000000D+01 + -0.963812548828D+04-0.328212070465D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 15 45 0.0-0.157839618623D-03-0.909494701773D-12 0.567000000000D+05 + -0.240826240234D+05-0.955310821533D+00 0.931322574616D-09 0.000000000000D+00 + -0.523918164062D+04-0.747537612915D-01 0.000000000000D+00-0.300000000000D+01 + 0.676218945312D+04-0.344772052765D+01-0.931322574616D-09 0.000000000000D+00 +23 22 1 13 15 45 0.0-0.266497954726D-04-0.909494701773D-12 0.558000000000D+05 + -0.114664238281D+05-0.286174678803D+01 0.000000000000D+00 0.000000000000D+00 + 0.634471191406D+04-0.909634590149D+00 0.279396772385D-08 0.300000000000D+01 + 0.218839882812D+05-0.123527240753D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 15 45 0.0 0.754948705435D-04 0.181898940355D-11 0.558000000000D+05 + 0.385980615234D+04-0.282454204559D+01-0.000000000000D+00 0.000000000000D+00 + 0.122904809570D+05-0.104886054993D+01 0.372529029846D-08 0.200000000000D+01 + 0.220414536133D+05 0.107943725586D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 16 15 0.0 0.717770308256D-05 0.000000000000D+00 0.576300000000D+05 + -0.180747412109D+05 0.735154151917D+00 0.931322574616D-09 0.000000000000D+00 + 0.164896328125D+05-0.671888351440D+00 0.186264514923D-08 0.100000000000D+01 + 0.719949023438D+04 0.338447856903D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 16 15 0.0 0.506368465722D-03 0.909494701773D-12 0.576000000000D+05 + -0.965558740234D+04-0.102152633667D+01 0.931322574616D-09 0.000000000000D+00 + 0.200097905273D+05 0.139684677124D+01 0.931322574616D-09-0.400000000000D+01 + -0.125536030273D+05 0.299663925171D+01 0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 16 15 0.0 0.498751178384D-04 0.000000000000D+00 0.576000000000D+05 + 0.444384570312D+04-0.198785877228D+01-0.000000000000D+00 0.000000000000D+00 + 0.111626728516D+05 0.236152744293D+01-0.931322574616D-09 0.500000000000D+01 + -0.224684116211D+05 0.773199081421D+00 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 16 15 0.0 0.123350881040D-03 0.909494701773D-12 0.576000000000D+05 + 0.156465156250D+05-0.184930133820D+01-0.931322574616D-09 0.000000000000D+00 + -0.393310546875D+04 0.198160743713D+01-0.186264514923D-08 0.600000000000D+01 + -0.197286948242D+05-0.186052894592D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 16 15 0.0 0.859033316374D-04 0.909494701773D-12 0.576000000000D+05 + 0.183164541016D+05-0.679060935974D+00-0.931322574616D-09 0.000000000000D+00 + -0.166724394531D+05 0.506393432617D+00-0.186264514923D-08 0.100000000000D+01 + -0.603257275391D+04-0.345912075043D+01-0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 16 15 0.0-0.268574804068D-04-0.181898940355D-11 0.576000000000D+05 + 0.815355810547D+04 0.107515048981D+01-0.931322574616D-09 0.000000000000D+00 + -0.200925561523D+05-0.149504947662D+01-0.931322574616D-09-0.400000000000D+01 + 0.133999335938D+05-0.289559078217D+01-0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 16 15 0.0 0.306386500597D-04 0.000000000000D+00 0.576000000000D+05 + -0.510105517578D+04 0.200457096100D+01 0.000000000000D+00 0.000000000000D+00 + -0.105465825195D+05-0.236190986633D+01 0.000000000000D+00 0.500000000000D+01 + 0.226875454102D+05-0.652857780457D+00-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 16 15 0.0-0.630477443337D-04 0.000000000000D+00 0.585000000000D+05 + -0.157841508789D+05 0.182426452637D+01 0.931322574616D-09 0.000000000000D+00 + 0.397116259766D+04-0.197117710114D+01 0.186264514923D-08 0.600000000000D+01 + 0.197035209961D+05 0.186093235016D+01-0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 16 15 0.0 0.339029356837D-04 0.272848410532D-11 0.576000000000D+05 + -0.291718457031D+04 0.406170845032D+00-0.000000000000D+00 0.000000000000D+00 + -0.230470737305D+05 0.139535140991D+01-0.186264514923D-08-0.200000000000D+01 + 0.106283579102D+05 0.313468647003D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 16 15 0.0-0.788746401668D-04-0.000000000000D+00 0.576000000000D+05 + -0.109506372070D+05-0.390890121460D+00 0.000000000000D+00 0.000000000000D+00 + -0.223416528320D+05-0.650724411011D+00-0.279396772385D-08-0.700000000000D+01 + -0.544693066406D+04 0.346987438202D+01-0.186264514923D-08 0.000000000000D+00 +11 22 1 13 16 15 0.0 0.472813844681D-04-0.909494701773D-12 0.576000000000D+05 + -0.132756245117D+05-0.102074909210D+01 0.000000000000D+00 0.000000000000D+00 + -0.884796923828D+04-0.252951908112D+01-0.279396772385D-08 0.000000000000D+00 + -0.198990551758D+05 0.180421161652D+01 0.000000000000D+00 0.000000000000D+00 +12 22 1 13 16 15 0.0 0.294620171189D-03 0.272848410532D-11 0.576000000000D+05 + -0.742141308594D+04-0.965994834900D+00 0.000000000000D+00 0.000000000000D+00 + 0.105181777344D+05-0.285917091370D+01-0.931322574616D-09-0.100000000000D+01 + -0.220056049805D+05-0.103748130798D+01 0.186264514923D-08 0.000000000000D+00 +13 22 1 13 16 15 0.0-0.170059502125D-04-0.000000000000D+00 0.576000000000D+05 + 0.232998144531D+04-0.424595832825D+00 0.000000000000D+00 0.000000000000D+00 + 0.225749516602D+05-0.154243850708D+01 0.931322574616D-09-0.200000000000D+01 + -0.116725039062D+05-0.307234668732D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 16 15 0.0 0.731684267521D-04 0.000000000000D+00 0.576000000000D+05 + 0.112871191406D+05 0.434907913208D+00-0.000000000000D+00 0.000000000000D+00 + 0.218548149414D+05 0.824193000793D+00 0.372529029846D-08-0.700000000000D+01 + 0.674533398438D+04-0.340209484100D+01 0.186264514923D-08 0.000000000000D+00 +15 22 1 13 16 15 0.0 0.989213585854D-04-0.000000000000D+00 0.576000000000D+05 + 0.131876118164D+05 0.988397598267D+00-0.000000000000D+00 0.000000000000D+00 + 0.824291894531D+04 0.258478927612D+01 0.372529029846D-08 0.000000000000D+00 + 0.202351289062D+05-0.169777297974D+01-0.000000000000D+00 0.000000000000D+00 +17 22 1 13 16 15 0.0 0.492271967232D-03 0.363797880709D-11 0.576000000000D+05 + 0.166175185547D+05-0.213692092895D+01-0.000000000000D+00 0.000000000000D+00 + 0.107192197266D+05-0.584600448608D+00 0.372529029846D-08 0.400000000000D+01 + 0.161499096680D+05 0.258571338654D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 16 15 0.0 0.103721395135D-03 0.000000000000D+00 0.581400000000D+05 + 0.249114604492D+05-0.319061279297D-01-0.931322574616D-09 0.000000000000D+00 + 0.532052880859D+04-0.248736381531D+00 0.931322574616D-09-0.300000000000D+01 + 0.608904296875D+03 0.360924911499D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 16 15 0.0-0.174301676452D-03-0.909494701773D-12 0.583800000000D+05 + 0.208938378906D+05 0.198678970337D+01-0.931322574616D-09 0.000000000000D+00 + -0.181548193359D+04 0.250263214111D+00-0.931322574616D-09 0.300000000000D+01 + -0.145141918945D+05 0.282708454132D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 16 15 0.0-0.629853457212D-04-0.000000000000D+00 0.576000000000D+05 + 0.235953564453D+04 0.311841392517D+01-0.000000000000D+00 0.000000000000D+00 + -0.100659414062D+05 0.722498893738D+00-0.372529029846D-08 0.200000000000D+01 + -0.233039062500D+05 0.755786895752D-02 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 16 15 0.0-0.269789248705D-03-0.272848410532D-11 0.576000000000D+05 + -0.176778813477D+05 0.199704360962D+01 0.000000000000D+00 0.000000000000D+00 + -0.104670615234D+05 0.582248687744D+00-0.279396772385D-08 0.400000000000D+01 + -0.150962441406D+05-0.274302101135D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 16 15 0.0-0.157843343914D-03-0.909494701773D-12 0.585000000000D+05 + -0.250529116211D+05-0.103621482849D+00 0.931322574616D-09 0.000000000000D+00 + -0.505562939453D+04 0.240278244019D+00-0.931322574616D-09-0.300000000000D+01 + 0.376075683594D+03-0.360214233398D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 16 15 0.0-0.266516581178D-04-0.909494701773D-12 0.585000000000D+05 + -0.163919716797D+05-0.256026935577D+01 0.931322574616D-09 0.000000000000D+00 + 0.518773437500D+04-0.382627487183D+00 0.186264514923D-08 0.300000000000D+01 + 0.188433237305D+05-0.212131023407D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 16 15 0.0 0.754985958338D-04 0.181898940355D-11 0.576000000000D+05 + -0.150701757812D+04-0.309104251862D+01 0.000000000000D+00 0.000000000000D+00 + 0.107428730469D+05-0.650342941284D+00 0.372529029846D-08 0.200000000000D+01 + 0.231096406250D+05 0.997419357300D-01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 16 45 0.0 0.717770308256D-05 0.000000000000D+00 0.618600000000D+05 + -0.164373437500D+05 0.104132461548D+01 0.186264514923D-08 0.000000000000D+00 + 0.145932031250D+05-0.143801403046D+01 0.186264514923D-08 0.100000000000D+01 + 0.129342915039D+05 0.294612884522D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 16 45 0.0 0.506370328367D-03 0.909494701773D-12 0.594000000000D+05 + -0.108939189453D+05-0.370764732361D+00 0.931322574616D-09 0.000000000000D+00 + 0.220843525391D+05 0.868248939514D+00 0.931322574616D-09-0.400000000000D+01 + -0.674471093750D+04 0.341575336456D+01 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 16 45 0.0 0.498760491610D-04 0.000000000000D+00 0.594000000000D+05 + 0.132851708984D+04-0.145580482483D+01 0.000000000000D+00 0.000000000000D+00 + 0.154605766602D+05 0.236645889282D+01-0.000000000000D+00 0.500000000000D+01 + -0.202236640625D+05 0.170452117920D+01 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 16 45 0.0 0.123352743685D-03 0.909494701773D-12 0.594000000000D+05 + 0.123899306641D+05-0.172698783874D+01-0.931322574616D-09 0.000000000000D+00 + 0.146784667969D+03 0.252322578430D+01-0.186264514923D-08 0.600000000000D+01 + -0.222696201172D+05-0.944341659546D+00 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 16 45 0.0 0.859042629600D-04 0.909494701773D-12 0.594000000000D+05 + 0.167317343750D+05-0.103897953033D+01-0.186264514923D-08 0.000000000000D+00 + -0.150734970703D+05 0.127699089050D+01-0.186264514923D-08 0.100000000000D+01 + -0.119447021484D+05-0.306717205048D+01-0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 16 45 0.0-0.268574804068D-04-0.181898940355D-11 0.594000000000D+05 + 0.950759912109D+04 0.444592475891D+00-0.931322574616D-09 0.000000000000D+00 + -0.223508032227D+05-0.973711967468D+00-0.186264514923D-08-0.400000000000D+01 + 0.773620800781D+04-0.335648441315D+01-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 16 45 0.0 0.306395813823D-04 0.000000000000D+00 0.594000000000D+05 + -0.193934423828D+04 0.148922252655D+01-0.000000000000D+00 0.000000000000D+00 + -0.148717924805D+05-0.239691829681D+01-0.000000000000D+00 0.500000000000D+01 + 0.206529414062D+05-0.159336376190D+01-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 16 45 0.0-0.630468130112D-04 0.000000000000D+00 0.603000000000D+05 + -0.125699130859D+05 0.170561695099D+01 0.931322574616D-09 0.000000000000D+00 + -0.847353515625D+02-0.250754070282D+01 0.931322574616D-09 0.600000000000D+01 + 0.222529287109D+05 0.953493118286D+00-0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 16 45 0.0 0.339075922966D-04 0.272848410532D-11 0.594000000000D+05 + -0.173010253906D+04 0.930319786072D+00-0.000000000000D+00 0.000000000000D+00 + -0.200044360352D+05 0.194538879395D+01-0.000000000000D+00-0.200000000000D+01 + 0.157891044922D+05 0.256237506867D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 16 45 0.0-0.788765028119D-04-0.909494701773D-12 0.594000000000D+05 + -0.114012851562D+05-0.706510543823D-01-0.000000000000D+00 0.000000000000D+00 + -0.227522753906D+05 0.182829856873D+00-0.279396772385D-08-0.700000000000D+01 + 0.929333007812D+03 0.356864070892D+01-0.186264514923D-08 0.000000000000D+00 +11 22 1 13 16 45 0.0 0.472804531455D-04-0.909494701773D-12 0.594000000000D+05 + -0.152407358398D+05-0.112293243408D+01-0.000000000000D+00 0.000000000000D+00 + -0.128359355469D+05-0.187465953827D+01-0.372529029846D-08 0.000000000000D+00 + -0.159240078125D+05 0.258380317688D+01-0.000000000000D+00 0.000000000000D+00 +12 22 1 13 16 45 0.0 0.294625759125D-03 0.272848410532D-11 0.594000000000D+05 + -0.959264794922D+04-0.143207740784D+01 0.000000000000D+00 0.000000000000D+00 + 0.537125341797D+04-0.281021118164D+01-0.186264514923D-08-0.100000000000D+01 + -0.229968168945D+05-0.566196441650D-01 0.931322574616D-09 0.000000000000D+00 +13 22 1 13 16 45 0.0-0.170068815351D-04-0.000000000000D+00 0.594000000000D+05 + 0.109453662109D+04-0.964245796204D+00 0.000000000000D+00 0.000000000000D+00 + 0.192877187500D+05-0.206784534454D+01 0.000000000000D+00-0.200000000000D+01 + -0.166808432617D+05-0.245644855499D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 16 45 0.0 0.731693580747D-04 0.000000000000D+00 0.594000000000D+05 + 0.118489985352D+05 0.149597167969D+00 0.000000000000D+00 0.000000000000D+00 + 0.225798164062D+05-0.102310180664D-01 0.279396772385D-08-0.700000000000D+01 + 0.439824707031D+03-0.355851364136D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 16 45 0.0 0.989213585854D-04-0.000000000000D+00 0.594000000000D+05 + 0.151138071289D+05 0.111333274841D+01 0.000000000000D+00 0.000000000000D+00 + 0.123542402344D+05 0.195477581024D+01 0.372529029846D-08 0.000000000000D+00 + 0.164375024414D+05-0.249448966980D+01 0.000000000000D+00 0.000000000000D+00 +17 22 1 13 16 45 0.0 0.492278486490D-03 0.363797880709D-11 0.594000000000D+05 + 0.121931123047D+05-0.274111080170D+01 0.000000000000D+00 0.000000000000D+00 + 0.990979736328D+04-0.283715248108D+00 0.372529029846D-08 0.400000000000D+01 + 0.201215893555D+05 0.179866886139D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 16 45 0.0 0.103722326457D-03 0.909494701773D-12 0.594000000000D+05 + 0.240339238281D+05-0.939930915832D+00-0.931322574616D-09 0.000000000000D+00 + 0.479542968750D+04-0.291858673096D+00 0.279396772385D-08-0.300000000000D+01 + 0.699774853516D+04 0.344325351715D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 16 45 0.0-0.174304470420D-03-0.909494701773D-12 0.594000000000D+05 + 0.238301220703D+05 0.124299335480D+01-0.931322574616D-09 0.000000000000D+00 + -0.172736914062D+04-0.120209693909D+00-0.000000000000D+00 0.300000000000D+01 + -0.892990917969D+04 0.333736038208D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 16 45 0.0-0.629872083664D-04-0.909494701773D-12 0.594000000000D+05 + 0.797602978516D+04 0.306867027283D+01-0.931322574616D-09 0.000000000000D+00 + -0.921002587891D+04 0.225927352905D+00-0.279396772385D-08 0.200000000000D+01 + -0.223881171875D+05 0.100348377228D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 16 45 0.0-0.269794836640D-03-0.272848410532D-11 0.594300000000D+05 + -0.134665634766D+05 0.264630699158D+01-0.000000000000D+00 0.000000000000D+00 + -0.963812158203D+04 0.305609703064D+00-0.372529029846D-08 0.400000000000D+01 + -0.193852954102D+05-0.199163341522D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 16 45 0.0-0.157846137881D-03-0.909494701773D-12 0.594000000000D+05 + -0.244214960938D+05 0.804664611816D+00 0.931322574616D-09 0.000000000000D+00 + -0.452320507812D+04 0.308468818665D+00-0.186264514923D-08-0.300000000000D+01 + -0.603896386719D+04-0.347967338562D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 16 45 0.0-0.266544520855D-04-0.909494701773D-12 0.603000000000D+05 + -0.205133120117D+05-0.197680950165D+01 0.931322574616D-09 0.000000000000D+00 + 0.491068017578D+04 0.507364273071D-01 0.931322574616D-09 0.300000000000D+01 + 0.143459125977D+05-0.284337234497D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 16 45 0.0 0.755023211241D-04 0.181898940355D-11 0.594000000000D+05 + -0.708630468750D+04-0.305634117127D+01 0.931322574616D-09 0.000000000000D+00 + 0.999135009766D+04-0.182018280029D+00 0.279396772385D-08 0.200000000000D+01 + 0.223957861328D+05-0.887826919556D+00-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 17 15 0.0 0.717677175999D-05 0.000000000000D+00 0.613200000000D+05 + -0.144829897461D+05 0.108733367920D+01 0.186264514923D-08 0.000000000000D+00 + 0.113373295898D+05-0.216331386566D+01 0.186264514923D-08 0.100000000000D+01 + 0.176684936523D+05 0.227994060516D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 17 15 0.0 0.506373122335D-03 0.909494701773D-12 0.612000000000D+05 + -0.110777534180D+05 0.135281562805D+00 0.186264514923D-08 0.000000000000D+00 + 0.230124516602D+05 0.135776519775D+00 0.931322574616D-09-0.400000000000D+01 + -0.415585449219D+03 0.357106208801D+01 0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 17 15 0.0 0.498769804835D-04 0.000000000000D+00 0.612000000000D+05 + -0.766041992188D+03-0.872208595276D+00 0.000000000000D+00 0.000000000000D+00 + 0.195068940430D+05 0.208124065399D+01 0.000000000000D+00 0.500000000000D+01 + -0.164122895508D+05 0.250261592865D+01 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 17 15 0.0 0.123355537653D-03 0.909494701773D-12 0.612000000000D+05 + 0.956191357422D+04-0.138494586945D+01-0.931322574616D-09 0.000000000000D+00 + 0.502101953125D+04 0.285009479523D+01-0.931322574616D-09 0.600000000000D+01 + -0.230843388672D+05 0.449638366699D-01 0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 17 15 0.0 0.859051942825D-04 0.909494701773D-12 0.612000000000D+05 + 0.147356953125D+05-0.113451671600D+01-0.186264514923D-08 0.000000000000D+00 + -0.120857788086D+05 0.203003215790D+01-0.186264514923D-08 0.100000000000D+01 + -0.169314931641D+05-0.243768310547D+01 0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 17 15 0.0-0.268612056971D-04-0.181898940355D-11 0.612000000000D+05 + 0.983748486328D+04-0.479135513306D-01-0.186264514923D-08 0.000000000000D+00 + -0.234727983398D+05-0.245046615601D+00-0.186264514923D-08-0.400000000000D+01 + 0.147305908203D+04-0.355728244782D+01-0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 17 15 0.0 0.306423753500D-04 0.000000000000D+00 0.612000000000D+05 + 0.223469238281D+03 0.912961959839D+00-0.000000000000D+00 0.000000000000D+00 + -0.190006264648D+05-0.214204311371D+01-0.000000000000D+00 0.500000000000D+01 + 0.170247036133D+05-0.241206073761D+01-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 17 15 0.0-0.630468130112D-04 0.000000000000D+00 0.622200000000D+05 + -0.977446972656D+04 0.137056732178D+01 0.000000000000D+00 0.000000000000D+00 + -0.492890869141D+04-0.283314132690D+01 0.931322574616D-09 0.600000000000D+01 + 0.230920009766D+05-0.272521972656D-01-0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 17 15 0.0 0.339122489095D-04 0.272848410532D-11 0.612000000000D+05 + 0.464942382812D+03 0.150896453857D+01-0.000000000000D+00 0.000000000000D+00 + -0.162020400391D+05 0.223291015625D+01 0.000000000000D+00-0.200000000000D+01 + 0.197340332031D+05 0.179244804382D+01-0.279396772385D-08 0.000000000000D+00 +10 22 1 13 17 15 0.0-0.788783654571D-04-0.909494701773D-12 0.612000000000D+05 + -0.110791870117D+05 0.457914352417D+00-0.000000000000D+00 0.000000000000D+00 + -0.217576762695D+05 0.893465042114D+00-0.186264514923D-08-0.700000000000D+01 + 0.723334326172D+04 0.339009189606D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 17 15 0.0 0.472785905004D-04-0.909494701773D-12 0.633600000000D+05 + -0.171642890625D+05-0.970121383667D+00 0.000000000000D+00 0.100000000000D+01 + -0.155323417969D+05-0.111291408539D+01-0.372529029846D-08 0.000000000000D+00 + -0.107176181641D+05 0.316349315643D+01-0.931322574616D-09 0.000000000000D+00 +12 22 1 13 17 15 0.0 0.294630415738D-03 0.272848410532D-11 0.612000000000D+05 + -0.124945541992D+05-0.176162528992D+01-0.000000000000D+00 0.000000000000D+00 + 0.567690429688D+03-0.248554992676D+01-0.279396772385D-08-0.100000000000D+01 + -0.222065322266D+05 0.929063796997D+00 0.931322574616D-09 0.000000000000D+00 +13 22 1 13 17 15 0.0-0.170078128576D-04-0.000000000000D+00 0.612000000000D+05 + -0.116809228516D+04-0.154814815521D+01 0.000000000000D+00 0.000000000000D+00 + 0.152958354492D+05-0.231953144074D+01-0.000000000000D+00-0.200000000000D+01 + -0.204018505859D+05-0.165125656128D+01 0.279396772385D-08 0.000000000000D+00 +14 22 1 13 17 15 0.0 0.731702893972D-04 0.000000000000D+00 0.612000000000D+05 + 0.116954111328D+05-0.351447105408D+00 0.931322574616D-09 0.000000000000D+00 + 0.218809257812D+05-0.740414619446D+00 0.186264514923D-08-0.700000000000D+01 + -0.589968896484D+04-0.343961238861D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 17 15 0.0 0.989213585854D-04-0.000000000000D+00 0.612000000000D+05 + 0.170449916992D+05 0.988691329956D+00 0.931322574616D-09 0.000000000000D+00 + 0.152086958008D+05 0.120625305176D+01 0.372529029846D-08 0.000000000000D+00 + 0.113706943359D+05-0.309882164002D+01 0.931322574616D-09 0.000000000000D+00 +17 22 1 13 17 15 0.0 0.492285937071D-03 0.363797880709D-11 0.612000000000D+05 + 0.690851367188D+04-0.308164978027D+01 0.931322574616D-09 0.000000000000D+00 + 0.978341943359D+04 0.158924102783D+00 0.372529029846D-08 0.400000000000D+01 + 0.225415478516D+05 0.872701644898D+00-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 17 15 0.0 0.103724189103D-03 0.909494701773D-12 0.612000000000D+05 + 0.215755766602D+05-0.176857757568D+01 0.000000000000D+00 0.000000000000D+00 + 0.441910009766D+04-0.877590179443D-01 0.372529029846D-08-0.300000000000D+01 + 0.128442197266D+05 0.301063060761D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 17 15 0.0-0.174307264388D-03-0.909494701773D-12 0.612000000000D+05 + 0.252819550781D+05 0.355530738831D+00-0.931322574616D-09 0.000000000000D+00 + -0.211380908203D+04-0.268045425415D+00 0.931322574616D-09 0.300000000000D+01 + -0.265499023438D+04 0.358948326111D+01 0.931322574616D-09 0.000000000000D+00 +20 22 1 13 17 15 0.0-0.629890710115D-04-0.909494701773D-12 0.612900000000D+05 + 0.132183115234D+05 0.270648288727D+01-0.186264514923D-08 0.000000000000D+00 + -0.922788183594D+04-0.229731559753D+00-0.186264514923D-08 0.200000000000D+01 + -0.197377607422D+05 0.192227649689D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 17 15 0.0-0.269800424576D-03-0.272848410532D-11 0.612000000000D+05 + -0.830470800781D+04 0.304098892212D+01-0.931322574616D-09 0.000000000000D+00 + -0.946135205078D+04-0.127542495728D+00-0.372529029846D-08 0.400000000000D+01 + -0.221735283203D+05-0.108631801605D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 17 15 0.0-0.157848931849D-03-0.909494701773D-12 0.614400000000D+05 + -0.221942163086D+05 0.164975547791D+01 0.000000000000D+00 0.000000000000D+00 + -0.409606103516D+04 0.126673698425D+00-0.279396772385D-08-0.300000000000D+01 + -0.119898242188D+05-0.308967876434D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 17 15 0.0-0.266563147306D-04-0.909494701773D-12 0.621000000000D+05 + -0.233819038086D+05-0.118347835541D+01 0.931322574616D-09 0.000000000000D+00 + 0.525877246094D+04 0.299254417419D+00 0.000000000000D+00 0.300000000000D+01 + 0.873940771484D+04-0.334563255310D+01-0.931322574616D-09 0.000000000000D+00 +24 22 1 13 17 15 0.0 0.755060464144D-04 0.181898940355D-11 0.612000000000D+05 + -0.123268510742D+05-0.271820545196D+01 0.186264514923D-08 0.000000000000D+00 + 0.100644086914D+05 0.247906684875D+00 0.186264514923D-08 0.200000000000D+01 + 0.199544238281D+05-0.180730152130D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 17 45 0.0 0.717677175999D-05 0.000000000000D+00 0.639000000000D+05 + -0.126663911133D+05 0.895647048950D+00 0.931322574616D-09 0.000000000000D+00 + 0.689600537109D+04-0.273808956146D+01 0.931322574616D-09 0.100000000000D+01 + 0.210360761719D+05 0.143754100800D+01-0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 17 45 0.0 0.506375916302D-03 0.909494701773D-12 0.630000000000D+05 + -0.105378574219D+05 0.424439430237D+00 0.186264514923D-08 0.000000000000D+00 + 0.225055151367D+05-0.708729743957D+00 0.931322574616D-09-0.400000000000D+01 + 0.594564941406D+04 0.345130634308D+01 0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 17 45 0.0 0.498779118061D-04 0.000000000000D+00 0.630000000000D+05 + -0.184669482422D+04-0.346890449524D+00 0.931322574616D-09 0.000000000000D+00 + 0.227893193359D+05 0.152462673187D+01 0.000000000000D+00 0.500000000000D+01 + -0.113313198242D+05 0.310610961914D+01 0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 17 45 0.0 0.123358331621D-03 0.181898940355D-11 0.630000000000D+05 + 0.748463183594D+04-0.909410476685D+00-0.000000000000D+00 0.000000000000D+00 + 0.102375532227D+05 0.289579200745D+01-0.000000000000D+00 0.600000000000D+01 + -0.221099892578D+05 0.103054904938D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 17 45 0.0 0.859061256051D-04 0.909494701773D-12 0.630000000000D+05 + 0.127987128906D+05-0.979453086853D+00-0.186264514923D-08 0.000000000000D+00 + -0.784453808594D+04 0.265173721314D+01-0.931322574616D-09 0.100000000000D+01 + -0.206069057617D+05-0.161959648132D+01 0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 17 45 0.0-0.268658623099D-04-0.181898940355D-11 0.630000000000D+05 + 0.946243212891D+04-0.329562187195D+00-0.186264514923D-08 0.000000000000D+00 + -0.231624765625D+05 0.600392341614D+00-0.186264514923D-08-0.400000000000D+01 + -0.490423193359D+04-0.348251056671D+01-0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 17 45 0.0 0.306433066726D-04 0.000000000000D+00 0.630000000000D+05 + 0.137666992188D+04 0.385210037231D+00-0.931322574616D-09 0.000000000000D+00 + -0.224162465820D+05-0.161050701141D+01-0.931322574616D-09 0.500000000000D+01 + 0.120810209961D+05-0.304540157318D+01-0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 17 45 0.0-0.630458816886D-04 0.000000000000D+00 0.630000000000D+05 + -0.771531982422D+04 0.903772354126D+00 0.000000000000D+00 0.000000000000D+00 + -0.101173178711D+05-0.288226795196D+01 0.000000000000D+00 0.600000000000D+01 + 0.221557827148D+05-0.100639343262D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 17 45 0.0 0.339159741998D-04 0.272848410532D-11 0.630000000000D+05 + 0.367078955078D+04 0.203545188904D+01-0.000000000000D+00 0.000000000000D+00 + -0.121349423828D+05 0.224030113220D+01 0.931322574616D-09-0.200000000000D+01 + 0.221584575195D+05 0.883782386780D+00-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 17 45 0.0-0.788802281022D-04-0.000000000000D+00 0.630000000000D+05 + -0.967323242188D+04 0.111818027496D+01-0.000000000000D+00 0.000000000000D+00 + -0.196635083008D+05 0.139250564575D+01-0.931322574616D-09-0.700000000000D+01 + 0.129751987305D+05 0.294813632965D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 17 45 0.0 0.472776591778D-04-0.909494701773D-12 0.639000000000D+05 + -0.185769409180D+05-0.558457374573D+00-0.931322574616D-09 0.100000000000D+01 + -0.168468432617D+05-0.358345985413D+00-0.279396772385D-08 0.000000000000D+00 + -0.468260937500D+04 0.349852561951D+01-0.186264514923D-08 0.000000000000D+00 +12 22 1 13 17 45 0.0 0.294636003673D-03 0.272848410532D-11 0.630000000000D+05 + -0.158014296875D+05-0.187058639526D+01-0.931322574616D-09 0.000000000000D+00 + -0.345070703125D+04-0.195189189911D+01-0.279396772385D-08-0.100000000000D+01 + -0.196953208008D+05 0.184304523468D+01 0.000000000000D+00 0.000000000000D+00 +13 22 1 13 17 45 0.0-0.170078128576D-04-0.000000000000D+00 0.630000000000D+05 + -0.443973437500D+04-0.206717109680D+01-0.000000000000D+00 0.000000000000D+00 + 0.111099174805D+05-0.228523540497D+01-0.186264514923D-08-0.200000000000D+01 + -0.225488579102D+05-0.718873977661D+00 0.186264514923D-08 0.000000000000D+00 +14 22 1 13 17 45 0.0 0.731702893972D-04 0.000000000000D+00 0.630000000000D+05 + 0.104958461914D+05-0.998086929321D+00 0.931322574616D-09 0.000000000000D+00 + 0.200328344727D+05-0.127424526215D+01 0.931322574616D-09-0.700000000000D+01 + -0.117826499023D+05-0.305452537537D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 17 45 0.0 0.989204272628D-04-0.000000000000D+00 0.630000000000D+05 + 0.185170078125D+05 0.605433464050D+00 0.931322574616D-09 0.000000000000D+00 + 0.166930664062D+05 0.451483726501D+00 0.372529029846D-08 0.000000000000D+00 + 0.542561865234D+04-0.346398448944D+01 0.186264514923D-08 0.000000000000D+00 +17 22 1 13 17 45 0.0 0.492292456329D-03 0.272848410532D-11 0.630000000000D+05 + 0.128505517578D+04-0.311396789551D+01 0.186264514923D-08 0.000000000000D+00 + 0.105057421875D+05 0.640984535217D+00 0.279396772385D-08 0.400000000000D+01 + 0.232225502930D+05-0.121029853821D+00-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 17 45 0.0 0.103726983070D-03 0.909494701773D-12 0.630000000000D+05 + 0.177808461914D+05-0.240855026245D+01 0.931322574616D-09 0.000000000000D+00 + 0.459991406250D+04 0.315773963928D+00 0.372529029846D-08-0.300000000000D+01 + 0.176961425781D+05 0.234548473358D+01 0.000000000000D+00 0.000000000000D+00 +19 22 1 13 17 45 0.0-0.174310058355D-03-0.909494701773D-12 0.630000000000D+05 + 0.250938496094D+05-0.558777809143D+00-0.000000000000D+00 0.000000000000D+00 + -0.253891552734D+04-0.161778450012D+00 0.186264514923D-08 0.300000000000D+01 + 0.382524707031D+04 0.356402587891D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 17 45 0.0-0.629909336567D-04-0.909494701773D-12 0.630000000000D+05 + 0.175589062500D+05 0.207822799683D+01-0.186264514923D-08 0.000000000000D+00 + -0.994956591797D+04-0.540464401245D+00-0.931322574616D-09 0.200000000000D+01 + -0.155573149414D+05 0.269246292114D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 17 45 0.0-0.269806012511D-03-0.272848410532D-11 0.630000000000D+05 + -0.270385351562D+04 0.312917423248D+01-0.186264514923D-08 0.000000000000D+00 + -0.101311972656D+05-0.616860389709D+00-0.279396772385D-08 0.400000000000D+01 + -0.232456870117D+05-0.972967147827D-01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 17 45 0.0-0.157852657139D-03-0.181898940355D-11 0.630000000000D+05 + -0.185873955078D+05 0.232071208954D+01-0.000000000000D+00 0.000000000000D+00 + -0.419255419922D+04-0.262773513794D+00-0.279396772385D-08-0.300000000000D+01 + -0.170186967773D+05-0.246177387237D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 17 45 0.0-0.266591086984D-04-0.909494701773D-12 0.639000000000D+05 + -0.247102226562D+05-0.284657478332D+00 0.931322574616D-09 0.000000000000D+00 + 0.584216894531D+04 0.306232452393D+00-0.931322574616D-09 0.300000000000D+01 + 0.245722998047D+04-0.358924198151D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 17 45 0.0 0.755088403821D-04 0.181898940355D-11 0.639000000000D+05 + -0.167151103516D+05-0.212017250061D+01 0.186264514923D-08 0.000000000000D+00 + 0.107999956055D+05 0.538985252380D+00 0.931322574616D-09 0.200000000000D+01 + 0.159730952148D+05-0.258780479431D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 18 15 0.0 0.717677175999D-05 0.000000000000D+00 0.657000000000D+05 + -0.113639780273D+05 0.529434204102D+00 0.931322574616D-09 0.000000000000D+00 + 0.162476611328D+04-0.307312965393D+01 0.931322574616D-09 0.100000000000D+01 + 0.227768715820D+05 0.484144210815D+00-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 18 15 0.0 0.506379641592D-03 0.909494701773D-12 0.648000000000D+05 + -0.970003857422D+04 0.464678764343D+00 0.186264514923D-08 0.000000000000D+00 + 0.204609956055D+05-0.155320644379D+01 0.931322574616D-09-0.400000000000D+01 + 0.118495058594D+05 0.306620788574D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 18 15 0.0 0.498797744513D-04 0.909494701773D-12 0.648000000000D+05 + -0.210768750000D+04 0.247249603271D-01 0.186264514923D-08 0.000000000000D+00 + 0.248692944336D+05 0.758726119995D+00 0.931322574616D-09 0.500000000000D+01 + -0.537504003906D+04 0.346899509430D+01 0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 18 15 0.0 0.123360194266D-03 0.181898940355D-11 0.648000000000D+05 + 0.630439208984D+04-0.406675338745D+00 0.000000000000D+00 0.000000000000D+00 + 0.152619233398D+05 0.263650798798D+01-0.000000000000D+00 0.600000000000D+01 + -0.194226074219D+05 0.193593788147D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 18 15 0.0 0.859070569277D-04 0.909494701773D-12 0.648000000000D+05 + 0.113266113281D+05-0.630595207214D+00-0.931322574616D-09 0.000000000000D+00 + -0.267682128906D+04 0.304574871063D+01-0.931322574616D-09 0.100000000000D+01 + -0.226868173828D+05-0.676461219788D+00 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 18 15 0.0-0.268695876002D-04-0.181898940355D-11 0.648000000000D+05 + 0.879845166016D+04-0.367083549500D+00-0.186264514923D-08 0.000000000000D+00 + -0.213082114258D+05 0.145101833343D+01-0.931322574616D-09-0.400000000000D+01 + -0.109018247070D+05-0.313818836212D+01-0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 18 15 0.0 0.306433066726D-04 0.000000000000D+00 0.648000000000D+05 + 0.169830126953D+04 0.328159332275D-02-0.186264514923D-08 0.000000000000D+00 + -0.246662177734D+05-0.859791755676D+00-0.931322574616D-09 0.500000000000D+01 + 0.620258300781D+04-0.344381046295D+01-0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 18 15 0.0-0.630458816886D-04 0.000000000000D+00 0.648000000000D+05 + -0.653735595703D+04 0.409602165222D+00-0.000000000000D+00 0.000000000000D+00 + -0.151235966797D+05-0.263048934936D+01-0.000000000000D+00 0.600000000000D+01 + 0.195150190430D+05-0.190899467468D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 18 15 0.0 0.339206308126D-04 0.181898940355D-11 0.648000000000D+05 + 0.769934082031D+04 0.240728855133D+01 0.000000000000D+00 0.000000000000D+00 + -0.828981201172D+04 0.199427318573D+01 0.186264514923D-08-0.200000000000D+01 + 0.228740727539D+05-0.939922332764D-01-0.186264514923D-08 0.000000000000D+00 +10 22 1 13 18 15 0.0-0.788811594248D-04-0.000000000000D+00 0.648000000000D+05 + -0.703641503906D+04 0.180700206757D+01-0.000000000000D+00 0.000000000000D+00 + -0.169036728516D+05 0.162804222107D+01-0.000000000000D+00-0.700000000000D+01 + 0.177090385742D+05 0.227746009827D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 18 15 0.0 0.472767278552D-04-0.909494701773D-12 0.657000000000D+05 + -0.190433681641D+05 0.708322525024D-01-0.931322574616D-09 0.100000000000D+01 + -0.168923657227D+05 0.280375480652D+00-0.186264514923D-08 0.000000000000D+00 + 0.171436865234D+04 0.356308746338D+01-0.186264514923D-08 0.000000000000D+00 +12 22 1 13 18 15 0.0 0.294640660286D-03 0.272848410532D-11 0.648000000000D+05 + -0.190660439453D+05-0.171000766754D+01-0.931322574616D-09 0.000000000000D+00 + -0.639168847656D+04-0.130632400513D+01-0.279396772385D-08-0.100000000000D+01 + -0.156574360352D+05 0.261429119110D+01-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 18 15 0.0-0.170087441802D-04-0.000000000000D+00 0.648000000000D+05 + -0.850897753906D+04-0.241878795624D+01-0.931322574616D-09 0.000000000000D+00 + 0.722163525391D+04-0.199785900116D+01-0.186264514923D-08-0.200000000000D+01 + -0.229564565430D+05 0.268932342529D+00 0.931322574616D-09 0.000000000000D+00 +14 22 1 13 18 15 0.0 0.731712207198D-04 0.000000000000D+00 0.648000000000D+05 + 0.807395703125D+04-0.169154167175D+01 0.000000000000D+00 0.000000000000D+00 + 0.174470703125D+05-0.155383205414D+01 0.000000000000D+00-0.700000000000D+01 + -0.167537822266D+05-0.243306159973D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 18 15 0.0 0.989204272628D-04-0.000000000000D+00 0.648000000000D+05 + 0.190901132812D+05-0.539779663086D-03 0.931322574616D-09 0.000000000000D+00 + 0.168962241211D+05-0.200350761414D+00 0.279396772385D-08 0.000000000000D+00 + -0.938681152344D+03-0.356156444550D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 18 15 0.0 0.492298044264D-03 0.272848410532D-11 0.648000000000D+05 + -0.411708300781D+04-0.284004402161D+01 0.186264514923D-08 0.000000000000D+00 + 0.120481625977D+05 0.105207824707D+01 0.186264514923D-08 0.400000000000D+01 + 0.221110747070D+05-0.110605525970D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 18 15 0.0 0.103728845715D-03 0.909494701773D-12 0.648000000000D+05 + 0.130668525391D+05-0.277985286713D+01 0.186264514923D-08 0.000000000000D+00 + 0.562510009766D+04 0.833902359009D+00 0.372529029846D-08-0.300000000000D+01 + 0.211793793945D+05 0.149982261658D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 18 15 0.0-0.174312852323D-03-0.909494701773D-12 0.648000000000D+05 + 0.233268876953D+05-0.137939453125D+01 0.000000000000D+00 0.000000000000D+00 + -0.255201220703D+04 0.183314323425D+00 0.279396772385D-08 0.300000000000D+01 + 0.100097729492D+05 0.326306819916D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 18 15 0.0-0.629927963018D-04-0.909494701773D-12 0.648000000000D+05 + 0.205918012695D+05 0.127064704895D+01-0.186264514923D-08 0.000000000000D+00 + -0.110391992188D+05-0.628631591797D+00 0.000000000000D+00 0.200000000000D+01 + -0.101703916016D+05 0.325403118133D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 18 15 0.0-0.269811600447D-03-0.272848410532D-11 0.664800000000D+05 + 0.277227832031D+04 0.290556240082D+01-0.186264514923D-08 0.000000000000D+00 + -0.116488125000D+05-0.105106258392D+01-0.186264514923D-08 0.400000000000D+01 + -0.225195190430D+05 0.898838996887D+00 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 18 15 0.0-0.157856382430D-03-0.181898940355D-11 0.648000000000D+05 + -0.139956459961D+05 0.273303127289D+01-0.931322574616D-09 0.000000000000D+00 + -0.511874316406D+04-0.779204368591D+00-0.279396772385D-08-0.300000000000D+01 + -0.207377988281D+05-0.164367675781D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 18 15 0.0-0.266609713435D-04-0.909494701773D-12 0.664200000000D+05 + -0.244154091797D+05 0.599725723267D+00 0.000000000000D+00 0.000000000000D+00 + 0.620787353516D+04 0.592336654663D-01-0.186264514923D-08 0.300000000000D+01 + -0.401492871094D+04-0.355535221100D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 18 15 0.0 0.755125656724D-04 0.181898940355D-11 0.658200000000D+05 + -0.198524931641D+05-0.134471893311D+01 0.931322574616D-09 0.000000000000D+00 + 0.118754042969D+05 0.615775108337D+00 0.000000000000D+00 0.200000000000D+01 + 0.107582495117D+05-0.316894817352D+01-0.931322574616D-09 0.000000000000D+00 + 1 22 1 13 18 45 0.0 0.717677175999D-05-0.000000000000D+00 0.682200000000D+05 + -0.108098154297D+05 0.813646316528D-01 0.000000000000D+00 0.000000000000D+00 + -0.399007910156D+04-0.311436367035D+01 0.000000000000D+00 0.100000000000D+01 + 0.227565805664D+05-0.506517410278D+00-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 18 45 0.0 0.506382435560D-03 0.909494701773D-12 0.666000000000D+05 + -0.900748876953D+04 0.268898010254D+00 0.186264514923D-08 0.000000000000D+00 + 0.169829580078D+05-0.228324699402D+01 0.931322574616D-09-0.400000000000D+01 + 0.168423173828D+05 0.244557285309D+01-0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 18 45 0.0 0.498816370964D-04 0.909494701773D-12 0.666000000000D+05 + -0.188982177734D+04 0.177504539490D+00 0.186264514923D-08 0.000000000000D+00 + 0.254521748047D+05-0.120820045471D+00 0.931322574616D-09 0.500000000000D+01 + 0.995922851562D+03 0.356401062012D+01 0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 18 45 0.0 0.123362988234D-03 0.181898940355D-11 0.666000000000D+05 + 0.597175683594D+04 0.152254104614D-01 0.931322574616D-09 0.000000000000D+00 + 0.195589580078D+05 0.209533214569D+01 0.000000000000D+00 0.600000000000D+01 + -0.152310527344D+05 0.269099235535D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 18 45 0.0 0.859079882503D-04 0.909494701773D-12 0.666000000000D+05 + 0.105914096680D+05-0.177589416504D+00-0.931322574616D-09 0.000000000000D+00 + 0.294480419922D+04 0.314899063110D+01-0.000000000000D+00 0.100000000000D+01 + -0.230109418945D+05 0.318583488464D+00 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 18 45 0.0-0.268733128905D-04-0.181898940355D-11 0.666000000000D+05 + 0.828139404297D+04-0.171943664551D+00-0.279396772385D-08 0.000000000000D+00 + -0.180047841797D+05 0.219221401215D+01-0.931322574616D-09-0.400000000000D+01 + -0.160557875977D+05-0.255129337311D+01 0.000000000000D+00 0.000000000000D+00 + 7 22 1 13 18 45 0.0 0.306433066726D-04-0.000000000000D+00 0.666000000000D+05 + 0.151805322266D+04-0.164068222046D+00-0.186264514923D-08 0.000000000000D+00 + -0.254353994141D+05 0.170421600342D-01-0.931322574616D-09 0.500000000000D+01 + -0.156373535156D+03-0.357571411133D+01-0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 18 45 0.0-0.630458816886D-04 0.000000000000D+00 0.666000000000D+05 + -0.619299853516D+04-0.552082061768D-02-0.931322574616D-09 0.000000000000D+00 + -0.194184521484D+05-0.209912300110D+01-0.000000000000D+00 0.600000000000D+01 + 0.153711914062D+05-0.266564083099D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 18 45 0.0 0.339243561029D-04 0.181898940355D-11 0.666000000000D+05 + 0.121948593750D+05 0.254338073731D+01 0.931322574616D-09 0.000000000000D+00 + -0.506850292969D+04 0.156096076965D+01 0.279396772385D-08-0.200000000000D+01 + 0.218237358398D+05-0.106568527222D+01-0.931322574616D-09 0.000000000000D+00 +10 22 1 13 18 45 0.0-0.788830220699D-04-0.000000000000D+00 0.666000000000D+05 + -0.321856738281D+04 0.241191005707D+01-0.000000000000D+00 0.000000000000D+00 + -0.139658740234D+05 0.159291934967D+01 0.931322574616D-09-0.700000000000D+01 + 0.210681127930D+05 0.143067169189D+01-0.279396772385D-08 0.000000000000D+00 +11 22 1 13 18 45 0.0 0.472757965326D-04-0.909494701773D-12 0.667500000000D+05 + -0.182388603516D+05 0.837710380554D+00-0.931322574616D-09 0.000000000000D+00 + -0.159591152344D+05 0.717642784119D+00-0.186264514923D-08 0.000000000000D+00 + 0.797886328125D+04 0.335229682922D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 18 45 0.0 0.294646248221D-03 0.272848410532D-11 0.666000000000D+05 + -0.217918173828D+05-0.127490997314D+01-0.186264514923D-08 0.000000000000D+00 + -0.815309179688D+04-0.659962654114D+00-0.279396772385D-08-0.100000000000D+01 + -0.104058041992D+05 0.318285369873D+01-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 18 45 0.0-0.170087441802D-04-0.000000000000D+00 0.666000000000D+05 + -0.129989736328D+05-0.252434539795D+01-0.931322574616D-09 0.000000000000D+00 + 0.402680908203D+04-0.152941799164D+01-0.279396772385D-08-0.200000000000D+01 + -0.215930761719D+05 0.123616600037D+01 0.000000000000D+00 0.000000000000D+00 +14 22 1 13 18 45 0.0 0.731721520424D-04 0.000000000000D+00 0.666000000000D+05 + 0.444503564453D+04-0.232063579559D+01-0.000000000000D+00 0.000000000000D+00 + 0.146013251953D+05-0.156447029114D+01-0.000000000000D+00-0.700000000000D+01 + -0.204284423828D+05-0.162337493897D+01 0.279396772385D-08 0.000000000000D+00 +15 22 1 13 18 45 0.0 0.989194959402D-04-0.000000000000D+00 0.666000000000D+05 + 0.184261997070D+05-0.753680229187D+00 0.931322574616D-09 0.000000000000D+00 + 0.160872792969D+05-0.660921096802D+00 0.186264514923D-08 0.000000000000D+00 + -0.723036816406D+04-0.338378906250D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 18 45 0.0 0.492304563522D-03 0.272848410532D-11 0.666000000000D+05 + -0.878345019531D+04-0.230799865723D+01 0.186264514923D-08 0.000000000000D+00 + 0.141903471680D+05 0.129266166687D+01 0.931322574616D-09 0.400000000000D+01 + 0.192916977539D+05-0.200638103485D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 18 45 0.0 0.103731639683D-03 0.909494701773D-12 0.666000000000D+05 + 0.795840625000D+04-0.284438610077D+01 0.279396772385D-08 0.000000000000D+00 + 0.760644042969D+04 0.135935497284D+01 0.279396772385D-08-0.300000000000D+01 + 0.230264379883D+05 0.539337158203D+00-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 18 45 0.0-0.174314714968D-03-0.909494701773D-12 0.666000000000D+05 + 0.202478540039D+05-0.200140190124D+01 0.186264514923D-08 0.000000000000D+00 + -0.177069433594D+04 0.707724571228D+00 0.279396772385D-08 0.300000000000D+01 + 0.154206323242D+05 0.270999717712D+01 0.000000000000D+00 0.000000000000D+00 +20 22 1 13 18 45 0.0-0.629946589470D-04-0.909494701773D-12 0.666000000000D+05 + 0.220933183594D+05 0.396802902222D+00-0.931322574616D-09 0.000000000000D+00 + -0.120555341797D+05-0.456547737122D+00 0.931322574616D-09 0.200000000000D+01 + -0.399462597656D+04 0.356322479248D+01 0.931322574616D-09 0.000000000000D+00 +21 22 1 13 18 45 0.0-0.269816257060D-03-0.272848410532D-11 0.666000000000D+05 + 0.759342333984D+04 0.241206836700D+01-0.186264514923D-08 0.000000000000D+00 + -0.138195766602D+05-0.132707309723D+01-0.931322574616D-09 0.400000000000D+01 + -0.200519194336D+05 0.182507324219D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 18 45 0.0-0.157860107720D-03-0.181898940355D-11 0.666000000000D+05 + -0.893143701172D+04 0.284201622009D+01-0.186264514923D-08 0.000000000000D+00 + -0.701073046875D+04-0.131712627411D+01-0.279396772385D-08-0.300000000000D+01 + -0.228590332031D+05-0.697752952576D+00 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 18 45 0.0-0.266637653112D-04-0.909494701773D-12 0.666000000000D+05 + -0.226300444336D+05 0.135347843170D+01-0.000000000000D+00 0.000000000000D+00 + 0.592266406250D+04-0.407610893250D+00-0.186264514923D-08 0.300000000000D+01 + -0.101766879883D+05-0.324659633637D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 18 45 0.0 0.755153596401D-04 0.181898940355D-11 0.666000000000D+05 + -0.215144111328D+05-0.500069618225D+00 0.931322574616D-09 0.000000000000D+00 + 0.128649135742D+05 0.440974235535D+00-0.931322574616D-09 0.200000000000D+01 + 0.471199609375D+04-0.350553226471D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 19 15 0.0 0.717677175999D-05-0.000000000000D+00 0.684000000000D+05 + -0.110564287109D+05-0.342476844788D+00 0.000000000000D+00 0.000000000000D+00 + -0.940370068359D+04-0.285175418854D+01 0.000000000000D+00 0.100000000000D+01 + 0.209770444336D+05-0.145789146423D+01-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 19 15 0.0 0.506386160850D-03 0.909494701773D-12 0.685500000000D+05 + -0.884116943359D+04-0.107708930969D+00 0.186264514923D-08 0.000000000000D+00 + 0.123697382812D+05-0.280030632019D+01 0.000000000000D+00-0.400000000000D+01 + 0.205404960938D+05 0.163696193695D+01-0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 19 15 0.0 0.498834997415D-04 0.909494701773D-12 0.684000000000D+05 + -0.161537695312D+04 0.872945785522D-01 0.279396772385D-08 0.000000000000D+00 + 0.244345058594D+05-0.100009155273D+01 0.000000000000D+00 0.500000000000D+01 + 0.729027001953D+04 0.338452053070D+01 0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 19 15 0.0 0.123365782201D-03 0.181898940355D-11 0.684000000000D+05 + 0.625528857422D+04 0.265037536621D+00 0.186264514923D-08 0.000000000000D+00 + 0.226752299805D+05 0.133879375458D+01 0.000000000000D+00 0.600000000000D+01 + -0.986056591797D+04 0.323737907410D+01 0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 19 15 0.0 0.859089195728D-04 0.909494701773D-12 0.684000000000D+05 + 0.106856445312D+05 0.272718429565D+00-0.000000000000D+00 0.000000000000D+00 + 0.847246533203D+04 0.294202899933D+01-0.000000000000D+00 0.100000000000D+01 + -0.215550288086D+05 0.128851318359D+01 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 19 15 0.0-0.268770381808D-04-0.181898940355D-11 0.684000000000D+05 + 0.828742578125D+04 0.202029228210D+00-0.186264514923D-08 0.000000000000D+00 + -0.135420361328D+05 0.272482013702D+01-0.931322574616D-09-0.400000000000D+01 + -0.199680634766D+05-0.176754474640D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 19 15 0.0 0.306433066726D-04-0.000000000000D+00 0.684000000000D+05 + 0.125440820312D+04-0.883693695068D-01-0.279396772385D-08 0.000000000000D+00 + -0.245975122070D+05 0.906044960022D+00-0.931322574616D-09 0.500000000000D+01 + -0.650312158203D+04-0.343024063110D+01-0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 19 15 0.0-0.630458816886D-04-0.000000000000D+00 0.684000000000D+05 + -0.645489941406D+04-0.251158714294D+00-0.186264514923D-08 0.000000000000D+00 + -0.225505253906D+05-0.135235500336D+01-0.000000000000D+00 0.600000000000D+01 + 0.100417856445D+05-0.321768283844D+01-0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 19 15 0.0 0.339280813932D-04 0.181898940355D-11 0.684000000000D+05 + 0.166857050781D+05 0.239794731140D+01 0.186264514923D-08 0.000000000000D+00 + -0.272644628906D+04 0.103457260132D+01 0.279396772385D-08-0.200000000000D+01 + 0.190864223633D+05-0.195621395111D+01-0.000000000000D+00 0.000000000000D+00 +10 22 1 13 19 15 0.0-0.788839533925D-04-0.000000000000D+00 0.684000000000D+05 + 0.153376416016D+04 0.282989978790D+01 0.931322574616D-09 0.000000000000D+00 + -0.113095429688D+05 0.132511901855D+01 0.186264514923D-08-0.700000000000D+01 + 0.227933476562D+05 0.473980903625D+00-0.186264514923D-08 0.000000000000D+00 +11 22 1 13 19 15 0.0 0.472748652101D-04-0.909494701773D-12 0.684000000000D+05 + -0.160085190430D+05 0.163610744476D+01-0.931322574616D-09 0.000000000000D+00 + -0.144599038086D+05 0.904664993286D+00-0.931322574616D-09 0.000000000000D+00 + 0.136268598633D+05 0.288255119324D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 19 15 0.0 0.294650904834D-03 0.272848410532D-11 0.691200000000D+05 + -0.235145258789D+05-0.605831146240D+00-0.186264514923D-08 0.000000000000D+00 + -0.883145166016D+04-0.119336128235D+00-0.186264514923D-08-0.100000000000D+01 + -0.434763623047D+04 0.350461387634D+01-0.186264514923D-08 0.000000000000D+00 +13 22 1 13 19 15 0.0-0.170096755028D-04-0.000000000000D+00 0.684000000000D+05 + -0.174232602539D+05-0.234248924255D+01-0.186264514923D-08 0.000000000000D+00 + 0.176534326172D+04-0.978630065918D+00-0.279396772385D-08-0.200000000000D+01 + -0.185634350586D+05 0.210834980011D+01-0.000000000000D+00 0.000000000000D+00 +14 22 1 13 19 15 0.0 0.731721520424D-04 0.000000000000D+00 0.684000000000D+05 + -0.178128906250D+03-0.278019046783D+01-0.931322574616D-09 0.000000000000D+00 + 0.119590141602D+05-0.133638095856D+01-0.931322574616D-09-0.700000000000D+01 + -0.225224379883D+05-0.688190460205D+00 0.186264514923D-08 0.000000000000D+00 +15 22 1 13 19 15 0.0 0.989194959402D-04-0.000000000000D+00 0.684000000000D+05 + 0.163502846680D+05-0.155074596405D+01 0.931322574616D-09 0.000000000000D+00 + 0.146644790039D+05-0.877018928528D+00 0.000000000000D+00 0.000000000000D+00 + -0.129628212891D+05-0.294419956207D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 19 15 0.0 0.492311082780D-03 0.272848410532D-11 0.684000000000D+05 + -0.123223217773D+05-0.160417842865D+01 0.186264514923D-08 0.000000000000D+00 + 0.165557011719D+05 0.129122066498D+01-0.000000000000D+00 0.400000000000D+01 + 0.149809858398D+05-0.275229549408D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 19 15 0.0 0.103737227619D-03 0.909494701773D-12 0.684000000000D+05 + 0.300640429688D+04-0.261155796051D+01 0.279396772385D-08 0.000000000000D+00 + 0.104560244141D+05 0.178072357178D+01 0.186264514923D-08-0.300000000000D+01 + 0.230965668945D+05-0.461764335632D+00-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 19 15 0.0-0.174317508936D-03-0.909494701773D-12 0.684000000000D+05 + 0.162854458008D+05-0.235222244263D+01 0.279396772385D-08 0.000000000000D+00 + 0.475004882812D+02 0.131767654419D+01 0.279396772385D-08 0.300000000000D+01 + 0.196398730469D+05 0.194765090942D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 19 15 0.0-0.629965215921D-04-0.909494701773D-12 0.684000000000D+05 + 0.220533564453D+05-0.422338485718D+00 0.000000000000D+00 0.000000000000D+00 + -0.125311479492D+05-0.333089828491D-01 0.186264514923D-08 0.200000000000D+01 + 0.249095751953D+04 0.359604835510D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 19 15 0.0-0.269821844995D-03-0.272848410532D-11 0.684000000000D+05 + 0.113424667969D+05 0.173071193695D+01-0.186264514923D-08 0.000000000000D+00 + -0.162842939453D+05-0.136778926849D+01-0.000000000000D+00 0.400000000000D+01 + -0.160343203125D+05 0.260990428925D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 19 15 0.0-0.157863833010D-03-0.181898940355D-11 0.684000000000D+05 + -0.394607080078D+04 0.264976596832D+01-0.279396772385D-08 0.000000000000D+00 + -0.980548193359D+04-0.176392841339D+01-0.186264514923D-08-0.300000000000D+01 + -0.232163896484D+05 0.303546905518D+00 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 19 15 0.0-0.266656279564D-04-0.909494701773D-12 0.684000000000D+05 + -0.196783212891D+05 0.188277530670D+01-0.186264514923D-08 0.000000000000D+00 + 0.465309082031D+04-0.101912021637D+01-0.279396772385D-08 0.300000000000D+01 + -0.155517055664D+05-0.268687629700D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 19 15 0.0 0.755181536078D-04 0.181898940355D-11 0.684300000000D+05 + -0.216813627930D+05 0.297241210938D+00 0.000000000000D+00 0.000000000000D+00 + 0.133156508789D+05 0.222444534302D-01-0.186264514923D-08 0.200000000000D+01 + -0.169865039062D+04-0.357112979889D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 19 45 0.0 0.717677175999D-05-0.000000000000D+00 0.702000000000D+05 + -0.119670097656D+05-0.640874862671D+00-0.931322574616D-09 0.000000000000D+00 + -0.140946997070D+05-0.232101726532D+01-0.000000000000D+00 0.100000000000D+01 + 0.175760136719D+05-0.229649162293D+01-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 19 45 0.0 0.506388954818D-03 0.909494701773D-12 0.702000000000D+05 + -0.945134570312D+04-0.577219963074D+00 0.931322574616D-09 0.000000000000D+00 + 0.707072949219D+04-0.303734016419D+01 0.000000000000D+00-0.400000000000D+01 + 0.226594467773D+05 0.702120780945D+00-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 19 45 0.0 0.498853623867D-04 0.909494701773D-12 0.702000000000D+05 + -0.170982519531D+04-0.225292205810D+00 0.279396772385D-08 0.000000000000D+00 + 0.219208081055D+05-0.176503467560D+01 0.000000000000D+00 0.500000000000D+01 + 0.130236738281D+05 0.294482517242D+01-0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 19 45 0.0 0.123369507492D-03 0.181898940355D-11 0.702000000000D+05 + 0.678644775391D+04 0.283888816833D+00 0.186264514923D-08 0.000000000000D+00 + 0.243086630859D+05 0.466242790222D+00 0.000000000000D+00 0.600000000000D+01 + -0.372729882812D+04 0.353307247162D+01 0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 19 45 0.0 0.859107822180D-04 0.909494701773D-12 0.702000000000D+05 + 0.115078266602D+05 0.614737510681D+00 0.000000000000D+00 0.000000000000D+00 + 0.133655766602D+05 0.245228958130D+01-0.000000000000D+00 0.100000000000D+01 + -0.184324863281D+05 0.215838813782D+01 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 19 45 0.0-0.268807634711D-04-0.181898940355D-11 0.702000000000D+05 + 0.906434716797D+04 0.668139457703D+00-0.186264514923D-08 0.000000000000D+00 + -0.836212011719D+04 0.298104476929D+01-0.000000000000D+00-0.400000000000D+01 + -0.223371098633D+05-0.847746849060D+00 0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 19 45 0.0 0.306433066726D-04-0.000000000000D+00 0.702000000000D+05 + 0.133657275391D+04 0.213634490967D+00-0.279396772385D-08 0.000000000000D+00 + -0.222365839844D+05 0.169069004059D+01-0.000000000000D+00 0.500000000000D+01 + -0.123449135742D+05-0.301826667786D+01-0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 19 45 0.0-0.630458816886D-04-0.000000000000D+00 0.702000000000D+05 + -0.695924462891D+04-0.268342018127D+00-0.186264514923D-08 0.000000000000D+00 + -0.242156391602D+05-0.487369537354D+00-0.000000000000D+00 0.600000000000D+01 + 0.393665087891D+04-0.352183246612D+01-0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 19 45 0.0 0.339308753610D-04 0.181898940355D-11 0.702000000000D+05 + 0.206563115234D+05 0.196877288818D+01 0.279396772385D-08 0.000000000000D+00 + -0.133599267578D+04 0.521397590637D+00 0.279396772385D-08-0.200000000000D+01 + 0.148718129883D+05-0.269637966156D+01 0.931322574616D-09 0.000000000000D+00 +10 22 1 13 19 45 0.0-0.788848847151D-04-0.000000000000D+00 0.702000000000D+05 + 0.681031982422D+04 0.298464393616D+01 0.186264514923D-08 0.000000000000D+00 + -0.929000390625D+04 0.900551795959D+00 0.186264514923D-08-0.700000000000D+01 + 0.227531376953D+05-0.518141746521D+00-0.931322574616D-09 0.000000000000D+00 +11 22 1 13 19 45 0.0 0.472739338875D-04-0.909494701773D-12 0.702000000000D+05 + -0.123991293945D+05 0.235107707977D+01-0.000000000000D+00 0.000000000000D+00 + -0.128560332031D+05 0.837014198303D+00 0.000000000000D+00 0.000000000000D+00 + 0.182221450195D+05 0.219021320343D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 19 45 0.0 0.294656492770D-03 0.272848410532D-11 0.711000000000D+05 + -0.238798544922D+05 0.217262268066D+00-0.186264514923D-08 0.000000000000D+00 + -0.869739550781D+04 0.231368064880D+00-0.186264514923D-08-0.100000000000D+01 + 0.204741064453D+04 0.355476474762D+01-0.186264514923D-08 0.000000000000D+00 +13 22 1 13 19 45 0.0-0.170106068254D-04-0.000000000000D+00 0.702000000000D+05 + -0.212605742188D+05-0.187655544281D+01-0.186264514923D-08 0.000000000000D+00 + 0.487760742188D+03-0.454190254211D+00-0.186264514923D-08-0.200000000000D+01 + -0.141006801758D+05 0.281817817688D+01-0.931322574616D-09 0.000000000000D+00 +14 22 1 13 19 45 0.0 0.731730833650D-04 0.000000000000D+00 0.702000000000D+05 + -0.541214843750D+04-0.298850631714D+01-0.931322574616D-09 0.000000000000D+00 + 0.989249462891D+04-0.938971519470D+00-0.186264514923D-08-0.700000000000D+01 + -0.228739882812D+05 0.300090789795D+00 0.931322574616D-09 0.000000000000D+00 +15 22 1 13 19 45 0.0 0.989185646176D-04-0.000000000000D+00 0.702000000000D+05 + 0.128858842773D+05-0.227745532989D+01 0.000000000000D+00 0.000000000000D+00 + 0.130834228516D+05-0.838945388794D+00-0.000000000000D+00 0.000000000000D+00 + -0.176923564453D+05-0.227665805817D+01 0.279396772385D-08 0.000000000000D+00 +17 22 1 13 19 45 0.0 0.492316670716D-03 0.272848410532D-11 0.702000000000D+05 + -0.145215551758D+05-0.838995933533D+00 0.931322574616D-09 0.000000000000D+00 + 0.186740659180D+05 0.101690578461D+01-0.931322574616D-09 0.400000000000D+01 + 0.951121386719D+04-0.328579139710D+01-0.931322574616D-09 0.000000000000D+00 +18 22 1 13 19 45 0.0 0.103740021586D-03 0.909494701773D-12 0.702000000000D+05 + -0.129690527344D+04-0.213611888886D+01 0.279396772385D-08 0.000000000000D+00 + 0.138959882812D+05 0.200139427185D+01 0.931322574616D-09-0.300000000000D+01 + 0.213859843750D+05-0.142647075653D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 19 45 0.0-0.174320302904D-03-0.909494701773D-12 0.702000000000D+05 + 0.119608803711D+05-0.240295886993D+01 0.279396772385D-08 0.000000000000D+00 + 0.295686230469D+04 0.190083789825D+01 0.186264514923D-08 0.300000000000D+01 + 0.223417373047D+05 0.103496170044D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 19 45 0.0-0.629974529147D-04-0.000000000000D+00 0.702000000000D+05 + 0.206718212891D+05-0.107761192322D+01 0.931322574616D-09 0.000000000000D+00 + -0.120568569336D+05 0.586327552795D+00 0.279396772385D-08 0.200000000000D+01 + 0.878348486328D+04 0.335016632080D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 19 45 0.0-0.269827432930D-03-0.272848410532D-11 0.702000000000D+05 + 0.137761860352D+05 0.969978332520D+00-0.186264514923D-08 0.000000000000D+00 + -0.185788784180D+05-0.113551425934D+01 0.000000000000D+00 0.400000000000D+01 + -0.107777231445D+05 0.319283199310D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 19 45 0.0-0.157868489623D-03-0.181898940355D-11 0.702000000000D+05 + 0.455192382812D+03 0.220459747315D+01-0.279396772385D-08 0.000000000000D+00 + -0.132452167969D+05-0.201896762848D+01-0.931322574616D-09-0.300000000000D+01 + -0.217793505859D+05 0.128307533264D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 19 45 0.0-0.266684219241D-04-0.909494701773D-12 0.702000000000D+05 + -0.160216025391D+05 0.213105487824D+01-0.186264514923D-08 0.000000000000D+00 + 0.222907373047D+04-0.167150592804D+01-0.186264514923D-08 0.300000000000D+01 + -0.197245205078D+05-0.191949081421D+01-0.000000000000D+00 0.000000000000D+00 +24 22 1 13 19 45 0.0 0.755209475756D-04 0.181898940355D-11 0.706800000000D+05 + -0.205369106445D+05 0.941212654114D+00-0.931322574616D-09 0.000000000000D+00 + 0.128290556641D+05-0.588488578796D+00-0.186264514923D-08 0.200000000000D+01 + -0.797776708984D+04-0.336027812958D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 20 15 0.0 0.717677175999D-05-0.000000000000D+00 0.720000000000D+05 + -0.132402392578D+05-0.734934806824D+00-0.931322574616D-09 0.000000000000D+00 + -0.176433466797D+05-0.159786224365D+01 0.000000000000D+00 0.100000000000D+01 + 0.128164799805D+05-0.295754718780D+01-0.279396772385D-08 0.000000000000D+00 + 2 22 1 13 20 15 0.0 0.506391748786D-03 0.909494701773D-12 0.720000000000D+05 + -0.109114799805D+05-0.103393077850D+01 0.931322574616D-09 0.000000000000D+00 + 0.161923779297D+04-0.296939659119D+01 0.000000000000D+00-0.400000000000D+01 + 0.230351333008D+05-0.287567138672D+00-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 20 15 0.0 0.498881563544D-04 0.909494701773D-12 0.720000000000D+05 + -0.252344482422D+04-0.698530197144D+00 0.279396772385D-08 0.000000000000D+00 + 0.182073295898D+05-0.231951808929D+01 0.000000000000D+00 0.500000000000D+01 + 0.177555668945D+05 0.227891635895D+01-0.000000000000D+00 0.000000000000D+00 + 4 22 1 13 20 15 0.0 0.123372301459D-03 0.181898940355D-11 0.720000000000D+05 + 0.712795019531D+04 0.553865432739D-01 0.279396772385D-08 0.000000000000D+00 + 0.243538422852D+05-0.406140327454D+00-0.000000000000D+00 0.600000000000D+01 + 0.269414160156D+04 0.355555820465D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 20 15 0.0 0.859117135405D-04 0.909494701773D-12 0.720000000000D+05 + 0.127813291016D+05 0.762307167053D+00 0.931322574616D-09 0.000000000000D+00 + 0.171722968750D+05 0.174958896637D+01-0.000000000000D+00 0.100000000000D+01 + -0.138853823242D+05 0.286111545563D+01 0.279396772385D-08 0.000000000000D+00 + 6 22 1 13 20 15 0.0-0.268844887614D-04-0.181898940355D-11 0.731700000000D+05 + 0.106852553711D+05 0.112179756165D+01-0.931322574616D-09 0.000000000000D+00 + -0.299247705078D+04 0.293516159058D+01-0.000000000000D+00-0.400000000000D+01 + -0.229808950195D+05 0.136982917786D+00 0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 20 15 0.0 0.306423753500D-04-0.000000000000D+00 0.720000000000D+05 + 0.212442871094D+04 0.682763099670D+00-0.279396772385D-08 0.000000000000D+00 + -0.186344565430D+05 0.227072715759D+01-0.000000000000D+00 0.500000000000D+01 + -0.172277470703D+05-0.237169265747D+01 0.000000000000D+00 0.000000000000D+00 + 8 22 1 13 20 15 0.0-0.630468130112D-04-0.000000000000D+00 0.720000000000D+05 + -0.727264062500D+04-0.399141311645D-01-0.279396772385D-08 0.000000000000D+00 + -0.243030366211D+05 0.381005287170D+00-0.000000000000D+00 0.600000000000D+01 + -0.247301708984D+04-0.355370235443D+01-0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 20 15 0.0 0.339308753610D-04 0.181898940355D-11 0.720000000000D+05 + 0.236277558594D+05 0.129835224152D+01 0.279396772385D-08 0.000000000000D+00 + -0.780821289062D+03 0.121760368347D+00 0.186264514923D-08-0.200000000000D+01 + 0.950471484375D+04-0.322831916809D+01 0.186264514923D-08 0.000000000000D+00 +10 22 1 13 20 15 0.0-0.788858160376D-04-0.000000000000D+00 0.720000000000D+05 + 0.120974506836D+05 0.283922004700D+01 0.279396772385D-08 0.000000000000D+00 + -0.810154101562D+04 0.419582366943D+00 0.186264514923D-08-0.700000000000D+01 + 0.209529516602D+05-0.146886444092D+01-0.000000000000D+00 0.000000000000D+00 +11 22 1 13 20 15 0.0 0.472720712423D-04-0.909494701773D-12 0.720000000000D+05 + -0.765834277344D+04 0.287745761871D+01 0.000000000000D+00 0.000000000000D+00 + -0.115761762695D+05 0.554757118225D+00 0.000000000000D+00 0.000000000000D+00 + 0.214099013672D+05 0.132876968384D+01-0.279396772385D-08 0.000000000000D+00 +12 22 1 13 20 15 0.0 0.294662080705D-03 0.272848410532D-11 0.729000000000D+05 + -0.227037895508D+05 0.108768367767D+01-0.186264514923D-08 0.000000000000D+00 + -0.814214599609D+04 0.344200134277D+00-0.931322574616D-09-0.100000000000D+01 + 0.828396435547D+04 0.332971858978D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 20 15 0.0-0.170106068254D-04-0.000000000000D+00 0.720000000000D+05 + -0.240365297852D+05-0.117468261719D+01-0.279396772385D-08 0.000000000000D+00 + 0.537319335938D+02-0.564336776733D-01-0.186264514923D-08-0.200000000000D+01 + -0.854872656250D+04 0.331067943573D+01-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 20 15 0.0 0.731740146875D-04 0.000000000000D+00 0.720000000000D+05 + -0.107583632812D+05-0.290102958679D+01-0.186264514923D-08 0.000000000000D+00 + 0.862270849609D+04-0.468589782715D+00-0.186264514923D-08-0.700000000000D+01 + -0.214561674805D+05 0.126500797272D+01 0.000000000000D+00 0.000000000000D+00 +15 22 1 13 20 15 0.0 0.989176332951D-04-0.000000000000D+00 0.720000000000D+05 + 0.825820556641D+04-0.282708549499D+01-0.000000000000D+00 0.000000000000D+00 + 0.117766181641D+05-0.581599235535D+00-0.931322574616D-09 0.000000000000D+00 + -0.210526899414D+05-0.143274593353D+01 0.186264514923D-08 0.000000000000D+00 +17 22 1 13 20 15 0.0 0.492322258651D-03 0.272848410532D-11 0.720300000000D+05 + -0.153759589844D+05-0.128782272339D+00 0.000000000000D+00 0.000000000000D+00 + 0.200613715820D+05 0.485462188721D+00-0.931322574616D-09 0.400000000000D+01 + 0.330501220703D+04-0.356515789032D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 20 15 0.0 0.103742815554D-03 0.909494701773D-12 0.720000000000D+05 + -0.459198681641D+04-0.150865173340D+01 0.186264514923D-08 0.000000000000D+00 + 0.175005332031D+05 0.195599460602D+01-0.000000000000D+00-0.300000000000D+01 + 0.180276772461D+05-0.228078842163D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 20 15 0.0-0.174322165549D-03-0.909494701773D-12 0.720000000000D+05 + 0.780441259766D+04-0.217249774933D+01 0.372529029846D-08 0.000000000000D+00 + 0.680683398438D+04 0.234517765045D+01 0.931322574616D-09 0.300000000000D+01 + 0.233176845703D+05 0.423936843872D-01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 20 15 0.0-0.629993155599D-04-0.000000000000D+00 0.720000000000D+05 + 0.183209697266D+05-0.148928546905D+01 0.186264514923D-08 0.000000000000D+00 + -0.103570170898D+05 0.131080818176D+01 0.186264514923D-08 0.200000000000D+01 + 0.143955947266D+05 0.284502315521D+01 0.000000000000D+00 0.000000000000D+00 +21 22 1 13 20 15 0.0-0.269832089543D-03-0.272848410532D-11 0.720000000000D+05 + 0.148572148438D+05 0.246925354004D+00-0.000000000000D+00 0.000000000000D+00 + -0.202125766602D+05-0.638837814331D+00 0.186264514923D-08 0.400000000000D+01 + -0.468857177734D+04 0.352899074554D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 20 15 0.0-0.157873146236D-03-0.272848410532D-11 0.724500000000D+05 + 0.389009326172D+04 0.159292221069D+01-0.279396772385D-08 0.000000000000D+00 + -0.169150429688D+05-0.201089954376D+01-0.000000000000D+00-0.300000000000D+01 + -0.186561166992D+05 0.216484546661D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 20 15 0.0-0.266702845693D-04-0.909494701773D-12 0.720000000000D+05 + -0.121824584961D+05 0.208770942688D+01-0.279396772385D-08 0.000000000000D+00 + -0.131962158203D+04-0.224964523315D+01-0.186264514923D-08 0.300000000000D+01 + -0.223726362305D+05-0.100376129150D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 20 15 0.0 0.755228102207D-04 0.181898940355D-11 0.720000000000D+05 + -0.184328417969D+05 0.135347652435D+01-0.186264514923D-08 0.000000000000D+00 + 0.111342612305D+05-0.130319976807D+01-0.186264514923D-08 0.200000000000D+01 + -0.136389956055D+05-0.288901710510D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 20 45 0.0 0.717677175999D-05-0.000000000000D+00 0.738000000000D+05 + -0.144635664062D+05-0.581801414490D+00-0.186264514923D-08 0.000000000000D+00 + -0.197933872070D+05-0.785717964172D+00 0.000000000000D+00 0.100000000000D+01 + 0.706636328125D+04-0.338999843597D+01-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 20 45 0.0 0.506394542754D-03 0.909494701773D-12 0.747000000000D+05 + -0.131020986328D+05-0.137226009369D+01 0.000000000000D+00 0.000000000000D+00 + -0.344784228516D+04-0.261747074127D+01 0.000000000000D+00-0.400000000000D+01 + 0.216367910156D+05-0.125632095337D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 20 45 0.0 0.498900189996D-04 0.909494701773D-12 0.738000000000D+05 + -0.426578906250D+04-0.124000930786D+01 0.186264514923D-08 0.000000000000D+00 + 0.137355795898D+05-0.260039424896D+01-0.000000000000D+00 0.500000000000D+01 + 0.211224008789D+05 0.143783283234D+01-0.931322574616D-09 0.000000000000D+00 + 4 22 1 13 20 45 0.0 0.123376026750D-03 0.181898940355D-11 0.738000000000D+05 + 0.685399072266D+04-0.391479492188D+00 0.279396772385D-08 0.000000000000D+00 + 0.229156591797D+05-0.116403961182D+01-0.000000000000D+00 0.600000000000D+01 + 0.890754736328D+04 0.330348587036D+01 0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 20 45 0.0 0.859135761857D-04 0.909494701773D-12 0.738000000000D+05 + 0.141034726562D+05 0.663591384888D+00 0.186264514923D-08 0.000000000000D+00 + 0.195965292969D+05 0.934774398804D+00-0.000000000000D+00 0.100000000000D+01 + -0.826556933594D+04 0.334258842468D+01 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 20 45 0.0-0.268882140517D-04-0.181898940355D-11 0.738000000000D+05 + 0.130322490234D+05 0.145845222473D+01-0.000000000000D+00 0.000000000000D+00 + 0.203441406250D+04 0.260730457306D+01-0.000000000000D+00-0.400000000000D+01 + -0.218505957031D+05 0.111070632935D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 20 45 0.0 0.306423753500D-04-0.000000000000D+00 0.738000000000D+05 + 0.384020019531D+04 0.122745609283D+01-0.186264514923D-08 0.000000000000D+00 + -0.142262822266D+05 0.257837677002D+01 0.000000000000D+00 0.500000000000D+01 + -0.207722011719D+05-0.154093170166D+01 0.931322574616D-09 0.000000000000D+00 + 8 22 1 13 20 45 0.0-0.630477443337D-04-0.000000000000D+00 0.738000000000D+05 + -0.697156445312D+04 0.406070709229D+00-0.279396772385D-08 0.000000000000D+00 + -0.229108173828D+05 0.113870143890D+01 0.000000000000D+00 0.600000000000D+01 + -0.869088330078D+04-0.330997657776D+01-0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 20 45 0.0 0.339336693287D-04 0.181898940355D-11 0.738000000000D+05 + 0.252336679688D+05 0.467618942261D+00 0.279396772385D-08 0.000000000000D+00 + -0.782431640625D+03-0.870733261108D-01 0.931322574616D-09-0.200000000000D+01 + 0.340030224609D+04-0.351018428803D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 20 45 0.0-0.788858160376D-04-0.000000000000D+00 0.738000000000D+05 + 0.168557163086D+05 0.240231132507D+01 0.372529029846D-08 0.000000000000D+00 + -0.774866992188D+04-0.103788375854D-01 0.186264514923D-08-0.700000000000D+01 + 0.175341933594D+05-0.230495262146D+01 0.000000000000D+00 0.000000000000D+00 +11 22 1 13 20 45 0.0 0.472711399198D-04-0.909494701773D-12 0.738000000000D+05 + -0.220120654297D+04 0.313697814941D+01 0.931322574616D-09 0.000000000000D+00 + -0.109417944336D+05 0.135164260864D+00 0.931322574616D-09 0.000000000000D+00 + 0.229439746094D+05 0.364690780640D+00-0.186264514923D-08 0.000000000000D+00 +12 22 1 13 20 45 0.0 0.294667668641D-03 0.272848410532D-11 0.747000000000D+05 + -0.200057202148D+05 0.188920497894D+01-0.931322574616D-09 0.000000000000D+00 + -0.760430712891D+04 0.215147018433D+00 0.000000000000D+00-0.100000000000D+01 + 0.138795727539D+05 0.284730339050D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 20 45 0.0-0.170115381479D-04 0.000000000000D+00 0.762600000000D+05 + -0.253987929688D+05-0.322566032410D+00-0.279396772385D-08 0.000000000000D+00 + 0.162784667969D+03 0.139588356018D+00-0.931322574616D-09-0.200000000000D+01 + -0.233603515625D+04 0.354750919342D+01-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 20 45 0.0 0.731740146875D-04 0.000000000000D+00 0.738000000000D+05 + -0.156775703125D+05-0.251794242859D+01-0.279396772385D-08 0.000000000000D+00 + 0.818521728516D+04-0.318565368652D-01-0.186264514923D-08-0.700000000000D+01 + -0.183789013672D+05 0.213194847107D+01-0.000000000000D+00 0.000000000000D+00 +15 22 1 13 20 45 0.0 0.989167019725D-04-0.000000000000D+00 0.738000000000D+05 + 0.286444042969D+04-0.311791610718D+01-0.931322574616D-09 0.000000000000D+00 + 0.110778828125D+05-0.178086280823D+00-0.931322574616D-09 0.000000000000D+00 + -0.227834257812D+05-0.477766990662D+00 0.186264514923D-08 0.000000000000D+00 +17 22 1 13 20 45 0.0 0.492327846587D-03 0.272848410532D-11 0.753600000000D+05 + -0.150806416016D+05 0.423340797424D+00-0.931322574616D-09 0.000000000000D+00 + 0.203030249023D+05-0.242621421814D+00-0.186264514923D-08 0.400000000000D+01 + -0.315722851562D+04-0.356837177277D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 20 45 0.0 0.103745609522D-03 0.909494701773D-12 0.750300000000D+05 + -0.670350537109D+04-0.840347290039D+00 0.931322574616D-09 0.000000000000D+00 + 0.207632836914D+05 0.162171459198D+01-0.931322574616D-09-0.300000000000D+01 + 0.132809218750D+05-0.295926570892D+01-0.931322574616D-09 0.000000000000D+00 +19 22 1 13 20 45 0.0-0.174324959517D-03-0.909494701773D-12 0.738000000000D+05 + 0.427152832031D+04-0.172376251221D+01 0.279396772385D-08 0.000000000000D+00 + 0.112597817383D+05 0.255783843994D+01 0.000000000000D+00 0.300000000000D+01 + 0.224923701172D+05-0.953473091126D+00-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 20 45 0.0-0.630002468824D-04-0.000000000000D+00 0.738000000000D+05 + 0.154801391602D+05-0.161966419220D+01 0.372529029846D-08 0.000000000000D+00 + -0.734293066406D+04 0.202687931061D+01 0.186264514923D-08 0.200000000000D+01 + 0.188934189453D+05 0.212019634247D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 20 45 0.0-0.269836746156D-03-0.272848410532D-11 0.738000000000D+05 + 0.147521582031D+05-0.332009315491D+00 0.000000000000D+00 0.000000000000D+00 + -0.207517309570D+05 0.681810379028D-01 0.186264514923D-08 0.400000000000D+01 + 0.176267431641D+04 0.359257411957D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 20 45 0.0-0.157878734171D-03-0.272848410532D-11 0.738000000000D+05 + 0.615574121094D+04 0.924803733826D+00-0.186264514923D-08 0.000000000000D+00 + -0.203080009766D+05-0.171036720276D+01 0.931322574616D-09-0.300000000000D+01 + -0.140861464844D+05 0.287997341156D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 20 45 0.0-0.266730785370D-04-0.909494701773D-12 0.738000000000D+05 + -0.865979492188D+04 0.178915786743D+01-0.372529029846D-08 0.000000000000D+00 + -0.576023828125D+04-0.264631557465D+01-0.931322574616D-09 0.300000000000D+01 + -0.232913857422D+05-0.104389190674D-01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 20 45 0.0 0.755256041884D-04 0.181898940355D-11 0.738000000000D+05 + -0.158272924805D+05 0.149580764770D+01-0.279396772385D-08 0.000000000000D+00 + 0.814095556641D+04-0.201208591461D+01-0.186264514923D-08 0.200000000000D+01 + -0.182434550781D+05-0.219372177124D+01-0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 21 15 0.0 0.717677175999D-05-0.000000000000D+00 0.756000000000D+05 + -0.151860361328D+05-0.182354927063D+00-0.279396772385D-08 0.000000000000D+00 + -0.204873085938D+05 0.105476379395D-02 0.000000000000D+00 0.100000000000D+01 + 0.770103027344D+03-0.356043815613D+01-0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 21 15 0.0 0.506394542754D-03 0.909494701773D-12 0.765000000000D+05 + -0.157274443359D+05-0.150476932526D+01 0.000000000000D+00 0.000000000000D+00 + -0.767067529297D+04-0.204499912262D+01 0.000000000000D+00-0.400000000000D+01 + 0.185698095703D+05-0.212958621979D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 21 15 0.0 0.498928129673D-04 0.909494701773D-12 0.756000000000D+05 + -0.696378955078D+04-0.174210262299D+01 0.931322574616D-09 0.000000000000D+00 + 0.902328759766D+04-0.258719062805D+01-0.000000000000D+00 0.500000000000D+01 + 0.228650444336D+05 0.485833168030D+00-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 21 15 0.0 0.123378820717D-03 0.181898940355D-11 0.756000000000D+05 + 0.562881201172D+04-0.987041473389D+00 0.279396772385D-08 0.000000000000D+00 + 0.202892070312D+05-0.171328353882D+01-0.000000000000D+00 0.600000000000D+01 + 0.144334399414D+05 0.279665470123D+01-0.000000000000D+00 0.000000000000D+00 + 5 22 1 13 21 15 0.0 0.859145075083D-04 0.909494701773D-12 0.756000000000D+05 + 0.150165375977D+05 0.310217857361D+00 0.186264514923D-08 0.000000000000D+00 + 0.205395942383D+05 0.123336791992D+00-0.931322574616D-09 0.100000000000D+01 + -0.200741748047D+04 0.356581306457D+01 0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 21 15 0.0-0.268919393420D-04-0.181898940355D-11 0.756000000000D+05 + 0.158128476562D+05 0.159150218964D+01 0.000000000000D+00 0.000000000000D+00 + 0.626137597656D+04 0.205978775024D+01-0.000000000000D+00-0.400000000000D+01 + -0.190340693359D+05 0.199846076965D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 21 15 0.0 0.306423753500D-04-0.000000000000D+00 0.756000000000D+05 + 0.652359423828D+04 0.173941802978D+01-0.186264514923D-08 0.000000000000D+00 + -0.953143017578D+04 0.258906841278D+01 0.000000000000D+00 0.500000000000D+01 + -0.227033369141D+05-0.590875625610D+00 0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 21 15 0.0-0.630496069789D-04-0.000000000000D+00 0.756000000000D+05 + -0.572105517578D+04 0.100059700012D+01-0.279396772385D-08 0.000000000000D+00 + -0.203277563477D+05 0.169082832336D+01 0.000000000000D+00 0.600000000000D+01 + -0.142341264648D+05-0.280892658234D+01-0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 21 15 0.0 0.339364632964D-04 0.181898940355D-11 0.756000000000D+05 + 0.252788017578D+05-0.416793823242D+00 0.279396772385D-08 0.000000000000D+00 + -0.954342773438D+03-0.641450881958D-01 0.931322574616D-09-0.200000000000D+01 + -0.296805517578D+04-0.351961708069D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 21 15 0.0-0.788895413280D-04 0.000000000000D+00 0.756000000000D+05 + 0.206019526367D+05 0.172692108154D+01 0.372529029846D-08 0.000000000000D+00 + -0.805009521484D+04-0.293269157410D+00 0.931322574616D-09-0.700000000000D+01 + 0.127626279297D+05-0.296233463287D+01 0.931322574616D-09 0.000000000000D+00 +11 22 1 13 21 15 0.0 0.472702085972D-04-0.909494701773D-12 0.756000000000D+05 + 0.345053906250D+04 0.309099674225D+01 0.186264514923D-08 0.000000000000D+00 + -0.111115058594D+05-0.320748329163D+00 0.931322574616D-09 0.000000000000D+00 + 0.227057749023D+05-0.627668380737D+00-0.931322574616D-09 0.000000000000D+00 +12 22 1 13 21 15 0.0 0.294673256576D-03 0.363797880709D-11 0.765000000000D+05 + -0.160087509766D+05 0.251479148865D+01 0.000000000000D+00 0.000000000000D+00 + -0.748940234375D+04-0.115623474121D+00 0.000000000000D+00-0.100000000000D+01 + 0.184021274414D+05 0.214523983002D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 21 15 0.0-0.170124694705D-04 0.000000000000D+00 0.756000000000D+05 + -0.251733593750D+05 0.570007324219D+00-0.186264514923D-08 0.000000000000D+00 + 0.412066406250D+03 0.968112945557D-01-0.931322574616D-09-0.200000000000D+01 + 0.405728222656D+04 0.351001453400D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 21 15 0.0 0.731749460101D-04 0.000000000000D+00 0.756000000000D+05 + -0.196719301758D+05-0.188443565369D+01-0.279396772385D-08 0.000000000000D+00 + 0.842818115234D+04 0.272654533386D+00-0.931322574616D-09-0.700000000000D+01 + -0.138804018555D+05 0.283390808106D+01-0.931322574616D-09 0.000000000000D+00 +15 22 1 13 21 15 0.0 0.989157706499D-04-0.909494701773D-12 0.756000000000D+05 + -0.278408544922D+04-0.310661602020D+01-0.186264514923D-08 0.000000000000D+00 + 0.111642705078D+05 0.273082733154D+00-0.931322574616D-09 0.000000000000D+00 + -0.227503295898D+05 0.514316558838D+00 0.931322574616D-09 0.000000000000D+00 +17 22 1 13 21 15 0.0 0.492333434522D-03 0.272848410532D-11 0.756000000000D+05 + -0.139914428711D+05 0.744376182556D+00-0.186264514923D-08 0.000000000000D+00 + 0.191270424805D+05-0.107151317596D+01-0.186264514923D-08 0.400000000000D+01 + -0.937470117188D+04-0.329493904114D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 21 15 0.0 0.103749334812D-03 0.909494701773D-12 0.756000000000D+05 + -0.766107568359D+04-0.244545936584D+00 0.000000000000D+00 0.000000000000D+00 + 0.231789560547D+05 0.102266979218D+01-0.931322574616D-09-0.300000000000D+01 + 0.751139013672D+04-0.340987873077D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 21 15 0.0-0.174321234226D-03-0.909494701773D-12 0.756000000000D+05 + 0.167257812500D+04-0.115274429321D+01 0.279396772385D-08 0.000000000000D+00 + 0.158403833008D+05 0.248091411591D+01-0.000000000000D+00 0.300000000000D+01 + 0.199294174805D+05-0.187580013275D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 21 15 0.0-0.630021095276D-04-0.000000000000D+00 0.756000000000D+05 + 0.126539746094D+05-0.147858047485D+01 0.372529029846D-08 0.000000000000D+00 + -0.313535449219D+04 0.261833095550D+01 0.931322574616D-09 0.200000000000D+01 + 0.219301230469D+05 0.123215484619D+01-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 21 15 0.0-0.269842334092D-03-0.272848410532D-11 0.756000000000D+05 + 0.137963774414D+05-0.688328742981D+00 0.186264514923D-08 0.000000000000D+00 + -0.198951552734D+05 0.894382476807D+00 0.186264514923D-08 0.400000000000D+01 + 0.807784423828D+04 0.337879180908D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 21 15 0.0-0.157884322107D-03-0.272848410532D-11 0.756000000000D+05 + 0.725497167969D+04 0.315509796143D+00-0.000000000000D+00 0.000000000000D+00 + -0.229070195312D+05-0.113581180572D+01 0.931322574616D-09-0.300000000000D+01 + -0.842229785156D+04 0.337220859528D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 21 15 0.0-0.266749411821D-04-0.909494701773D-12 0.756000000000D+05 + -0.584911181641D+04 0.131215572357D+01-0.279396772385D-08 0.000000000000D+00 + -0.106878803711D+05-0.278020763397D+01-0.000000000000D+00 0.300000000000D+01 + -0.224096953125D+05 0.983748435974D+00 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 21 15 0.0 0.755283981562D-04 0.909494701773D-12 0.756000000000D+05 + -0.132061850586D+05 0.137588596344D+01-0.372529029846D-08 0.000000000000D+00 + 0.396265332031D+04-0.260165309906D+01-0.931322574616D-09 0.200000000000D+01 + -0.214340444336D+05-0.132828903198D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 21 45 0.0 0.717677175999D-05-0.000000000000D+00 0.774000000000D+05 + -0.149985000000D+05 0.418402671814D+00-0.279396772385D-08 0.000000000000D+00 + -0.198693813477D+05 0.655685424805D+00 0.931322574616D-09 0.100000000000D+01 + -0.558568457031D+04-0.345571327210D+01-0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 21 45 0.0 0.506396405399D-03 0.909494701773D-12 0.783000000000D+05 + -0.183624375000D+05-0.137736225128D+01-0.931322574616D-09 0.000000000000D+00 + -0.107344746094D+05-0.134747695923D+01 0.000000000000D+00-0.400000000000D+01 + 0.140684331055D+05-0.283967304230D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 21 45 0.0 0.498946756124D-04 0.909494701773D-12 0.774000000000D+05 + -0.104504130859D+05-0.209998416901D+01 0.931322574616D-09 0.000000000000D+00 + 0.458397656250D+04-0.230490970612D+01-0.000000000000D+00 0.500000000000D+01 + 0.228484287109D+05-0.504346847534D+00-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 21 45 0.0 0.123382546008D-03 0.181898940355D-11 0.774000000000D+05 + 0.327045751953D+04-0.163247680664D+01 0.186264514923D-08 0.000000000000D+00 + 0.169094389648D+05-0.199469280243D+01-0.931322574616D-09 0.600000000000D+01 + 0.188459028320D+05 0.207437610626D+01-0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 21 45 0.0 0.859163701534D-04 0.909494701773D-12 0.774000000000D+05 + 0.150888310547D+05-0.260849952698D+00 0.279396772385D-08 0.000000000000D+00 + 0.201098720703D+05-0.573298454285D+00-0.931322574616D-09 0.100000000000D+01 + 0.440573925781D+04 0.351372432709D+01 0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 21 45 0.0-0.268947333098D-04-0.181898940355D-11 0.774000000000D+05 + 0.186064057617D+05 0.146724605560D+01 0.931322574616D-09 0.000000000000D+00 + 0.937391992188D+04 0.138660144806D+01-0.000000000000D+00-0.400000000000D+01 + -0.147489423828D+05 0.273194026947D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 21 45 0.0 0.306414440274D-04-0.000000000000D+00 0.780300000000D+05 + 0.100181635742D+05 0.211216163635D+01-0.931322574616D-09 0.000000000000D+00 + -0.507140136719D+04 0.232492923737D+01 0.000000000000D+00 0.500000000000D+01 + -0.228721914062D+05 0.404338836670D+00 0.186264514923D-08 0.000000000000D+00 + 8 22 1 13 21 45 0.0-0.630505383015D-04-0.000000000000D+00 0.774000000000D+05 + -0.333923876953D+04 0.164496898651D+01-0.279396772385D-08 0.000000000000D+00 + -0.169843149414D+05 0.197698688507D+01 0.931322574616D-09 0.600000000000D+01 + -0.186714399414D+05-0.208916473389D+01 0.000000000000D+00 0.000000000000D+00 + 9 22 1 13 21 45 0.0 0.339392572641D-04 0.181898940355D-11 0.774000000000D+05 + 0.237703251953D+05-0.123969078064D+01 0.186264514923D-08 0.000000000000D+00 + -0.874880859375D+03 0.188105583191D+00 0.000000000000D+00-0.200000000000D+01 + -0.910585156250D+04-0.325566577911D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 21 45 0.0-0.788895413280D-04 0.000000000000D+00 0.774000000000D+05 + 0.229824033203D+05 0.901883125305D+00 0.372529029846D-08 0.000000000000D+00 + -0.867436914062D+04-0.360716819763D+00 0.931322574616D-09-0.700000000000D+01 + 0.700744335938D+04-0.339088630676D+01 0.186264514923D-08 0.000000000000D+00 +11 22 1 13 21 45 0.0 0.472692772746D-04-0.909494701773D-12 0.774000000000D+05 + 0.874653759766D+04 0.274679946899D+01 0.279396772385D-08 0.000000000000D+00 + -0.120536166992D+05-0.705620765686D+00 0.931322574616D-09 0.000000000000D+00 + 0.207134130859D+05-0.157174491882D+01-0.000000000000D+00 0.000000000000D+00 +12 22 1 13 21 45 0.0 0.294678844511D-03 0.363797880709D-11 0.777000000000D+05 + -0.111072329102D+05 0.288377380371D+01 0.000000000000D+00 0.000000000000D+00 + -0.809576855469D+04-0.570950508118D+00 0.000000000000D+00-0.100000000000D+01 + 0.215031210938D+05 0.127807331085D+01-0.279396772385D-08 0.000000000000D+00 +13 22 1 13 21 45 0.0-0.170143321157D-04 0.000000000000D+00 0.776400000000D+05 + -0.233925502930D+05 0.138668537140D+01-0.186264514923D-08 0.000000000000D+00 + 0.371500976562D+03-0.177502632141D+00 0.000000000000D+00-0.200000000000D+01 + 0.101365546875D+05 0.320079421997D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 21 45 0.0 0.731758773327D-04 0.000000000000D+00 0.774000000000D+05 + -0.223606401367D+05-0.108360290527D+01-0.279396772385D-08 0.000000000000D+00 + 0.904250781250D+04 0.371338844299D+00-0.931322574616D-09-0.700000000000D+01 + -0.830870019531D+04 0.331666278839D+01-0.931322574616D-09 0.000000000000D+00 +15 22 1 13 21 45 0.0 0.989157706499D-04-0.000000000000D+00 0.774000000000D+05 + -0.813847509766D+04-0.279535007477D+01-0.279396772385D-08 0.000000000000D+00 + 0.120252578125D+05 0.665057182312D+00-0.931322574616D-09 0.000000000000D+00 + -0.209558085938D+05 0.146663379669D+01 0.000000000000D+00 0.000000000000D+00 +17 22 1 13 21 45 0.0 0.492339022458D-03 0.272848410532D-11 0.774000000000D+05 + -0.125591118164D+05 0.803292274475D+00-0.279396772385D-08 0.000000000000D+00 + 0.164545263672D+05-0.188545322418D+01-0.931322574616D-09 0.400000000000D+01 + -0.148653100586D+05-0.276601314545D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 21 45 0.0 0.103752128780D-03 0.909494701773D-12 0.774000000000D+05 + -0.768611669922D+04 0.181949615478D+00-0.000000000000D+00 0.000000000000D+00 + 0.243260107422D+05 0.226613998413D+00-0.931322574616D-09-0.300000000000D+01 + 0.116329638672D+04-0.359793567658D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 21 45 0.0-0.174324028194D-03-0.909494701773D-12 0.775200000000D+05 + 0.127340332031D+03-0.572159767151D+00 0.931322574616D-09 0.000000000000D+00 + 0.200086337891D+05 0.210154724121D+01-0.931322574616D-09 0.300000000000D+01 + 0.158265732422D+05-0.265336132050D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 21 45 0.0-0.630030408502D-04-0.000000000000D+00 0.774000000000D+05 + 0.102879072266D+05-0.112092685699D+01 0.372529029846D-08 0.000000000000D+00 + 0.194766259766D+04 0.298523235321D+01-0.000000000000D+00 0.200000000000D+01 + 0.232724189453D+05 0.249752998352D+00-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 21 45 0.0-0.269846990705D-03-0.272848410532D-11 0.774000000000D+05 + 0.124314648438D+05-0.784074783325D+00 0.279396772385D-08 0.000000000000D+00 + -0.175285761719D+05 0.172606945038D+01 0.931322574616D-09 0.400000000000D+01 + 0.137694365234D+05 0.290420532227D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 21 45 0.0-0.157888978720D-03-0.272848410532D-11 0.774000000000D+05 + 0.738815771484D+04-0.133878707886D+00 0.000000000000D+00 0.000000000000D+00 + -0.242702250977D+05-0.351306915283D+00 0.186264514923D-08-0.300000000000D+01 + -0.210371972656D+04 0.360259819031D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 21 45 0.0-0.266777351499D-04-0.909494701773D-12 0.787200000000D+05 + -0.398102587891D+04 0.760481834412D+00-0.279396772385D-08 0.000000000000D+00 + -0.155854101562D+05-0.260973834992D+01 0.931322574616D-09 0.300000000000D+01 + -0.197955766602D+05 0.190198898315D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 21 45 0.0 0.755302608013D-04 0.909494701773D-12 0.774000000000D+05 + -0.110011699219D+05 0.104536724091D+01-0.372529029846D-08 0.000000000000D+00 + -0.109375097656D+04-0.297355461121D+01-0.000000000000D+00 0.200000000000D+01 + -0.229633686523D+05-0.359912872314D+00 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 22 15 0.0 0.717584043741D-05-0.000000000000D+00 0.792000000000D+05 + -0.136077773438D+05 0.113836288452D+01-0.279396772385D-08 0.000000000000D+00 + -0.182560839844D+05 0.109619808197D+01 0.931322574616D-09 0.100000000000D+01 + -0.115098178711D+05-0.308393955231D+01-0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 22 15 0.0 0.506398268044D-03 0.000000000000D+00 0.801000000000D+05 + -0.205225156250D+05-0.979130744934D+00-0.186264514923D-08 0.000000000000D+00 + -0.125137446289D+05-0.636805534363D+00 0.931322574616D-09-0.400000000000D+01 + 0.847855371094D+04-0.333104228973D+01-0.279396772385D-08 0.000000000000D+00 + 3 22 1 13 22 15 0.0 0.498974695802D-04 0.909494701773D-12 0.801000000000D+05 + -0.143858784180D+05-0.222929859161D+01 0.000000000000D+00 0.000000000000D+00 + 0.848096679688D+03-0.181950664520D+01 0.000000000000D+00 0.500000000000D+01 + 0.210720566406D+05-0.145684242249D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 22 15 0.0 0.123384408653D-03 0.181898940355D-11 0.792000000000D+05 + -0.210946777344D+03-0.221617984772D+01 0.186264514923D-08 0.000000000000D+00 + 0.132790512695D+05-0.199309730530D+01-0.931322574616D-09 0.600000000000D+01 + 0.218051181641D+05 0.119237995148D+01-0.931322574616D-09 0.000000000000D+00 + 5 22 1 13 22 15 0.0 0.859182327986D-04 0.909494701773D-12 0.792000000000D+05 + 0.139922695312D+05-0.973001480102D+00 0.279396772385D-08 0.000000000000D+00 + 0.185989965820D+05-0.106570434570D+01-0.931322574616D-09 0.100000000000D+01 + 0.104788569336D+05 0.319045257568D+01 0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 22 15 0.0-0.268984586000D-04-0.181898940355D-11 0.792000000000D+05 + 0.209328784180D+05 0.107442855835D+01 0.931322574616D-09 0.000000000000D+00 + 0.112438212891D+05 0.697814941406D+00-0.931322574616D-09-0.400000000000D+01 + -0.932585791016D+04 0.325467586517D+01 0.279396772385D-08 0.000000000000D+00 + 7 22 1 13 22 15 0.0 0.306414440274D-04 0.000000000000D+00 0.792000000000D+05 + 0.139912875977D+05 0.225928020477D+01-0.000000000000D+00 0.000000000000D+00 + -0.128852001953D+04 0.185045719147D+01 0.000000000000D+00 0.500000000000D+01 + -0.212671547852D+05 0.136725330353D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 22 15 0.0-0.630524009466D-04-0.909494701773D-12 0.792000000000D+05 + 0.163483886719D+03 0.222728443146D+01-0.186264514923D-08 0.000000000000D+00 + -0.133809360352D+05 0.198082828522D+01 0.931322574616D-09 0.600000000000D+01 + -0.216572187500D+05-0.120667362213D+01 0.931322574616D-09 0.000000000000D+00 + 9 22 1 13 22 15 0.0 0.339420512319D-04 0.181898940355D-11 0.792000000000D+05 + 0.209164565430D+05-0.189593887329D+01 0.931322574616D-09 0.000000000000D+00 + -0.166158203125D+03 0.624159812927D+00-0.000000000000D+00-0.200000000000D+01 + -0.145363789062D+05-0.273892974853D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 22 15 0.0-0.788895413280D-04 0.000000000000D+00 0.792000000000D+05 + 0.238251171875D+05 0.376234054565D-01 0.372529029846D-08 0.000000000000D+00 + -0.920107177734D+04-0.183668136597D+00 0.000000000000D+00-0.700000000000D+01 + 0.712634765625D+03-0.355811500549D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 22 15 0.0 0.472692772746D-04-0.909494701773D-12 0.792000000000D+05 + 0.131905703125D+05 0.215642356873D+01 0.372529029846D-08 0.000000000000D+00 + -0.135513886719D+05-0.924212455749D+00 0.000000000000D+00 0.000000000000D+00 + 0.171203984375D+05-0.239462375641D+01 0.000000000000D+00 0.000000000000D+00 +12 22 1 13 22 15 0.0 0.294685363770D-03 0.363797880709D-11 0.792000000000D+05 + -0.580697802734D+04 0.295461559296D+01 0.186264514923D-08 0.000000000000D+00 + -0.955921044922D+04-0.105003833771D+01 0.000000000000D+00-0.100000000000D+01 + 0.229442177734D+05 0.312865257263D+00-0.186264514923D-08 0.000000000000D+00 +13 22 1 13 22 15 0.0-0.170152634382D-04 0.000000000000D+00 0.801000000000D+05 + -0.202899658203D+05 0.202322006226D+01-0.931322574616D-09 0.000000000000D+00 + -0.336298339844D+03-0.632893562317D+00 0.000000000000D+00-0.200000000000D+01 + 0.154309584961D+05 0.264358615875D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 22 15 0.0 0.731768086553D-04 0.000000000000D+00 0.799500000000D+05 + -0.235369648438D+05-0.223125457764D+00-0.279396772385D-08 0.000000000000D+00 + 0.961904833984D+04 0.228266716003D+00 0.000000000000D+00-0.700000000000D+01 + -0.209469189453D+04 0.354296207428D+01-0.186264514923D-08 0.000000000000D+00 +15 22 1 13 22 15 0.0 0.989148393273D-04-0.000000000000D+00 0.792000000000D+05 + -0.126949877930D+05-0.223142814636D+01-0.372529029846D-08 0.000000000000D+00 + 0.134643789062D+05 0.901116371155D+00-0.000000000000D+00 0.000000000000D+00 + -0.175387724609D+05 0.230537700653D+01-0.000000000000D+00 0.000000000000D+00 +17 22 1 13 22 15 0.0 0.492344610393D-03 0.272848410532D-11 0.792000000000D+05 + -0.112483574219D+05 0.615744590759D+00-0.372529029846D-08 0.000000000000D+00 + 0.124188505859D+05-0.256760883331D+01-0.000000000000D+00 0.400000000000D+01 + -0.192034018555D+05-0.202273845673D+01-0.000000000000D+00 0.000000000000D+00 +18 22 1 13 22 15 0.0 0.103754922748D-03 0.909494701773D-12 0.792000000000D+05 + -0.714712500000D+04 0.374793052673D+00-0.186264514923D-08 0.000000000000D+00 + 0.239367792969D+05-0.665500640869D+00-0.931322574616D-09-0.300000000000D+01 + -0.527439843750D+04-0.350873756409D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 22 15 0.0-0.174325890839D-03-0.909494701773D-12 0.792000000000D+05 + -0.448118652344D+03-0.923986434936D-01 0.000000000000D+00 0.000000000000D+00 + 0.232445620117D+05 0.145464611053D+01-0.931322574616D-09 0.300000000000D+01 + 0.105005727539D+05-0.322602748871D+01-0.931322574616D-09 0.000000000000D+00 +20 22 1 13 22 15 0.0-0.630030408502D-04-0.000000000000D+00 0.792000000000D+05 + 0.869495654297D+04-0.636736869812D+00 0.279396772385D-08 0.000000000000D+00 + 0.743557519531D+04 0.306044769287D+01-0.931322574616D-09 0.200000000000D+01 + 0.228181088867D+05-0.751151084900D+00-0.000000000000D+00 0.000000000000D+00 +21 22 1 13 22 15 0.0-0.269852578640D-03-0.272848410532D-11 0.792000000000D+05 + 0.111259233398D+05-0.627638816833D+00 0.372529029846D-08 0.000000000000D+00 + -0.137490678711D+05 0.244532299042D+01 0.000000000000D+00 0.400000000000D+01 + 0.183981210938D+05 0.220542907715D+01 0.000000000000D+00 0.000000000000D+00 +22 22 1 13 22 15 0.0-0.157894566655D-03-0.272848410532D-11 0.792000000000D+05 + 0.691174169922D+04-0.353507995606D+00 0.186264514923D-08 0.000000000000D+00 + -0.241052226562D+05 0.543366432190D+00 0.931322574616D-09-0.300000000000D+01 + 0.437848486328D+04 0.355278587341D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 22 15 0.0-0.266795977950D-04-0.909494701773D-12 0.792000000000D+05 + -0.308813671875D+04 0.247202873230D+00-0.186264514923D-08 0.000000000000D+00 + -0.199026533203D+05-0.214038085938D+01 0.931322574616D-09 0.300000000000D+01 + -0.156509277344D+05 0.267329025269D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 22 15 0.0 0.755330547690D-04 0.181898940355D-11 0.792000000000D+05 + -0.951783007812D+04 0.590571403503D+00-0.279396772385D-08 0.000000000000D+00 + -0.657059326172D+04-0.306099510193D+01 0.000000000000D+00 0.200000000000D+01 + -0.227130468750D+05 0.636198997498D+00 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 22 45 0.0 0.717584043741D-05-0.000000000000D+00 0.810000000000D+05 + -0.108923217773D+05 0.187155342102D+01-0.186264514923D-08 0.000000000000D+00 + -0.160789414062D+05 0.127843570709D+01 0.931322574616D-09 0.100000000000D+01 + -0.165445131836D+05-0.247386455536D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 22 45 0.0 0.506400130689D-03 0.000000000000D+00 0.819000000000D+05 + -0.217450029297D+05-0.345119476318D+00-0.186264514923D-08 0.000000000000D+00 + -0.130854223633D+05-0.228586196899D-01 0.931322574616D-09-0.400000000000D+01 + 0.223164843750D+04-0.356480312347D+01-0.186264514923D-08 0.000000000000D+00 + 3 22 1 13 22 45 0.0 0.498984009027D-04 0.000000000000D+00 0.819000000000D+05 + -0.183082187500D+05-0.208065509796D+01 0.000000000000D+00 0.000000000000D+00 + -0.190156250000D+04-0.122676563263D+01 0.000000000000D+00 0.500000000000D+01 + 0.176705800781D+05-0.229830169678D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 22 45 0.0 0.123388133943D-03 0.181898940355D-11 0.810000000000D+05 + -0.460674951172D+04-0.263227844238D+01 0.931322574616D-09 0.000000000000D+00 + 0.988656005859D+04-0.173908042908D+01-0.000000000000D+00 0.600000000000D+01 + 0.230832119141D+05 0.218522071838D+00-0.186264514923D-08 0.000000000000D+00 + 5 22 1 13 22 45 0.0 0.859191641211D-04 0.909494701773D-12 0.810000000000D+05 + 0.115635283203D+05-0.172229385376D+01 0.186264514923D-08 0.000000000000D+00 + 0.164287036133D+05-0.130067348480D+01-0.186264514923D-08 0.100000000000D+01 + 0.157432631836D+05 0.262097644806D+01-0.000000000000D+00 0.000000000000D+00 + 6 22 1 13 22 45 0.0-0.269021838903D-04-0.181898940355D-11 0.810000000000D+05 + 0.223326391602D+05 0.446793556213D+00 0.186264514923D-08 0.000000000000D+00 + 0.119419067383D+05 0.101446151733D+00-0.931322574616D-09-0.400000000000D+01 + -0.318314355469D+04 0.352633857727D+01 0.186264514923D-08 0.000000000000D+00 + 7 22 1 13 22 45 0.0 0.306423753500D-04 0.000000000000D+00 0.810000000000D+05 + 0.179844433594D+05 0.212934684753D+01 0.000000000000D+00 0.000000000000D+00 + 0.152106542969D+04 0.126118755341D+01-0.000000000000D+00 0.500000000000D+01 + -0.180144243164D+05 0.222321796417D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 22 45 0.0-0.630542635918D-04-0.909494701773D-12 0.810000000000D+05 + 0.457752099609D+04 0.264130020142D+01-0.931322574616D-09 0.000000000000D+00 + -0.100056469727D+05 0.173219299316D+01 0.931322574616D-09 0.600000000000D+01 + -0.229590258789D+05-0.230367660522D+00 0.186264514923D-08 0.000000000000D+00 + 9 22 1 13 22 45 0.0 0.339448451996D-04 0.181898940355D-11 0.810000000000D+05 + 0.170926064453D+05-0.230742454529D+01-0.000000000000D+00 0.000000000000D+00 + 0.143429101562D+04 0.116313934326D+01-0.000000000000D+00-0.200000000000D+01 + -0.188382797852D+05-0.200988864899D+01 0.279396772385D-08 0.000000000000D+00 +10 22 1 13 22 45 0.0-0.788886100054D-04 0.000000000000D+00 0.810000000000D+05 + 0.231632236328D+05-0.751492500305D+00 0.186264514923D-08 0.000000000000D+00 + -0.919721240234D+04 0.222553253174D+00-0.000000000000D+00-0.700000000000D+01 + -0.563704101562D+04-0.345149421692D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 22 45 0.0 0.472683459520D-04-0.909494701773D-12 0.810000000000D+05 + 0.164145122070D+05 0.140820407867D+01 0.372529029846D-08 0.000000000000D+00 + -0.152402026367D+05-0.909877777100D+00 0.000000000000D+00 0.000000000000D+00 + 0.122039711914D+05-0.303264045715D+01 0.931322574616D-09 0.000000000000D+00 +12 22 1 13 22 45 0.0 0.294690951705D-03 0.363797880709D-11 0.810000000000D+05 + -0.648055664062D+03 0.273123741150D+01 0.279396772385D-08 0.000000000000D+00 + -0.118256181641D+05-0.144571876526D+01 0.000000000000D+00-0.100000000000D+01 + 0.226152109375D+05-0.675994873047D+00-0.931322574616D-09 0.000000000000D+00 +13 22 1 13 22 45 0.0-0.170161947608D-04 0.000000000000D+00 0.819000000000D+05 + -0.162631406250D+05 0.240417098999D+01 0.000000000000D+00 0.000000000000D+00 + -0.196485498047D+04-0.118415737152D+01 0.000000000000D+00-0.200000000000D+01 + 0.195301943359D+05 0.188146591186D+01-0.279396772385D-08 0.000000000000D+00 +14 22 1 13 22 45 0.0 0.731777399778D-04 0.000000000000D+00 0.817200000000D+05 + -0.231972988281D+05 0.582056045532D+00-0.279396772385D-08 0.000000000000D+00 + 0.972320214844D+04-0.148451805115D+00 0.000000000000D+00-0.700000000000D+01 + 0.428122412109D+04 0.349540901184D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 22 45 0.0 0.989139080048D-04-0.000000000000D+00 0.810000000000D+05 + -0.160703618164D+05-0.149954605103D+01-0.372529029846D-08 0.000000000000D+00 + 0.151330458984D+05 0.911457061768D+00-0.000000000000D+00 0.000000000000D+00 + -0.127638735352D+05 0.296555328369D+01-0.931322574616D-09 0.000000000000D+00 +17 22 1 13 22 45 0.0 0.492350198329D-03 0.272848410532D-11 0.810000000000D+05 + -0.104554819336D+05 0.240902900696D+00-0.372529029846D-08 0.000000000000D+00 + 0.735017871094D+04-0.301918697357D+01 0.000000000000D+00 0.400000000000D+01 + -0.220530297852D+05-0.112297439575D+01 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 22 45 0.0 0.103757716715D-03 0.909494701773D-12 0.810000000000D+05 + -0.649082373047D+04 0.312610626221D+00-0.279396772385D-08 0.000000000000D+00 + 0.219435351562D+05-0.153545665741D+01-0.000000000000D+00-0.300000000000D+01 + -0.113052768555D+05-0.314876842499D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 22 45 0.0-0.174328684807D-03-0.909494701773D-12 0.810000000000D+05 + -0.320200683594D+03 0.197079658508D+00-0.000000000000D+00 0.000000000000D+00 + 0.251307495117D+05 0.617764472961D+00-0.931322574616D-09 0.300000000000D+01 + 0.436282910156D+04-0.354941940308D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 22 45 0.0-0.630086287856D-04-0.000000000000D+00 0.817500000000D+05 + 0.800587255859D+04-0.135540008545D+00 0.186264514923D-08 0.000000000000D+00 + 0.127753598633D+05 0.282076644898D+01-0.931322574616D-09 0.200000000000D+01 + 0.206034873047D+05-0.169352149963D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 22 45 0.0-0.269857235253D-03-0.272848410532D-11 0.810000000000D+05 + 0.102923041992D+05-0.271831512451D+00 0.372529029846D-08 0.000000000000D+00 + -0.885535644531D+04 0.294907665253D+01-0.000000000000D+00 0.400000000000D+01 + 0.216065029297D+05 0.133629894257D+01-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 22 45 0.0-0.157900154591D-03-0.272848410532D-11 0.810000000000D+05 + 0.627049169922D+04-0.316473960876D+00 0.279396772385D-08 0.000000000000D+00 + -0.223196586914D+05 0.142877960205D+01 0.931322574616D-09-0.300000000000D+01 + 0.105200532227D+05 0.322661781311D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 22 45 0.0-0.266823917627D-04-0.909494701773D-12 0.810000000000D+05 + -0.300561621094D+04-0.124543190002D+00-0.000000000000D+00 0.000000000000D+00 + -0.231419531250D+05-0.142428970337D+01 0.186264514923D-08 0.300000000000D+01 + -0.102960288086D+05 0.323796749115D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 22 45 0.0 0.755358487368D-04 0.181898940355D-11 0.810000000000D+05 + -0.888616796875D+04 0.117393493652D+00-0.279396772385D-08 0.000000000000D+00 + -0.119272856445D+05-0.283995532989D+01 0.931322574616D-09 0.200000000000D+01 + -0.207028818359D+05 0.158272838593D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 23 15 0.0 0.717584043741D-05-0.000000000000D+00 0.828000000000D+05 + -0.693001318359D+04 0.250543403626D+01-0.186264514923D-08 0.000000000000D+00 + -0.138093647461D+05 0.120274734497D+01 0.186264514923D-08 0.100000000000D+01 + -0.203007319336D+05-0.167262458801D+01 0.931322574616D-09 0.000000000000D+00 + 2 22 1 13 23 15 0.0 0.506401062012D-03 0.000000000000D+00 0.837000000000D+05 + -0.216685263672D+05 0.448600769043D+00-0.186264514923D-08 0.000000000000D+00 + -0.127085849609D+05 0.404702186585D+00 0.186264514923D-08-0.400000000000D+01 + -0.418836962891D+04-0.352201366425D+01-0.931322574616D-09 0.000000000000D+00 + 3 22 1 13 23 15 0.0 0.499002635479D-04 0.000000000000D+00 0.837000000000D+05 + -0.217052236328D+05-0.164854335785D+01-0.931322574616D-09 0.000000000000D+00 + -0.356970947266D+04-0.636287689209D+00 0.931322574616D-09 0.500000000000D+01 + 0.129042226562D+05-0.296345233917D+01-0.279396772385D-08 0.000000000000D+00 + 4 22 1 13 23 15 0.0 0.123391859233D-03 0.181898940355D-11 0.828000000000D+05 + -0.953632080078D+04-0.279826545715D+01 0.000000000000D+00 0.000000000000D+00 + 0.712802294922D+04-0.130324268341D+01-0.000000000000D+00 0.600000000000D+01 + 0.225815561523D+05-0.772357940674D+00-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 23 15 0.0 0.859210267663D-04 0.909494701773D-12 0.828000000000D+05 + 0.783852783203D+04-0.239434242248D+01 0.186264514923D-08 0.000000000000D+00 + 0.140772480469D+05-0.126938533783D+01-0.186264514923D-08 0.100000000000D+01 + 0.197926630859D+05 0.184917545319D+01-0.931322574616D-09 0.000000000000D+00 + 6 22 1 13 23 15 0.0-0.269059091806D-04-0.181898940355D-11 0.828000000000D+05 + 0.224441865234D+05-0.341782569885D+00 0.186264514923D-08 0.000000000000D+00 + 0.117181210938D+05-0.314261436462D+00-0.186264514923D-08-0.400000000000D+01 + 0.320524853516D+04 0.352585411072D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 23 15 0.0 0.306423753500D-04 0.000000000000D+00 0.828000000000D+05 + 0.214853291016D+05 0.171495723724D+01 0.000000000000D+00 0.000000000000D+00 + 0.324907763672D+04 0.667321205139D+00-0.931322574616D-09 0.500000000000D+01 + -0.133676333008D+05 0.290620708466D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 23 15 0.0-0.630561262369D-04-0.000000000000D+00 0.828000000000D+05 + 0.952089648438D+04 0.280449008942D+01-0.931322574616D-09 0.000000000000D+00 + -0.725488183594D+04 0.130144691467D+01 0.000000000000D+00 0.600000000000D+01 + -0.224760122070D+05 0.763446807861D+00 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 23 15 0.0 0.339476391673D-04 0.181898940355D-11 0.828000000000D+05 + 0.127807441406D+05-0.243541812897D+01-0.931322574616D-09 0.000000000000D+00 + 0.402150292969D+04 0.170257759094D+01-0.000000000000D+00-0.200000000000D+01 + -0.216785849609D+05-0.112559509277D+01 0.186264514923D-08 0.000000000000D+00 +10 22 1 13 23 15 0.0-0.788886100054D-04 0.000000000000D+00 0.828000000000D+05 + 0.212255200195D+05-0.136533737183D+01 0.931322574616D-09 0.000000000000D+00 + -0.829614648438D+04 0.800546646118D+00-0.000000000000D+00-0.700000000000D+01 + -0.115530068359D+05-0.307932186127D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 23 15 0.0 0.472674146295D-04-0.909494701773D-12 0.828000000000D+05 + 0.182314609375D+05 0.612377166748D+00 0.372529029846D-08 0.000000000000D+00 + -0.166704882812D+05-0.636319160461D+00-0.000000000000D+00 0.000000000000D+00 + 0.634386132812D+04-0.343632125855D+01 0.186264514923D-08 0.000000000000D+00 +12 22 1 13 23 15 0.0 0.294697470963D-03 0.363797880709D-11 0.828000000000D+05 + 0.387718750000D+04 0.226193904877D+01 0.372529029846D-08 0.000000000000D+00 + -0.146560268555D+05-0.166274356842D+01-0.000000000000D+00-0.100000000000D+01 + 0.205421572266D+05-0.161245727539D+01-0.000000000000D+00 0.000000000000D+00 +13 22 1 13 23 15 0.0-0.170180574060D-04-0.000000000000D+00 0.844800000000D+05 + -0.118100551758D+05 0.249484443665D+01 0.931322574616D-09 0.000000000000D+00 + -0.459349365234D+04-0.172571754456D+01 0.000000000000D+00-0.200000000000D+01 + 0.221164956055D+05 0.973497390747D+00-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 23 15 0.0 0.731786713004D-04 0.000000000000D+00 0.837600000000D+05 + -0.215375415039D+05 0.122821998596D+01-0.186264514923D-08 0.000000000000D+00 + 0.897439599609D+04-0.707697868347D+00 0.000000000000D+00-0.700000000000D+01 + 0.103263168945D+05 0.317778396606D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 23 15 0.0 0.989129766822D-04-0.000000000000D+00 0.828000000000D+05 + -0.180573730469D+05-0.707890510559D+00-0.279396772385D-08 0.000000000000D+00 + 0.165909116211D+05 0.665472984314D+00 0.000000000000D+00 0.000000000000D+00 + -0.700094433594D+04 0.339605903626D+01-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 23 15 0.0 0.492355786264D-03 0.272848410532D-11 0.828000000000D+05 + -0.104385361328D+05-0.229077339172D+00-0.372529029846D-08 0.000000000000D+00 + 0.172780517578D+04-0.317556953430D+01 0.931322574616D-09 0.400000000000D+01 + -0.231940659180D+05-0.136691093445D+00 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 23 15 0.0 0.103760510683D-03 0.909494701773D-12 0.828000000000D+05 + -0.616060058594D+04 0.204038619995D-01-0.279396772385D-08 0.000000000000D+00 + 0.184928251953D+05-0.226661777496D+01 0.000000000000D+00-0.300000000000D+01 + -0.164634926758D+05-0.254531478882D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 23 15 0.0-0.174331478775D-03-0.909494701773D-12 0.828000000000D+05 + 0.114207031250D+03 0.242823600769D+00-0.186264514923D-08 0.000000000000D+00 + 0.254190673828D+05-0.301079750061D+00-0.931322574616D-09 0.300000000000D+01 + -0.211225634766D+04-0.359839439392D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 23 15 0.0-0.630086287856D-04-0.000000000000D+00 0.828000000000D+05 + 0.815067675781D+04 0.272379875183D+00 0.931322574616D-09 0.000000000000D+00 + 0.174153090820D+05 0.229087352753D+01-0.186264514923D-08 0.200000000000D+01 + 0.168001884766D+05-0.250496864319D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 23 15 0.0-0.269861891866D-03-0.272848410532D-11 0.828000000000D+05 + 0.102146093750D+05 0.195427894592D+00 0.372529029846D-08 0.000000000000D+00 + -0.330549121094D+04 0.316579818726D+01-0.931322574616D-09 0.400000000000D+01 + 0.231465898438D+05 0.363759040832D+00-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 23 15 0.0-0.157905742526D-03-0.272848410532D-11 0.828000000000D+05 + 0.591509521484D+04-0.431518554688D-01 0.372529029846D-08 0.000000000000D+00 + -0.190393300781D+05 0.218532752991D+01-0.000000000000D+00-0.300000000000D+01 + 0.158435439453D+05 0.264983940124D+01 0.000000000000D+00 0.000000000000D+00 +23 22 1 13 23 15 0.0-0.266851857305D-04-0.909494701773D-12 0.828000000000D+05 + -0.340532861328D+04-0.279055595398D+00 0.000000000000D+00 0.000000000000D+00 + -0.249360273438D+05-0.552239418030D+00 0.931322574616D-09 0.300000000000D+01 + -0.414485742188D+04 0.355227756500D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 23 15 0.0 0.755386427045D-04 0.181898940355D-11 0.845100000000D+05 + -0.904160205078D+04-0.267019271851D+00-0.186264514923D-08 0.000000000000D+00 + -0.166222226562D+05-0.233337593079D+01 0.186264514923D-08 0.200000000000D+01 + -0.170891962891D+05 0.240629863739D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 23 45 0.0 0.717584043741D-05-0.000000000000D+00 0.846000000000D+05 + -0.199332275391D+04 0.293934249878D+01-0.931322574616D-09 0.000000000000D+00 + -0.118780625000D+05 0.913256645203D+00 0.931322574616D-09 0.100000000000D+01 + -0.224881840820D+05-0.742092132568D+00 0.186264514923D-08 0.000000000000D+00 + 2 22 1 13 23 45 0.0 0.506401993334D-03 0.000000000000D+00 0.846000000000D+05 + -0.200970668945D+05 0.129686260223D+01-0.186264514923D-08 0.000000000000D+00 + -0.117739833984D+05 0.591281890869D+00 0.186264514923D-08-0.400000000000D+01 + -0.102828842773D+05-0.320542526245D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 23 45 0.0 0.499011948705D-04 0.000000000000D+00 0.855000000000D+05 + -0.240961850586D+05-0.973142623901D+00-0.931322574616D-09 0.000000000000D+00 + -0.425706347656D+04-0.153166770935D+00 0.186264514923D-08 0.500000000000D+01 + 0.713953857422D+04-0.340021419525D+01-0.186264514923D-08 0.000000000000D+00 + 4 22 1 13 23 45 0.0 0.123394653201D-03 0.181898940355D-11 0.855000000000D+05 + -0.145023706055D+05-0.266889572144D+01 0.000000000000D+00 0.000000000000D+00 + 0.524518750000D+04-0.783946037293D+00 0.000000000000D+00 0.600000000000D+01 + 0.203383149414D+05-0.170406818390D+01-0.279396772385D-08 0.000000000000D+00 + 5 22 1 13 23 45 0.0 0.859228894115D-04 0.909494701773D-12 0.846000000000D+05 + 0.305444384766D+04-0.288301753998D+01 0.931322574616D-09 0.000000000000D+00 + 0.119976386719D+05-0.100820350647D+01-0.931322574616D-09 0.100000000000D+01 + 0.223143261719D+05 0.934464454651D+00-0.186264514923D-08 0.000000000000D+00 + 6 22 1 13 23 45 0.0-0.269087031484D-04-0.181898940355D-11 0.846000000000D+05 + 0.210668906250D+05-0.118882656097D+01 0.186264514923D-08 0.000000000000D+00 + 0.109522885742D+05-0.495222091675D+00-0.186264514923D-08-0.400000000000D+01 + 0.934612207031D+04 0.325306510925D+01 0.931322574616D-09 0.000000000000D+00 + 7 22 1 13 23 45 0.0 0.306433066726D-04 0.000000000000D+00 0.846000000000D+05 + 0.240097934570D+05 0.105443286896D+01 0.931322574616D-09 0.000000000000D+00 + 0.398485058594D+04 0.175098419190D+00-0.186264514923D-08 0.500000000000D+01 + -0.768762597656D+04 0.336388015747D+01 0.279396772385D-08 0.000000000000D+00 + 8 22 1 13 23 45 0.0-0.630570575595D-04-0.000000000000D+00 0.846900000000D+05 + 0.144954443359D+05 0.267216682434D+01-0.000000000000D+00 0.000000000000D+00 + -0.537087988281D+04 0.787002563477D+00-0.000000000000D+00 0.600000000000D+01 + -0.202467485352D+05 0.169720935822D+01 0.279396772385D-08 0.000000000000D+00 + 9 22 1 13 23 45 0.0 0.339504331350D-04 0.181898940355D-11 0.846000000000D+05 + 0.849225341797D+04-0.228629779816D+01-0.186264514923D-08 0.000000000000D+00 + 0.749960009766D+04 0.213587665558D+01 0.000000000000D+00-0.200000000000D+01 + -0.228385361328D+05-0.155039787293D+00 0.931322574616D-09 0.000000000000D+00 +10 22 1 13 23 45 0.0-0.788876786828D-04 0.000000000000D+00 0.846000000000D+05 + 0.183960703125D+05-0.173380470276D+01 0.000000000000D+00 0.000000000000D+00 + -0.626595117188D+04 0.146008014679D+01-0.000000000000D+00-0.700000000000D+01 + -0.165799809570D+05-0.247005748749D+01 0.279396772385D-08 0.000000000000D+00 +11 22 1 13 23 45 0.0 0.472655519843D-04-0.909494701773D-12 0.846000000000D+05 + 0.186593110352D+05-0.116881370544D+00 0.279396772385D-08 0.000000000000D+00 + -0.173859941406D+05-0.122624397278D+00-0.931322574616D-09 0.000000000000D+00 + -0.693017578125D+01-0.357427215576D+01 0.279396772385D-08 0.000000000000D+00 +12 22 1 13 23 45 0.0 0.294703990221D-03 0.363797880709D-11 0.846000000000D+05 + 0.739756787109D+04 0.163113117218D+01 0.372529029846D-08 0.000000000000D+00 + -0.176632866211D+05-0.163411712647D+01-0.931322574616D-09-0.100000000000D+01 + 0.168851811523D+05-0.242454814911D+01 0.000000000000D+00 0.000000000000D+00 +13 22 1 13 23 45 0.0-0.170189887285D-04-0.000000000000D+00 0.846000000000D+05 + -0.745002392578D+04 0.230645942688D+01 0.186264514923D-08 0.000000000000D+00 + -0.810645019531D+04-0.214950561523D+01-0.000000000000D+00-0.200000000000D+01 + 0.229894194336D+05-0.988197326660D-02-0.186264514923D-08 0.000000000000D+00 +14 22 1 13 23 45 0.0 0.731796026230D-04 0.000000000000D+00 0.855000000000D+05 + -0.189174960938D+05 0.163941764832D+01-0.931322574616D-09 0.000000000000D+00 + 0.711707373047D+04-0.136360359192D+01 0.000000000000D+00-0.700000000000D+01 + 0.155736015625D+05 0.261471939087D+01-0.279396772385D-08 0.000000000000D+00 +15 22 1 13 23 45 0.0 0.989120453596D-04 0.000000000000D+00 0.846300000000D+05 + -0.186515454102D+05 0.295724868774D-01-0.279396772385D-08 0.000000000000D+00 + 0.173826542969D+05 0.177465438843D+00 0.931322574616D-09 0.000000000000D+00 + -0.696217285156D+03 0.356366157532D+01-0.186264514923D-08 0.000000000000D+00 +17 22 1 13 23 45 0.0 0.492361374199D-03 0.272848410532D-11 0.846000000000D+05 + -0.112717519531D+05-0.685109138489D+00-0.279396772385D-08 0.000000000000D+00 + -0.389179980469D+04-0.301678943634D+01 0.186264514923D-08 0.400000000000D+01 + -0.225391245117D+05 0.859562873840D+00 0.000000000000D+00 0.000000000000D+00 +18 22 1 13 23 45 0.0 0.103762373328D-03 0.909494701773D-12 0.846000000000D+05 + -0.651577978516D+04-0.434775352478D+00-0.372529029846D-08 0.000000000000D+00 + 0.139255268555D+05-0.276263618469D+01 0.931322574616D-09-0.300000000000D+01 + -0.203496831055D+05-0.174450492859D+01-0.000000000000D+00 0.000000000000D+00 +19 22 1 13 23 45 0.0-0.174334272742D-03-0.909494701773D-12 0.846000000000D+05 + 0.401723144531D+03 0.364151000977D-01-0.279396772385D-08 0.000000000000D+00 + 0.240705541992D+05-0.118065261841D+01-0.000000000000D+00 0.300000000000D+01 + -0.842396240234D+04-0.336904907227D+01-0.000000000000D+00 0.000000000000D+00 +20 22 1 13 23 45 0.0-0.630095601082D-04-0.000000000000D+00 0.846000000000D+05 + 0.887443212891D+04 0.494763374329D+00 0.000000000000D+00 0.000000000000D+00 + 0.208889633789D+05 0.153953647614D+01-0.186264514923D-08 0.200000000000D+01 + 0.117018251953D+05-0.312320327759D+01-0.931322574616D-09 0.000000000000D+00 +21 22 1 13 23 45 0.0-0.269867479801D-03-0.272848410532D-11 0.846000000000D+05 + 0.109980234375D+05 0.666266441345D+00 0.279396772385D-08 0.000000000000D+00 + 0.235127539062D+04 0.306707096100D+01-0.186264514923D-08 0.400000000000D+01 + 0.228989121094D+05-0.637244224548D+00-0.000000000000D+00 0.000000000000D+00 +22 22 1 13 23 45 0.0-0.157911330461D-03-0.272848410532D-11 0.846000000000D+05 + 0.621925976562D+04 0.402599334717D+00 0.372529029846D-08 0.000000000000D+00 + -0.145906899414D+05 0.271273612976D+01-0.931322574616D-09-0.300000000000D+01 + 0.199360747070D+05 0.186792945862D+01-0.000000000000D+00 0.000000000000D+00 +23 22 1 13 23 45 0.0-0.266870483756D-04-0.909494701773D-12 0.846000000000D+05 + -0.385770751953D+04-0.180966377258D+00 0.186264514923D-08 0.000000000000D+00 + -0.251052949219D+05 0.360816955566D+00 0.931322574616D-09 0.300000000000D+01 + 0.232689599609D+04 0.359184837341D+01 0.000000000000D+00 0.000000000000D+00 +24 22 1 13 23 45 0.0 0.755414366722D-04 0.181898940355D-11 0.720000000000D+03 + -0.973954687500D+04-0.472729682922D+00 0.000000000000D+00 0.000000000000D+00 + -0.201952500000D+05-0.160759258270D+01 0.186264514923D-08 0.200000000000D+01 + -0.121525019531D+05 0.304320144653D+01 0.000000000000D+00 0.000000000000D+00 diff --git a/src/test/resources/gnss/navigation/brdc0130.22n b/src/test/resources/gnss/navigation/brdc0130.22n new file mode 100644 index 0000000000..2b0abb58da --- /dev/null +++ b/src/test/resources/gnss/navigation/brdc0130.22n @@ -0,0 +1,80 @@ + 2 NAVIGATION DATA RINEX VERSION / TYPE +CCRINEXN V1.6.0 UX CDDIS 14-JAN-22 18:31 PGM / RUN BY / DATE +IGS BROADCAST EPHEMERIS FILE COMMENT + 0.1118D-07 -0.7451D-08 -0.5960D-07 0.1192D-06 ION ALPHA + 0.1147D+06 -0.1638D+06 -0.1966D+06 0.9175D+06 ION BETA + 0.279396772385D-08 0.888178419700D-14 589824 2192 DELTA-UTC: A0,A1,T,W + 18 LEAP SECONDS + END OF HEADER + 1 22 1 13 0 0 0.0 0.458852853626D-03-0.989075488178D-11 0.000000000000D+00 + 0.220000000000D+02-0.941250000000D+02 0.396730811145D-08-0.193409916688D+00 + -0.483170151711D-05 0.112517168745D-01 0.636465847492D-05 0.515367410850D+04 + 0.345600000000D+06-0.186264514923D-08-0.128576744812D+01 0.135973095894D-06 + 0.986447989815D+00 0.267656250000D+03 0.882373215186D+00-0.806355016494D-08 + -0.219294848796D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00 0.512227416039D-08 0.220000000000D+02 + 0.338418000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 0 0 0.0-0.648468267173D-03-0.102318153950D-11 0.000000000000D+00 + 0.500000000000D+02-0.783437500000D+02 0.452590280773D-08-0.294192167427D-01 + -0.413320958614D-05 0.206718930276D-01 0.554695725441D-05 0.515367072487D+04 + 0.345600000000D+06-0.353902578354D-06-0.137437210544D+01-0.763684511185D-07 + 0.964780206900D+00 0.280593750000D+03-0.145877130192D+01-0.847392440154D-08 + -0.184650548579D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00-0.176951289177D-07 0.500000000000D+02 + 0.338418000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 0 0 0.0-0.759996473789D-04-0.147792889038D-10 0.000000000000D+00 + 0.900000000000D+01-0.385312500000D+02 0.411374278247D-08-0.136192496059D+01 + -0.206381082535D-05 0.386439217254D-02 0.962987542152D-05 0.515370129013D+04 + 0.345600000000D+06 0.122934579849D-06-0.250378255179D+00 0.931322574615D-07 + 0.972501944952D+00 0.200312500000D+03 0.977839840440D+00-0.772425031746D-08 + 0.296083761649D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00 0.186264514923D-08 0.900000000000D+01 + 0.338418000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 2 0 0.0 0.458782073110D-03-0.989075488178D-11 0.000000000000D+00 + 0.230000000000D+02-0.875625000000D+02 0.398195157855D-08 0.856784152042D+00 + -0.441074371338D-05 0.112522983691D-01 0.648945569992D-05 0.515367502975D+04 + 0.352800000000D+06-0.875443220139D-07-0.128582503296D+01 0.115483999252D-06 + 0.986446248943D+00 0.268687500000D+03 0.882354871656D+00-0.802997733793D-08 + -0.279654505875D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00 0.512227416039D-08 0.230000000000D+02 + 0.345618000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 2 0 0.0-0.648475252092D-03-0.102318153950D-11 0.000000000000D+00 + 0.510000000000D+02-0.101218750000D+03 0.446518599292D-08 0.102066764496D+01 + -0.562891364098D-05 0.206743824528D-01 0.562518835068D-05 0.515367678451D+04 + 0.352800000000D+06-0.229105353355D-06-0.137443319836D+01-0.383704900742D-06 + 0.964778723501D+00 0.280625000000D+03-0.145867661016D+01-0.838820654533D-08 + -0.223223583873D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00-0.176951289177D-07 0.510000000000D+02 + 0.352608000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 2 0 0.0-0.761062838137D-04-0.147792889038D-10 0.000000000000D+00 + 0.480000000000D+02-0.352812500000D+02 0.416267339205D-08-0.311632934865D+00 + -0.190921127796D-05 0.386500684544D-02 0.932998955250D-05 0.515370277405D+04 + 0.352800000000D+06 0.204890966415D-07-0.250434374180D+00 0.670552253723D-07 + 0.972503590735D+00 0.205343750000D+03 0.977708885865D+00-0.783604068826D-08 + 0.305727020472D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00 0.186264514923D-08 0.480000000000D+02 + 0.345618000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 1 22 1 13 4 0 0.0 0.458711292595D-03-0.989075488178D-11 0.000000000000D+00 + 0.240000000000D+02-0.828125000000D+02 0.395480759075D-08 0.190702358293D+01 + -0.443309545517D-05 0.112518356182D-01 0.625848770142D-05 0.515367356300D+04 + 0.360000000000D+06-0.257045030594D-06-0.128588337560D+01-0.147148966789D-06 + 0.986445480911D+00 0.274468750000D+03 0.882293174549D+00-0.781639701288D-08 + -0.116790579082D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00 0.512227416039D-08 0.240000000000D+02 + 0.352818000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 2 22 1 13 4 0 0.0-0.648482702672D-03-0.102318153950D-11 0.000000000000D+00 + 0.520000000000D+02-0.107218750000D+03 0.432089426830D-08 0.207083224759D+01 + -0.566989183426D-05 0.206748397322D-01 0.474043190479D-05 0.515367732811D+04 + 0.360000000000D+06 0.320374965668D-06-0.137449102898D+01-0.143423676491D-06 + 0.964779523717D+00 0.283750000000D+03-0.145866421632D+01-0.781532553968D-08 + -0.280368821344D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00-0.176951289177D-07 0.520000000000D+02 + 0.352818000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 + 3 22 1 13 4 0 0.0-0.762129202485D-04-0.147792889038D-10 0.000000000000D+00 + 0.490000000000D+02-0.364687500000D+02 0.414767276722D-08 0.738535335304D+00 + -0.193342566490D-05 0.386480439920D-02 0.926852226257D-05 0.515370239258D+04 + 0.360000000000D+06-0.856816768646D-07-0.250490193282D+00 0.391155481338D-07 + 0.972505606636D+00 0.206312500000D+03 0.977701038773D+00-0.779389607563D-08 + 0.229652423088D-09 0.100000000000D+01 0.219200000000D+04 0.000000000000D+00 + 0.200000000000D+01 0.000000000000D+00 0.186264514923D-08 0.490000000000D+02 + 0.352818000000D+06 0.400000000000D+01 0.000000000000D+00 0.000000000000D+00 diff --git a/src/test/resources/gnss/navigation/invalid-but-accepted.n b/src/test/resources/gnss/navigation/invalid-but-accepted.n new file mode 100644 index 0000000000..84db66fd85 --- /dev/null +++ b/src/test/resources/gnss/navigation/invalid-but-accepted.n @@ -0,0 +1,25 @@ + 3.05 NAVIGATION DATA MIXED RINEX VERSION / TYPE +MergeMNfile.tcl IGS 20220303 103506 GMT PGM / RUN BY / DATE +gfzrnx-1.16-8177 FILE MERGE 20220302 102431 UTC COMMENT + This file is an extract from a real but invalid file COMMENT + It claims to be in 3.05 version of the format COMMENT + but the GLONASS navigation messages only have 3 lines COMMENT + they miss the fourth line that was added in 3.05 COMMENT + we nevertheless accept this file COMMENT + END OF HEADER +G32 2022 03 01 22 00 00-7.673818618059E-05-7.048583938740E-12 0.000000000000E+00 + 2.500000000000E+01-8.525000000000E+01 4.025881979858E-09-2.966823168848E+00 + -4.360452294350E-06 5.700907553546E-03 1.271441578865E-05 5.153581066132E+03 + 2.520000000000E+05-2.980232238770E-08-8.699194228357E-02-3.166496753693E-08 + 9.585262613691E-01 1.339375000000E+02-2.332265338750E+00-7.636032357112E-09 + 1.500062483624E-10 1.000000000000E+00 2.199000000000E+03 0.000000000000E+00 + 2.000000000000E+00 0.000000000000E+00 4.656612873077E-10 2.500000000000E+01 + 2.448180000000E+05 4.000000000000E+00 +R01 2022 03 01 00 15 00 7.145106792450E-06-0.000000000000E+00 1.728000000000E+05 + -2.035722656250E+03 2.404670715332E+00-9.313225746155E-10 0.000000000000E+00 + -1.203031152344E+04-1.903871536255E+00 1.862645149231E-09 1.000000000000E+00 + -2.239498242188E+04 8.049907684326E-01 1.862645149231E-09 0.000000000000E+00 +R01 2022 03 01 00 45 00 7.145106792450E-06-0.000000000000E+00 1.750800000000E+05 + 1.849898437500E+03 1.886345863342E+00 0.000000000000E+00 0.000000000000E+00 + -1.558774804688E+04-2.006055831909E+00 9.313225746155E-10 1.000000000000E+00 + -2.009856835938E+04 1.730035781860E+00 2.793967723846E-09 0.000000000000E+00 diff --git a/src/test/resources/gnss/navigation/missing-run-by-305.n b/src/test/resources/gnss/navigation/missing-run-by-305.n new file mode 100644 index 0000000000..80e9aee034 --- /dev/null +++ b/src/test/resources/gnss/navigation/missing-run-by-305.n @@ -0,0 +1,6 @@ + 3.05 N: GNSS NAV DATA C: BEIDOU RINEX VERSION / TYPE + 4 4 573 6 LEAP SECONDS +BDSA 1.3970E-08 0.0000E+00 -3.5763E-07 7.1526E-07 IONOSPHERIC CORR +BDSB 1.3926E+05 -4.2598E+05 1.5073E+06 -7.8643E+05 IONOSPHERIC CORR +BDUT 5.5879354477E-09 9.769962617E-15 159414 790 TIME SYSTEM CORR + END OF HEADER \ No newline at end of file diff --git a/src/test/resources/gnss/navigation/missing-run-by-400.n b/src/test/resources/gnss/navigation/missing-run-by-400.n new file mode 100644 index 0000000000..aca397361a --- /dev/null +++ b/src/test/resources/gnss/navigation/missing-run-by-400.n @@ -0,0 +1,10 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER diff --git a/src/test/resources/gnss/navigation/unknown-ephemeris.n b/src/test/resources/gnss/navigation/unknown-ephemeris.n new file mode 100644 index 0000000000..4c723e1e5f --- /dev/null +++ b/src/test/resources/gnss/navigation/unknown-ephemeris.n @@ -0,0 +1,19 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +Manual Orekit 20230226 221319 UTC PGM / RUN BY / DATE +https://doi.org/10.xxxx DOI +Apache V2 LICENSE OF USE +not really a station STATION INFORMATION +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH Ω01 LNAV +Ω01 2022 10 05 00 00 00 2.727881073952e-04-6.252776074689e-12 0.000000000000e+00 + 7.000000000000e+01 7.334375000000e+01 3.794800925833e-09 2.948187988778e+00 + 3.667548298836e-06 1.200602215249e-02 4.000961780548e-06 5.153660102844e+03 + 2.592000000000e+05-3.911554813385e-08 2.410755371695e-01-1.192092895508e-07 + 9.889225508565e-01 3.138750000000e+02 9.444071913331e-01-7.813182593274e-09 + 2.167947446570e-10 1.000000000000e+00 2.230000000000e+03 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 5.122274160385e-09 7.000000000000e+01 + 2.520180000000e+05 4.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/wrong-type-Beidou.n b/src/test/resources/gnss/navigation/wrong-type-Beidou.n new file mode 100644 index 0000000000..f6bdcfcde2 --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-Beidou.n @@ -0,0 +1,20 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH C06 XXXX +C06 2022 10 05 00 00 00 9.048783686012e-04-1.184208286986e-11 2.574980159653e-19 + 6.000000000000e+00-7.187500000000e+00 1.097902874919e-09 2.152331220686e+00 + -5.401670932770e-08 3.037390066311e-03 2.197921276093e-05 6.493546108246e+03 + 2.592000000000e+05-9.220093488693e-08 2.872027314940e+00-1.303851604462e-08 + 9.467625640496e-01-4.523750000000e+02-3.092539458236e+00-1.799360664880e-09 + 7.482454531408e-10 0.000000000000e+00 8.740000000000e+02 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 8.600000000000e-09-1.800000000000e-09 + 2.592000000000e+05 5.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/wrong-type-GPS.n b/src/test/resources/gnss/navigation/wrong-type-GPS.n new file mode 100644 index 0000000000..8f03de9c0c --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-GPS.n @@ -0,0 +1,19 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +Manual Orekit 20230226 221319 UTC PGM / RUN BY / DATE +https://doi.org/10.xxxx DOI +Apache V2 LICENSE OF USE +not really a station STATION INFORMATION +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH G01 XXXX +G01 2022 10 05 00 00 00 2.727881073952e-04-6.252776074689e-12 0.000000000000e+00 + 7.000000000000e+01 7.334375000000e+01 3.794800925833e-09 2.948187988778e+00 + 3.667548298836e-06 1.200602215249e-02 4.000961780548e-06 5.153660102844e+03 + 2.592000000000e+05-3.911554813385e-08 2.410755371695e-01-1.192092895508e-07 + 9.889225508565e-01 3.138750000000e+02 9.444071913331e-01-7.813182593274e-09 + 2.167947446570e-10 1.000000000000e+00 2.230000000000e+03 0.000000000000e+00 + 2.000000000000e+00 0.000000000000e+00 5.122274160385e-09 7.000000000000e+01 + 2.520180000000e+05 4.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/wrong-type-Galileo.n b/src/test/resources/gnss/navigation/wrong-type-Galileo.n new file mode 100644 index 0000000000..e7302534fb --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-Galileo.n @@ -0,0 +1,20 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH E01 XXXX +E01 2022 10 05 00 30 00-5.407053395174e-04-7.347011887759e-12 0.000000000000e+00 + 5.100000000000e+01 5.593750000000e+00 3.506931792071e-09-6.303635591153e-01 + 2.998858690262e-07 2.659204183146e-04 5.604699254036e-06 5.440599237442e+03 + 2.610000000000e+05 2.235174179077e-08-1.950400432315e+00 2.235174179077e-08 + 9.718589427173e-01 2.261875000000e+02-1.107378249080e+00-5.725238479163e-09 + -3.360854278785e-10 5.160000000000e+02 2.230000000000e+03 + 3.120000000000e+00 6.500000000000e+01 2.328306436539e-10 0.000000000000e+00 + 2.618140000000e+05 diff --git a/src/test/resources/gnss/navigation/wrong-type-Glonass.n b/src/test/resources/gnss/navigation/wrong-type-Glonass.n new file mode 100644 index 0000000000..03e8d3fbfe --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-Glonass.n @@ -0,0 +1,17 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH R01 XXXX +R01 2022 10 05 00 15 00 1.574866473675e-05 0.000000000000e+00 2.592300000000e+05 + -6.686352539062e+03 3.702297210693e-01 9.313225746155e-10 0.000000000000e+00 + -1.537300634766e+04-2.607774734497e+00 9.313225746155e-10 1.000000000000e+00 + 1.922300781250e+04-1.955271720886e+00-2.793967723846e-09 0.000000000000e+00 + 1.790000000000e+02 8.381903171539e-09 2.000000000000e+00 3.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/wrong-type-IRNSS.n b/src/test/resources/gnss/navigation/wrong-type-IRNSS.n new file mode 100644 index 0000000000..50d820eff2 --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-IRNSS.n @@ -0,0 +1,20 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH I02 XXXX +I02 2022 10 05 00 05 36 4.418715834618e-04-2.182787284255e-11 0.000000000000e+00 + 1.610000000000e+02-1.531875000000e+02 4.263034715364e-09-4.329943394951e-01 + -5.219131708145e-06 2.062962856144e-03 4.917383193970e-07 6.493340431213e+03 + 2.595360000000e+05-1.154839992523e-07-1.874347616502e+00 1.527369022369e-07 + 5.123365807379e-01 6.900000000000e+01-2.938609902772e+00-3.767299780300e-09 + -6.739566444280e-10 2.230000000000e+03 + 2.000000000000e+00 0.000000000000e+00-1.862645149231e-09 + 2.598840000000e+05 diff --git a/src/test/resources/gnss/navigation/wrong-type-QZSS.n b/src/test/resources/gnss/navigation/wrong-type-QZSS.n new file mode 100644 index 0000000000..bf23047644 --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-QZSS.n @@ -0,0 +1,20 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH J02 XXXX +J02 2022 10 05 00 00 00-6.473157554865e-06-9.094947017729e-13 0.000000000000e+00 + 6.100000000000e+01-6.221875000000e+01 3.107272287506e-09-3.496194153491e-01 + -2.235174179077e-06 7.498337992001e-02 3.991648554802e-06 6.493705060959e+03 + 2.592000000000e+05-8.139759302139e-07-1.885344927612e+00-1.471489667892e-07 + 7.255417466631e-01 5.134375000000e+01-1.561478530488e+00-3.431571510156e-09 + -7.575315542299e-10 2.000000000000e+00 2.230000000000e+03 1.000000000000e+00 + 2.800000000000e+00 0.000000000000e+00 4.656612873077e-10 8.290000000000e+02 + 2.556060000000e+05 0.000000000000e+00 diff --git a/src/test/resources/gnss/navigation/wrong-type-SBAS.n b/src/test/resources/gnss/navigation/wrong-type-SBAS.n new file mode 100644 index 0000000000..905c18da6c --- /dev/null +++ b/src/test/resources/gnss/navigation/wrong-type-SBAS.n @@ -0,0 +1,16 @@ + 4.00 NAVIGATION DATA M RINEX VERSION / TYPE +BCEmerge congo 20221006 004604 GMT PGM / RUN BY / DATE +Merged GPS/GLO/GAL/BDS/QZS/SBAS/IRNSS navigation file COMMENT +based on CONGO and IGS tracking data COMMENT +DLR/GSOC: O. Montenbruck; P. Steigenberger COMMENT +extracted from BRD400DLR_S_20222780000_01D_MN.rnx COMMENT +https://cddis.nasa.gov/archive/gnss/data/daily/2022/brdc/ COMMENT +https://doi.org/10.57677/BRD400DLR DOI + 102 MERGED FILE + 18 18 2185 7 LEAP SECONDS + END OF HEADER +> EPH S22 XXXX +S22 2022 10 05 00 00 32 0.000000000000e+00 0.000000000000e+00 2.592480000000e+05 + -3.389392800000e+04 0.000000000000e+00 0.000000000000e+00 6.300000000000e+01 + 2.508018800000e+04 0.000000000000e+00 0.000000000000e+00 3.276700000000e+04 + 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 1.050000000000e+02 diff --git a/src/test/resources/gnss/tai-utc.dat b/src/test/resources/gnss/tai-utc.dat index dad80f2116..89d16226f3 100644 --- a/src/test/resources/gnss/tai-utc.dat +++ b/src/test/resources/gnss/tai-utc.dat @@ -38,3 +38,4 @@ 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S + 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S diff --git a/src/test/resources/ilrs/crd201_all_samples b/src/test/resources/ilrs/crd201_all_samples new file mode 100644 index 0000000000..eaa4b39f29 --- /dev/null +++ b/src/test/resources/ilrs/crd201_all_samples @@ -0,0 +1,311 @@ +00 6.1. Full rate +00 7080_lageos2_crd_20061113_15_00.fr2 +H1 CRD 2 2007 3 20 14 +H2 MLRS 7080 24 19 4 NASA +H3 LAGEOS2 9207002 5986 22195 0 1 1 +H4 0 2006 11 13 15 23 52 2006 11 13 15 45 35 1 1 1 1 0 0 2 0 +C0 0 532.000 std1 +10 55432.0414338 0.047960587856 std1 2 0 0 0 -na na +12 55432.0414338 std1 20735.0 1601.0000 0.00 0.0000 0.0000 +20 55432.0414338 801.80 301.36 39 0 +30 55432.0414338 297.2990 38.6340 0 2 1 na na +40 55432.0414338 0 std1 -na na 0.000 -913.0 0.0 56.0 na na na 3 3 0 4 na +10 55435.6429746 0.047926839980 std1 2 0 0 0 na na +12 55435.6429746 std1 20697.0 1601.0000 0.00 0.0000 0.0000 +30 55435.6429746 297.4480 38.7190 0 2 1 na na +10 56735.8021609 0.046094881873 std1 2 0 0 0 na na +12 56735.8021609 std1 18092.0 1601.0000 0.00 0.0000 0.0000 +30 56735.8021609 15.2330 45.7100 0 2 1 na na +H8 +00 6.2. Normal Point +00 7080_lageos2_crd_20061113_15_00.np2 +H1 CRD 2 2007 3 20 14 +H2 MLRS 7080 24 19 4 NASA +H3 LAGEOS2 9207002 5986 22195 0 1 1 +H4 1 2006 11 13 15 25 4 2006 11 13 15 44 40 0 0 0 0 1 0 2 0 +C0 0 532.000 std1 +11 55504.9728030 0.047379676080 std1 2 120 18 94.0 na na na 0.0 0 0.0 +20 55504.9728030 801.80 282.10 39 1 +40 55504.9728030 0 std1 na na 0.000 -913.0 0.0 56.0 na na na 3 3 0 4 na +11 55988.9809589 0.044893190432 std1 2 120 19 83.0 na na na 0.0 0 0.0 +20 55988.9809589 801.50 282.80 39 1 +11 56141.8467215 0.044635017248 std1 2 120 28 66.0 na na na 0.0 0 0.0 +11 56223.2817254 0.044605221903 std1 2 120 25 87.0 na na na 0.0 0 0.0 +20 56223.2817254 801.50 282.60 39 1 +11 56373.5463612 0.044746486398 std1 2 120 25 78.0 na na na 0.0 0 0.0 +20 56373.5463612 801.50 282.10 39 1 +11 56439.9749454 0.044889147842 std1 2 120 25 99.0 na na na 0.0 0 0.0 +11 56565.2288146 0.045288773098 std1 2 120 25 92.0 na na na 0.0 0 0.0 +11 56680.8785419 0.045804632570 std1 2 120 10 55.0 na na na 0.0 0 0.0 +20 56680.8785419 801.50 282.00 39 1 +50 std1 86.0 na -na na 0 +H8 +00 6.3. Sampled Engineering (Quicklook) +00 7080_lageos2_crd_20061113_15_00.ql2 +H1 CRD 2 2007 3 20 14 +H2 MLRS 7080 24 19 4 NASA +H3 LAGEOS2 9207002 5986 22195 0 1 1 +H4 2 2006 11 13 15 24 17 2006 11 13 15 44 59 0 0 0 0 0 0 2 0 +C0 0 532.000 std1 +10 55457.0521861 0.047753624332 std1 2 0 0 0 na na +20 55457.0521861 801.80 282.10 39 0 +30 55457.0521861 298.3470 39.2230 0 0 0 0.0000000 0.0000000 +10 55482.4631214 0.047552685849 std1 2 0 0 0 na na +30 55482.4631214 299.4370 39.8100 0 0 0 0.0000000 0.0000000 +10 56589.0390552 0.045383653062 std1 2 0 0 0 na na +20 56589.0390552 801.50 282.00 39 0 +30 56589.0390552 6.7380 47.9120 0 0 0 0.0000000 0.0000000 +10 56623.4538362 0.045531247776 std1 2 0 0 0 na na +30 56623.4538362 8.8120 47.4510 0 0 0 0.0000000 0.0000000 +10 56657.6685552 0.045690091816 std1 2 0 0 0 na na +30 56657.6685552 10.8230 46.9570 0 0 0 0.0000000 0.0000000 +10 56699.7866762 0.045901952309 std1 2 0 0 0 na na +30 56699.7866762 13.2310 46.3060 0 0 0 0.0000000 0.0000000 +50 std1 86.0 na na na 0 +H8 +00 6.4. Sample 2-Color Normal Point file +00 7810_lageos1_crd_20061230_07_00.np 2 +H1 CRD 2 2007 3 20 14 +H2 ZIMMERWALD 7810 68 1 7 EUROLAS +H3 LAGEOS1 7603901 1155 8820 0 1 1 +H4 1 2006 12 30 7 35 34 2006 12 30 8 12 29 0 0 0 0 1 0 2 0 +C0 0 846.000 std1 +C0 0 423.000 std2 +11 27334.1080890 0.051571851861 std1 2 120 36 154.0 na na na 0.0 0 0.0 +20 27334.1080890 923.30 275.40 43 1 +40 27334.1080890 0 std1 na na 0.000 113069.0 0.0 138.0 na na na 2 2 0 1 na +11 27343.5080895 0.051405458691 std2 2 120 28 79.0 na na na 0.0 0 0.0 +11 27372.6080888 0.050895050517 std2 2 120 30 76.0 na na na 0.0 0 0.0 +11 27373.1080893 0.050886342010 std1 2 120 17 158.0 na na na 0.0 0 0.0 +11 28003.8080894 0.042252027043 std1 2 120 19 170.0 na na na 0.0 0 0.0 +20 28003.8080894 923.40 275.50 42 1 +11 28008.7080899 0.042208378233 std2 2 120 85 71.0 na na na 0.0 0 0.0 +11 28402.1080897 0.040251470202 std1 2 120 6 183.0 na na na 0.0 0 0.0 +11 28406.5080897 0.040247878310 std2 2 120 45 78.0 na na na 0.0 0 0.0 +11 28620.0080896 0.040574433849 std1 2 120 18 163.0 na na na 0.0 0 0.0 +20 28620.0080896 923.50 275.50 42 1 +11 28627.6080899 0.040603966534 std2 2 120 114 71.0 na na na 0.0 0 0.0 +11 29151.2080895 0.045287136931 std2 2 120 7 65.0 na na na 0.0 0 0.0 +11 29156.7080892 0.045360524908 std1 2 120 7 134.0 na na na 0.0 0 0.0 +20 29156.7080892 923.50 275.80 42 1 +11 29225.6080889 0.046314735294 std1 2 120 45 164.0 na na na 0.0 0 0.0 +11 29237.7080892 0.046488750878 std2 2 120 50 78.0 na na na 0.0 0 0.0 +11 29326.8080894 0.047825380133 std1 2 120 49 152.0 na na na 0.0 0 0.0 +11 29334.2080895 0.047940570614 std2 2 120 73 85.0 na na na 0.0 0 0.0 +11 29461.4080892 0.050011219353 std2 2 120 29 76.0 na na na 0.0 0 0.0 +11 29477.2080896 0.050279566397 std1 2 120 25 187.0 na na na 0.0 0 0.0 +11 29544.4080897 0.051445695153 std1 2 120 19 164.0 na na na 0.0 0 0.0 +11 29549.5080897 0.051535764981 std2 2 120 14 87.0 na na na 0.0 0 0.0 +50 std1 165.0 na na na 0 +50 std2 78.0 na na na 0 +H8 +00 6.5. Sample showing all current record types +00 This is a recent MLRS normal point file. +00 Plausible '21' records have been added +00 Part of the full rate file has been added, so keep reading. +h1 CRD 2 2008 3 25 1 +h2 MDOL 7080 24 19 4 NASA +h3 jason1 105501 4378 26997 0 1 1 +h4 1 2008 3 25 0 45 17 2008 3 25 0 55 9 0 0 0 0 1 0 2 0 +h5 1 08 032500 esa 8401 +c0 0 532.000 std ml1 mcp mt1 swv met +c1 0 ml1 Nd-Yag 1064.00 10.00 100.00 200.0 na 1 +c2 0 mcp mcp 532.000 -1.00 3800.0 0.0 unknown na 0.00 na 0.0 none 5 10 1 +c3 0 mt1 TAC TAC MLRS_CMOS_TMRB_TD811 na 445.9 +c5 0 swv Monitor,Sattrk 2.000Bm,2.00Cm conpro,crd_cal,PoissonCRD,gnp 2.4a,1.7,2.2a,CM-2.01a +c6 0 met Paroscientific Met4 123456 Paroscientific Met4 123456 Paroscientific Met4 123456 +40 2716.0000000 0 std 67 58 -na -883.3 0.0 96.4 0.718 -0.126 364.4 3 3 0 3 14.5 +20 2716.000 801.73 286.76 35. 0 +21 2716.000 3.1 45 none 20 na 3 10 300.12 +11 2726.697640514675 0.013737698432 std 2 15 1 72.7 1.494 -0.536 -32.4 0.67 0 20.7 +11 2804.507921286791 0.011496837034 std 2 15 1 72.7 1.494 -0.536 -32.4 0.67 0 20.6 +11 2810.908760187949 0.011334723870 std 2 15 16 65.4 1.229 -1.235 -33.5 10.67 0 85.6 +20 2822.000 801.73 286.56 35. 0 +11 2828.611102554046 0.010908518342 std 2 15 1 72.7 1.494 -0.536 -32.4 0.67 0 20.1 +11 2850.814029348448 0.010424908601 std 2 15 3 116.6 0.649 -2.333 -86.7 2.00 0 40.1 +11 3104.347543373788 0.010760099652 std 2 15 2 108.7 0.354 -2.750 -73.5 1.33 0 21.3 +11 3113.248715491056 0.010963708963 std 2 15 11 78.5 1.345 -0.730 -45.8 7.33 0 62.3 +11 3124.950255557618 0.011244819341 std 2 15 14 65.2 1.635 0.207 4.5 9.33 0 71.5 +11 3142.652594816107 0.011696747487 std 2 15 12 74.2 1.369 -0.535 -161.6 8.00 0 68.9 +11 3150.653650787761 0.011910674436 std 2 15 2 123.0 0.354 -2.750 -83.7 1.33 0 30.6 +20 3151.000 801.73 286.16 35. 0 +21 3152.000 2 80 fog 20 na 3 10 298.43 +11 3169.356124039857 0.012431881802 std 2 15 1 72.7 1.494 -0.536 -32.4 0.67 0 20.4 +50 std 72.7 1.494 -0.536 -32.4 0 +h8 +00 Note that there is no h9 “end of file” record after the “h8”, +00 so this is a different part of the same file. +00 +00 The following is part of the full-rate file from the same pass. +00 '21' records have been added to this example. +00 Even though this is not transponder data, a c4 record has been dummied. +00 The 'mc1' clock field id for the c4 record was added to the c0 record. +00 The file also contains 91, 92, and 93 records, which are user-defined. +00 Station-defined records will normally be stripped off by the station before transmittal. +00 Just bypass them as you do not know the format. +00 The analysts can also add their own 9x records if they wish. +h1 CRD 2 2008 3 25 1 +h2 MDOL 7080 24 19 4 EUROLAS +h3 jason1 105501 4378 26997 0 1 1 +h4 0 2008 3 25 0 45 17 2008 3 25 0 55 9 0 0 0 0 1 0 2 0 +c0 0 532.000 std ml1 mcp mt1 mc1 swv met spi +c1 0 ml1 Nd-Yag 1064.00 10.00 100.00 200.0 -1.00 1 +c2 0 mcp mcp_varamp 532.000 na 3800.0 0.0 unknown na 0.00 na 0.0 none 5 10 1 +c3 0 mt1 TAC TAC MLRS_CMOS_TMRB_TD811 na 445.9 +c4 0 mc1 0.000 0.00 1234567890123456.789 0.00 0.000000000000 0 0 0 +c5 0 swv Monitor,Sattrk 2.000Bm,2.00Cm conpro,crd_cal,PoissonCRD,gnp 2.4a,1.7,2.2a,CM-2.01a +c6 0 met Paroscientific Met4 123456 Paroscientific Met4 123456 Paroscientific Met4 123456 +c7 0 spi SpiderCCR na na 0 80 crdcal 1.7 +91 8 85 2640 -2438728.97 -4909741.31 5429800.07 1474.0965 -5367.5721 -4187.1144 2 +20 2716.000 801.73 286.76 35 0 +21 2716.000 3.1 45 none 20 na 3 10 290.45 +40 2716.0000000 0 std 67 58 na -883.3 0.0 96.4 0.718 -0.126 364.4 3 3 0 3 14.5 +41 1016.0000000 0 std 37 28 na -883.2 0.0 96.2 0.715 -0.125 364.3 3 3 0 1 15.5 +41 4416.0000000 0 std 30 30 na -883.4 0.0 96.6 0.721 -0.127 364.4 3 3 0 2 13.7 +42 1006.1000000 -0.000000000780 std spi 18.612 3 3 2 0 0 4 na na +42 1006.2000000 -0.000000000783 std spi 18.611 3 3 2 0 0 4 na na +42 1006.5000000 -0.000000000779 std spi 18.609 3 3 2 0 0 4 na na +30 2717.996 326.8923 32.9177 0 1 1 na na +12 2717.9964890 std 0.0 0.0000 0.00 0.0000 0.0000 +30 2725.897 326.6035 33.9991 0 1 1 na na +10 2726.697640514675 0.013737698432 std 2 2 0 0 na na +30 2734.998 326.2469 35.2830 0 1 1 na na +10 2738.899248614531 0.013359440021 std 2 1 0 0 na na +30 2742.799 325.9195 36.4168 0 1 1 na na +30 2752.100 325.4955 37.8239 0 1 1 na na +10 2752.100991800282 0.012962363200 std 2 1 0 0 na na +30 2762.002 324.9939 39.3585 0 1 1 na na +21 3309.000 2 80 fog 20 na 3 10 +30 3309.224 164.3231 22.4342 0 1 1 na na +10 3309.224609210523 0.016974823000 std 2 1 0 0 na na +93 3309.224609210523 std 0.000 16.660 -20.265 0.97511 -0.00099 -2416.305 35267.021 +92 3309.000 -0.0003 0.0003 +h8 +00 6.6. Sample demonstrating free format +00 File 1: +h1 CRD 2 2008 5 8 19 +h2 MDOL 7080 24 19 4 NASA +h3 giovea 505101 7001 28922 0 1 1 +h4 1 2008 5 8 9 40 23 2008 5 8 9 50 45 0 0 0 0 1 0 2 0 +c0 0 532.000 std ml1 mcp_with_amp mt1 +c1 0 ml1 Nd-Yag 1064.00 10.00 100.00 200.0 na 1 +c2 0 mcp_with_amp mcp_and_avantek_amp 532.000 na 3800.0 0.0 unknown na 0.00 na 0.0 none 5 10 0 +c3 0 mt1 TAC TAC MLRS_CMOS_TMRB_TD811 na 439.45 +40 34823.000 0 std 398 190 na 402.3 0.0 131.1 0.168 -0.130 494.4 3 3 0 4 na +20 34823.000 796.55 287.86 24. 0 +11 34945.620986680762 0.167738944021 std 2 300 116 193.32 1.821 0.904 -22.8 3.87 0 40.5 +11 35237.103254500325 0.167288847260 std 2 300 143 173.04 1.601 -0.009 -61.3 4.77 0 38.5 +11 35422.490473700898 0.167002428581 std 2 300 19 179.75 1.318 -0.974 -259.7 0.63 0 3.2 +50 std 178.8 1.711 0.451 -128.2 0 +h8 +00 6.6. Sample demonstrating free format +00 File 2: +h1 CRD 2 2008 5 8 19 +h2 MDOL 7080 24 19 4 NASA +h3 giovea 505101 7001 28922 0 1 1 +h4 1 2008 5 8 9 40 23 2008 5 8 9 50 45 0 0 0 0 1 0 2 0 +c0 0 532.000 std ml1 mcp mt1 +c1 0 ml1 Nd-Yag 1064.00 10.00 100.00 200.0 na 1 +c2 0 mcp mcp 532.000 na 3800.0 0.0 unknown na 0.00 na 0.00 none 5 10 0 +c3 0 mt1 TAC TAC MLRS_CMOS_TMRB_TD811 na 439.4 +40 34823.000000 0 std 398 190 na 402.3 0.0 131.1 0.168 -0.130 494.4 3 3 0 4 1.0 +20 34823.000 796.55 287.86 24. 0 +11 34945.620986680762 0.167738944021 std 2 300 116 193.3 1.821 0.904 -22.8 3.87 0 40.3 +11 35237.103254500325 0.167288847260 std 2 300 143 173.0 1.601 -0.009 -61.3 4.77 0 35.3 +11 35422.490473700898 0.167002428581 std 2 300 19 179.7 1.318 -0.974 -259.7 0.63 0 2.5 +50 std 178.8 1.711 0.451 -128.2 0 +h8 +00 6.7. Sample demonstrating data blocks +H1 CRD 01 2009 5 10 7 +H2 HERL 7840 35 01 04 EUROLAS +H3 Ajisai 8606101 1500 16908 0 1 1 +H4 1 2009 5 10 5 29 2 2009 5 10 5 34 48 0 0 0 0 1 0 2 0 +C0 0 532.080 ES 10hz SPD5 GPS NA +C1 0 10hz Nd-Yag 1064.16 10.00 20.00 100.0 20.00 4 +C2 0 SPD5 SPAD5 532.000 20.00 0.0 0.0 +0.7v 0.0 0.15 20.0 0.0 Single_fot na na 0 +C3 0 GPS Radiocode_GPS_8000 Radiocode_GPS_8000 HxET_=_3x_dassault No_Sn 0.0 +20 19560.960 1015.20 277.50 99. 0 +20 19923.840 1015.23 277.70 98. 0 +20 20096.640 1015.24 277.80 98. 0 +20 20459.520 1015.23 278.10 98. 0 +40 19185.120 0 ES na na 122.977 105423.9 0.0 35.4 0.2 2.8 0.0 2 2 0 3 na +40 18014.400 0 ES na na 122.977 105420.9 0.0 35.4 0.2 2.9 0.0 2 2 0 1 na +40 20355.840 0 ES na na 122.977 105426.9 0.0 35.4 0.1 2.7 0.0 2 2 0 2 na +11 19755.5635353 0.015411425559 ES 2 30.0 42 217.0 0.000 0.000 0.0 5.4 0 0.0 +11 19786.3810075 0.014973907243 ES 2 30.0 56 213.0 0.000 0.000 0.0 7.3 0 0.0 +11 19813.6766125 0.014664455551 ES 2 30.0 87 213.0 0.000 0.000 0.0 11.3 0 0.0 +11 19844.4141312 0.014410182562 ES 2 30.0 66 218.0 0.000 0.000 0.0 8.6 0 0.0 +11 19871.9499495 0.014271511355 ES 2 30.0 70 208.0 0.000 0.000 0.0 9.1 0 0.0 +11 19903.0875582 0.014219515428 ES 2 30.0 27 248.0 0.000 0.000 0.0 3.5 0 0.0 +11 19939.9086510 0.014303031023 ES 2 30.0 36 213.0 0.000 0.000 0.0 4.7 0 0.0 +11 19966.0837316 0.014456622851 ES 2 30.0 48 234.0 0.000 0.000 0.0 6.2 0 0.0 +11 19993.1391490 0.014694794599 ES 2 30.0 56 208.0 0.000 0.000 0.0 7.3 0 0.0 +11 20025.6374106 0.015082008815 ES 2 30.0 46 194.0 0.000 0.000 0.0 6.0 0 0.0 +11 20053.0926792 0.015489205740 ES 2 30.0 59 185.0 0.000 0.000 0.0 7.6 0 0.0 +11 20080.3882364 0.015960679555 ES 2 30.0 24 189.0 0.000 0.000 0.0 3.1 0 0.0 +H8 +00 real npt data +H1 CRD 01 2022 03 26 20 +H2 GRZL 7839 34 02 4 +H3 lageos1 7603901 1155 08820 0 1 +H4 1 2022 3 25 23 10 20 2022 3 26 0 14 20 0 0 0 0 1 0 2 0 +C0 0 532.000 0902 2kHz C_SPAD1 GPS +C1 0 2kHz Nd:Van 1064 2000 0.400 10 10 1 +C2 0 C_SPAD1 SPAD 532.0 20 5.0 400 +1V 10 0.3 35 36 WinClean2.2 +C3 0 GPS HP58503A HP58503A Graz_Dassault NoSN 0.077 +20 83974 969.49 283.15 37.9 1 +20 410 969.45 283.15 37.5 1 +40 83974 0 0902 10000 7894 1.742 112201.6 3.8 16 0.045 -0.644 -1 2 2 0 +40 410 0 0902 10000 8073 1.742 112205.4 3.8 16 0.043 -0.665 -1 2 2 0 +11 83987.444463735456 0.056122042094 0902 2 120.0 121 35.1 0.075 -0.867 -9.9 0.1 0 +11 84058.066263742720 0.054897834737 0902 2 120.0 1801 36.8 -0.292 -1.101 27.4 0.8 0 +11 84532.284263732816 0.047566086749 0902 2 120.0 2949 36.3 0.257 -1.174 -24.5 1.2 0 +11 85051.311263740064 0.042400210493 0902 2 120.0 5502 33.7 0.182 -0.998 -18.1 2.3 0 +11 85251.779063745392 0.041551883692 0902 2 120.0 10716 31.5 0.443 -0.627 -13.4 4.5 0 +11 86033.919563737696 0.045255031318 0902 2 120.0 3330 35.6 0.295 -1.050 -22.1 1.4 0 +11 86042.791563732528 0.045355368159 0902 2 120.0 1778 35.7 0.214 -1.127 -24.1 0.7 0 +11 86346.020063735536 0.049383687622 0902 2 120.0 1516 34.4 -0.017 -1.038 11.4 0.6 0 +11 345.645163732581 0.056059159587 0902 2 120.0 525 33.7 0.023 -0.939 12.9 0.2 0 +11 380.563063745387 0.056695029716 0902 2 120.0 635 33.6 -0.020 -1.024 14.7 0.3 0 +50 0902 34.0 0.258 -0.949 -18.6 1 +H8 +h1 CRD 2 2022 5 1 3 +h2 YARL 7090 5 13 3 ILRS +h3 lageos2 9207002 5986 22195 0 1 1 +h4 1 2022 5 1 2 18 58 2022 5 1 2 24 3 0 0 0 0 1 0 2 0 +h5 1 22 043000 HTS 12001 +c0 0 532.000 new la1 mcp ti1 swm met cac +c1 0 la1 Nd:Yag 1064.00 5.00 100.00 150.0 15.00 1 +c2 0 mcp MCP-PMT 532.000 15.0 3488.3 31.0 analog 400.0 1.00 80.0 30.00 none na na 0 +c3 0 ti1 Truetime_XLDC Truetime_XLDC Cybi_ETM na 0.0 +c5 0 swm sattrk 6.10 HPLDP,GNP 9.11.3,2.8.3 +c6 0 met Paroscientific MET-4 106772 Paroscientific MET-4 106772 Paroscientific MET-4 106772 +c7 0 cac B 150.42450 1.00 0.1651 na HPLDP 9.11.3 +40 10888.300576549955 0 new 1853 1842 150.4245 96814.3 9.0 18.0 na na na 2 2 0 3 na +41 7907.550577400252 0 new 822 819 150.4245 96809.6 na 18.0 na na na 2 2 0 1 na +41 13872.550578089431 0 new 1031 1020 150.4245 96819.0 na 16.7 na na na 2 2 0 2 na +20 8357.401 989.60 296.00 31. 0 +11 8357.400568200001 0.040190018544 new 2 120.0 8 26.0 0.451 -1.446 na 1.33 0 na +20 8516.401 989.60 296.20 31. 0 +11 8516.400568100000 0.040136942985 new 2 120.0 1 32.0 0.000 0.000 na 0.17 0 na +20 8593.001 989.60 296.20 31. 0 +11 8593.000566900000 0.040249271363 new 2 120.0 5 40.0 1.445 0.180 na 0.83 0 na +20 8643.401 989.50 296.30 31. 0 +11 8643.400567700000 0.040371538045 new 2 120.0 1 32.0 0.000 0.000 na 0.17 0 na +50 new 32.1 1.430 1.604 na 0 +h8 +h1 crd 1 2012 1 16 3 +h2 ZIML 7810 68 1 4 +h3 ajisai 8606101 1500 16908 0 1 +h4 1 2012 1 16 3 11 54 -1 -1 -1 -1 -1 -1 0 0 0 0 1 0 2 0 +c0 0 532.100 sys1 frq1 spad tim1 +c1 0 frq1 Nd-YAG 1064.20 100.00 8.00 60.0 10.00 0 +c2 0 spad CSPAD 532.100 -1.00 -1.0 -1.0 tbd -1.0 0.10 90.0 10.00 na +c3 0 tim1 TrueTime_XL-DC BVA_8607BE_10MHz A032ET 3203 0.0 +40 11514.247530279681 0 sys1 -1 836 658.711 108382.5 0.0 28.6 -1.000 -1.000 -1.0 2 0 0 +50 sys1 253.0 0.000 0.000 0.0 0 +11 11514.247500086414 0.022245890018 sys1 2 30 97 23.3 -1.000 -1.000 -1.0 -1.00 0 +20 11514.248 913.46 266.04 100. 1 +11 11532.317500081099 0.021670462296 sys1 2 30 302 14.9 -1.000 -1.000 -1.0 -1.00 0 +h8 +H9 diff --git a/src/test/resources/ilrs/crd_all_fields.frd b/src/test/resources/ilrs/crd_all_fields.frd index ee9c00d5cb..3def866651 100644 --- a/src/test/resources/ilrs/crd_all_fields.frd +++ b/src/test/resources/ilrs/crd_all_fields.frd @@ -2,7 +2,7 @@ H1 CRD 2 2017 09 26 04 H2 STL3 7825 90 01 4 network H3 champ 0003902 8002 026405 0 1 0 H4 0 2017 09 26 03 55 41 2017 09 26 04 04 48 0 0 0 0 1 0 2 0 -C0 0 532.10 IDAA IDAB IDAJ IDAV +C0 0 532.10 IDAA IDAB IDAJ IDAV id C1 0 IDAB Nd-YAG 532.10 0.00 21.00 12.0 0.00 1 C2 0 IDAJ CSPAD 532.00 20.00 11.0 100.0 ECL 12.0 2.00 90.0 12.0 Manual 0 0 0 C3 0 IDAV TrueTime_XLi TrueTime_OCXO MRCS NA 0.2322 @@ -16,11 +16,11 @@ C7 0 calib name 1.4 12.0 0.0 0.0 software software 42 0. 0. id id 0. 0 0 0 0 0 0 0 0 20 14353.388283000000 923.74 289.42 28.1 0 21 14353.388283000000 0. 0. WMO 012 0. 12 20 245.3 -40 14140.700000000001 0 IDAA 1139 264 69.592 160524.4 112.1 23.3 0.100 -0.400 -1.3 2 3 0 -10 14487.343206247217 0.003603959600 IDAA 2 2 0 0 0 -10 14488.159872846696 0.003589037085 IDAA 2 2 0 0 0 -10 14488.276539547229 0.003586934492 IDAA 2 2 0 0 0 -10 14488.359872846821 0.003585437115 IDAA 2 2 0 0 0 +40 14140.700000000001 0 IDAA 1139 264 69.592 160524.4 112.1 23.3 0.100 -0.400 -1.3 2 3 0 0 na +10 14487.343206247217 0.003603959600 IDAA 2 2 0 0 0 0 +10 14488.159872846696 0.003589037085 IDAA 2 2 0 0 0 0 +10 14488.276539547229 0.003586934492 IDAA 2 2 0 0 0 0 +10 14488.359872846821 0.003585437115 IDAA 2 2 0 0 0 0 12 14488.359872846821 id 65.0 1.3 42.0 42.0 42.0 30 14343.574333000000 215.000000 15.000010 0 2 0 na na 30 14345.216363000000 215.436415 14.562718 0 2 0 na na diff --git a/src/test/resources/ionex/split-1.19i b/src/test/resources/ionex/split-1.19i new file mode 100644 index 0000000000..c421f3f929 --- /dev/null +++ b/src/test/resources/ionex/split-1.19i @@ -0,0 +1,1306 @@ + 1.0 IONOSPHERE MAPS GPS IONEX VERSION / TYPE +BIMINX V5.3 AIUB 16-JAN-19 07:26 PGM / RUN BY / DATE +BROADCAST IONOSPHERE MODEL FOR DAY 015, 2019 COMMENT + 2019 1 15 0 0 0 EPOCH OF FIRST MAP + 2019 1 15 2 0 0 EPOCH OF LAST MAP + 3600 INTERVAL + 3 # OF MAPS IN FILE + NONE MAPPING FUNCTION + 0.0 ELEVATION CUTOFF + OBSERVABLES USED + 6371.0 BASE RADIUS + 2 MAP DIMENSION + 350.0 350.0 0.0 HGT1 / HGT2 / DHGT + 87.5 -87.5 -2.5 LAT1 / LAT2 / DLAT + -180.0 180.0 5.0 LON1 / LON2 / DLON + -1 EXPONENT +TEC/RMS values in 0.1 TECU; 9999, if no value available COMMENT + END OF HEADER + 1 START OF TEC MAP + 2019 1 15 0 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 93 93 93 93 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 93 94 95 95 95 95 94 93 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 95 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 94 95 96 97 97 97 97 96 95 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 99 98 97 95 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 94 96 97 98 99 100 100 99 99 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 102 101 100 99 98 96 95 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 95 97 99 100 102 102 103 102 102 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 106 105 104 103 102 100 99 97 96 94 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 96 98 101 103 104 105 106 106 106 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 110 109 109 107 106 104 103 101 99 98 96 95 94 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 96 100 102 105 107 108 109 110 110 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 114 114 113 112 111 109 107 105 103 102 100 98 97 96 95 94 + 93 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 97 101 104 107 110 112 113 114 114 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 119 119 118 117 116 114 112 110 108 106 104 102 100 98 97 96 + 95 94 94 93 93 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 98 102 106 110 113 115 117 118 119 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 123 123 122 121 119 117 115 113 110 108 106 104 102 100 98 + 97 96 95 94 93 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 99 103 108 112 116 119 121 122 123 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 128 129 128 128 126 125 123 120 118 115 113 110 108 105 103 101 + 99 98 96 95 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 96 + 101 106 111 115 119 122 125 127 128 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 133 134 134 133 132 130 128 126 123 120 117 115 112 109 106 104 + 102 100 98 96 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 97 + 103 109 114 119 123 126 129 131 133 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 138 139 140 139 138 136 134 132 129 126 123 119 116 113 110 107 + 104 102 99 97 95 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 100 + 106 112 118 123 127 131 134 137 138 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 144 145 145 145 144 142 140 138 135 131 128 124 121 117 114 110 + 107 104 101 98 95 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 95 102 + 109 115 121 127 131 136 139 142 144 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 151 151 151 150 149 146 144 141 137 133 129 125 121 117 113 + 110 106 103 99 96 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 97 105 + 112 119 125 131 136 140 144 147 149 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 155 157 157 157 156 155 153 150 147 143 139 135 130 126 121 117 + 113 108 104 100 97 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 99 107 + 115 122 129 135 141 145 149 153 155 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 161 163 163 164 163 161 159 156 153 149 145 140 135 130 125 121 + 116 111 106 102 97 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 94 102 110 + 118 126 133 139 145 150 155 158 161 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 168 169 170 169 168 165 163 159 155 151 146 141 135 130 124 + 119 113 108 103 98 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 96 104 113 + 121 129 137 144 150 155 160 164 166 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 172 174 175 176 175 174 172 169 165 161 157 152 146 141 135 129 + 123 117 111 105 100 94 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 98 107 116 + 125 133 141 148 154 160 165 169 172 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 178 180 181 182 181 180 178 175 172 167 163 158 152 146 140 134 + 127 121 114 108 102 96 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 100 110 119 + 128 137 145 152 159 165 170 174 178 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 186 187 188 188 186 184 182 178 174 169 163 158 151 145 138 + 132 125 118 111 104 98 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 102 112 122 + 131 140 149 156 163 170 175 179 183 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 188 191 193 194 194 192 190 188 184 180 175 169 163 157 150 143 + 136 129 122 114 107 100 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 105 115 125 + 134 144 152 160 168 174 180 184 188 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 196 198 199 199 198 196 194 190 186 181 175 169 162 155 148 + 141 133 125 118 110 102 95 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 96 107 117 127 + 137 147 156 164 172 179 184 189 193 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 198 201 204 205 205 204 202 200 196 192 187 181 175 168 161 153 + 145 137 129 121 113 105 97 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 98 109 120 130 + 140 150 159 168 176 183 189 194 198 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 206 208 210 210 209 208 205 202 197 192 186 180 173 166 158 + 150 142 133 125 116 108 99 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 100 111 122 133 + 143 153 162 171 179 187 193 198 203 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 207 211 213 215 215 215 213 211 207 203 198 192 185 178 171 163 + 154 146 137 128 119 111 102 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 101 113 124 135 + 145 156 165 174 183 190 197 202 207 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 215 217 219 220 219 218 216 212 208 203 197 191 183 176 168 + 159 150 141 132 123 114 104 95 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 103 114 126 137 + 148 158 168 177 186 193 200 206 211 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 214 218 221 223 224 224 223 220 217 213 208 202 196 188 180 172 + 163 154 145 136 126 117 107 98 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 104 115 127 138 + 149 160 170 180 188 196 203 209 214 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 218 222 225 227 228 228 227 225 222 218 213 207 200 193 185 177 + 168 159 149 139 129 119 110 100 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 93 105 117 128 140 + 151 162 172 182 191 199 206 212 218 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 225 228 230 232 232 231 229 226 222 217 211 205 197 189 181 + 172 162 153 143 133 122 112 102 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 94 105 117 129 141 + 152 163 173 183 192 201 208 215 220 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 222 227 231 233 235 235 234 233 230 226 221 215 209 201 193 185 + 176 166 156 146 136 125 115 104 94 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 94 106 118 130 141 + 153 164 174 184 194 202 210 217 222 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 229 233 236 237 238 237 236 233 229 225 219 212 205 197 189 + 179 170 160 149 139 128 117 107 96 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 94 106 118 130 141 + 153 164 175 185 194 203 211 218 224 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 225 230 234 238 239 240 240 238 236 232 228 222 216 209 201 192 + 183 173 163 152 142 131 120 109 98 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 94 106 117 129 141 + 153 164 175 185 195 204 212 219 225 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 226 231 235 239 241 242 242 241 238 235 231 225 219 212 204 195 + 186 176 166 155 144 133 122 111 100 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 93 105 117 129 140 + 152 163 174 185 194 204 212 219 226 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 226 231 236 239 242 243 243 242 240 237 233 227 221 214 206 198 + 188 178 168 157 146 135 124 112 101 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 104 116 127 139 + 151 162 173 184 194 203 211 219 226 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 225 231 236 239 242 244 244 243 241 238 234 229 223 216 208 200 + 191 181 170 160 148 137 126 114 103 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 103 114 126 137 + 149 160 171 182 192 201 210 218 225 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 223 230 235 239 242 244 244 244 242 239 235 231 225 218 210 202 + 192 183 172 161 150 139 127 115 104 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 101 112 124 135 + 147 158 169 180 190 199 208 216 223 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 221 228 233 237 241 243 244 243 242 239 236 231 225 219 211 203 + 194 184 173 163 151 140 128 116 105 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 99 110 121 133 + 144 155 166 177 187 197 206 214 221 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 218 225 231 235 239 241 242 242 241 239 236 231 226 219 212 204 + 194 185 174 164 152 141 129 117 105 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 97 108 118 130 + 141 152 163 173 184 193 202 211 218 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 221 227 232 236 239 240 241 240 238 235 231 225 219 212 204 + 195 185 175 164 153 141 130 118 106 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 105 115 126 + 137 148 159 169 179 189 198 207 215 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 210 217 223 228 233 236 237 238 238 236 233 229 224 218 211 203 + 194 185 175 164 153 141 130 118 106 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 102 112 122 + 133 143 154 164 174 184 193 202 210 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 205 212 218 224 228 232 234 235 235 233 231 227 222 217 210 202 + 194 184 174 164 152 141 129 117 106 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 98 108 118 + 128 138 149 159 169 179 188 197 205 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 198 206 213 218 223 227 229 231 231 230 228 224 220 214 208 200 + 192 183 173 162 152 140 129 117 105 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 95 104 113 + 123 133 143 153 163 172 181 190 198 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 191 199 206 212 217 221 224 226 226 226 224 221 217 211 205 198 + 190 181 171 161 150 139 127 116 104 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 100 109 + 118 127 136 146 156 165 174 183 191 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 191 198 205 210 214 218 220 221 220 219 216 213 208 202 195 + 187 178 169 159 148 137 126 114 103 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 97 104 + 112 121 130 139 148 157 166 175 183 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 183 190 196 202 207 210 213 214 214 213 211 208 203 198 191 + 183 175 166 156 146 135 124 112 101 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 100 + 107 115 123 131 140 149 158 166 175 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 173 180 187 193 198 202 205 207 207 207 205 202 198 193 186 + 179 171 162 153 143 132 121 110 99 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 96 + 102 109 116 123 131 140 148 157 165 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 154 162 170 177 183 188 193 196 198 199 199 198 195 192 187 181 + 174 167 158 149 139 129 118 108 97 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 98 103 109 115 122 130 138 146 154 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 143 151 158 165 172 177 182 186 189 190 191 190 188 185 180 175 + 169 161 153 144 135 125 115 105 95 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 95 98 102 107 113 120 127 135 143 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 130 138 146 153 159 166 171 175 178 180 181 181 180 177 173 168 + 162 156 148 140 131 121 112 102 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 95 97 101 105 110 116 123 130 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 117 125 132 139 146 153 158 163 167 169 171 171 170 168 165 161 + 155 149 142 134 126 117 108 99 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 96 100 105 111 117 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 110 117 124 132 138 144 150 154 157 159 160 160 159 156 153 + 148 142 136 129 121 113 104 96 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 98 104 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 95 102 109 116 123 129 135 140 144 147 149 149 149 147 144 + 140 135 129 123 115 108 100 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 99 106 113 119 125 130 133 136 137 137 136 134 + 131 127 122 116 110 103 97 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 95 102 109 114 119 122 125 126 126 124 + 122 119 115 110 105 99 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 98 104 108 112 114 115 114 + 113 111 108 104 100 96 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 93 98 101 103 105 + 105 104 102 99 96 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 96 97 97 96 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 1 END OF TEC MAP + 2 START OF TEC MAP + 2019 1 15 1 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 93 93 + 93 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 94 94 + 94 95 94 94 93 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 94 95 95 + 96 97 97 97 96 96 95 94 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 96 95 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 94 96 97 + 98 99 99 99 99 99 98 97 96 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 100 98 97 96 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 95 97 98 + 100 101 102 102 102 102 102 101 100 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 102 101 99 98 96 95 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 95 98 100 + 102 104 105 106 106 106 106 105 104 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 108 107 105 104 102 100 98 97 95 94 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 96 99 102 + 104 106 108 109 110 110 110 109 108 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 113 112 110 108 106 104 102 100 98 97 95 94 93 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 97 100 104 + 106 109 111 113 114 114 114 114 113 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 118 117 115 113 111 109 106 104 102 100 98 97 96 94 94 93 + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 97 101 105 + 109 112 115 117 118 119 119 119 118 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 122 120 118 116 114 111 109 106 104 102 100 98 96 95 94 + 93 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 98 103 107 + 111 115 118 121 122 123 124 124 123 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 129 128 126 124 121 119 116 113 110 108 105 103 101 99 97 96 + 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 100 105 110 + 114 118 122 125 127 128 129 129 129 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 134 133 132 130 127 124 121 118 115 112 109 106 103 101 99 97 + 95 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 96 102 108 113 + 118 122 126 129 131 133 134 135 134 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 140 139 138 135 133 130 127 123 120 116 113 109 106 104 101 99 + 96 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 98 104 110 116 + 121 126 130 134 137 139 140 140 140 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 146 145 144 142 139 136 132 128 125 121 117 113 110 106 103 100 + 97 95 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 94 100 107 114 120 + 125 131 135 139 142 144 146 146 146 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 152 151 150 148 145 142 138 134 130 125 121 117 113 109 105 102 + 99 96 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 96 103 110 117 123 + 130 135 140 144 147 150 151 152 152 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 158 158 156 154 151 148 144 140 135 130 126 121 116 112 108 104 + 100 96 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 98 105 113 120 127 + 134 140 145 149 153 155 157 158 158 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 164 163 160 157 154 150 145 141 135 130 125 120 115 110 105 + 101 97 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 100 108 116 124 131 + 138 144 150 154 158 161 163 164 165 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 171 170 169 167 164 160 156 151 146 141 135 130 124 118 113 107 + 102 98 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 94 102 111 119 128 135 + 142 149 155 159 164 167 169 170 171 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 177 176 175 173 170 167 162 157 152 146 141 134 128 122 116 110 + 104 98 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 96 105 114 123 131 139 + 147 153 159 165 169 172 175 176 177 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 183 181 179 176 173 168 163 158 152 146 139 133 126 120 113 + 107 101 95 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 98 108 117 126 135 143 + 151 158 164 170 174 178 181 182 183 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 189 189 188 186 183 179 175 170 164 158 151 145 138 130 123 116 + 110 103 96 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 100 110 120 129 138 147 + 155 162 169 175 179 183 186 188 189 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 195 195 194 192 189 185 181 176 170 163 157 150 142 135 127 120 + 113 105 98 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 103 113 123 133 142 151 + 159 167 174 179 185 189 192 194 195 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 200 200 199 198 195 191 187 181 176 169 162 155 147 140 132 124 + 116 108 101 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 94 105 115 126 136 145 155 + 163 171 178 184 189 194 197 199 200 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 206 206 205 203 201 197 192 187 181 175 168 160 152 144 136 128 + 119 111 103 95 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 96 107 118 128 139 149 158 + 167 175 182 189 194 198 202 204 206 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 211 210 209 206 203 198 193 187 180 173 165 157 149 140 132 + 123 114 106 98 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 98 109 120 131 142 152 161 + 170 179 186 193 198 203 207 209 211 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 216 216 214 212 208 204 198 192 186 178 170 162 153 144 135 + 126 117 109 100 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 100 111 122 133 144 154 164 + 173 182 190 196 202 207 211 214 215 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 221 220 219 217 213 209 204 197 191 183 175 167 158 149 139 + 130 121 111 102 94 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 101 113 124 135 146 157 167 + 176 185 193 200 206 211 215 218 220 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 225 225 224 221 218 214 209 202 196 188 180 171 162 153 143 + 134 124 114 105 96 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 103 114 126 137 148 159 169 + 179 188 196 203 209 215 219 222 224 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 228 229 229 228 226 223 218 213 207 200 193 185 176 167 157 147 + 137 127 117 107 98 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 104 116 127 139 150 161 171 + 181 190 198 206 212 218 222 225 228 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 231 232 233 232 230 227 223 218 212 205 197 189 180 171 161 151 + 141 131 120 110 100 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 93 105 117 128 140 151 162 173 + 183 192 200 208 215 220 225 229 231 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 234 235 236 235 233 230 226 221 216 209 201 193 184 175 165 155 + 144 134 123 113 102 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 94 105 117 129 141 152 163 174 + 184 193 202 210 217 222 227 231 234 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 236 238 238 238 236 234 230 225 219 213 205 197 188 178 169 158 + 148 137 126 115 105 94 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 94 106 118 130 141 153 164 175 + 185 194 203 211 218 224 229 233 236 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 238 240 241 240 239 236 233 228 223 216 209 200 191 182 172 161 + 151 140 129 118 107 96 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 94 106 118 130 141 153 164 175 + 185 195 204 212 219 225 230 235 238 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 241 242 242 241 239 235 231 225 219 212 203 194 185 175 164 + 154 143 131 120 109 98 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 94 106 117 129 141 152 164 175 + 185 195 204 212 219 226 231 236 239 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 242 243 243 243 240 237 233 228 221 214 206 197 188 178 167 + 156 145 134 122 111 100 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 93 105 117 128 140 152 163 174 + 184 194 203 211 219 226 231 236 239 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 242 244 244 243 242 239 234 229 223 216 208 199 190 180 169 + 158 147 136 124 113 101 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 104 115 127 139 150 161 172 + 183 192 202 210 218 225 231 236 239 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 242 243 244 244 242 239 235 231 225 218 210 201 192 182 171 + 160 149 138 126 114 103 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 103 114 125 137 148 159 170 + 180 190 200 208 216 223 229 234 239 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 237 240 242 243 243 242 239 236 231 225 219 211 203 193 183 173 + 162 151 139 127 115 104 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 101 112 123 134 146 157 167 + 178 188 197 206 214 221 227 233 237 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 235 238 241 242 242 241 239 236 231 226 219 212 203 194 185 174 + 163 152 140 128 117 105 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 99 110 121 132 143 153 164 + 174 184 194 203 211 218 225 230 235 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 232 235 238 240 240 240 238 235 231 225 219 212 204 195 185 175 + 164 153 141 129 117 105 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 97 107 118 128 139 150 160 + 170 180 190 199 207 214 221 227 232 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 228 232 235 237 238 237 236 233 229 224 218 211 203 195 185 175 + 164 153 141 130 118 106 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 94 104 114 125 135 145 156 + 166 175 185 194 202 210 217 223 228 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 223 227 231 233 234 234 233 231 227 222 217 210 202 194 184 174 + 164 153 141 130 118 106 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 101 111 121 131 141 151 + 160 170 179 188 197 204 211 218 223 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 217 222 226 228 230 230 229 227 224 220 214 208 201 192 183 174 + 163 152 141 129 117 106 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 98 107 116 126 135 145 + 154 164 173 182 190 198 205 212 217 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 216 220 223 225 225 225 223 220 217 212 205 198 190 182 172 + 162 151 140 128 117 105 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 95 103 112 121 130 139 + 148 157 166 175 183 191 198 205 211 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 209 213 216 218 220 220 218 216 212 208 202 195 188 179 170 + 160 149 138 127 116 104 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 100 107 115 124 132 + 141 150 158 167 175 183 190 197 203 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 195 200 205 209 211 213 213 213 211 208 203 198 192 184 176 167 + 157 147 136 125 114 103 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 96 103 110 118 125 + 133 142 150 158 166 174 182 189 195 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 185 191 196 200 203 205 206 206 204 202 198 193 187 180 172 164 + 154 145 134 123 112 101 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 93 99 105 112 118 + 126 133 141 149 157 164 172 179 185 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 181 186 191 194 197 198 198 197 195 192 188 182 176 168 160 + 151 141 131 121 110 99 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 95 100 106 111 + 118 124 131 139 146 154 161 168 175 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 163 170 175 180 184 187 189 190 189 188 185 181 176 170 163 155 + 147 138 128 118 107 97 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 96 100 105 + 110 115 121 128 135 142 149 156 163 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 151 157 163 168 173 176 179 180 180 179 177 174 169 164 158 150 + 142 133 124 115 105 95 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 94 97 99 + 103 107 111 117 123 130 137 144 151 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 137 143 150 155 160 165 168 170 170 170 169 166 162 157 151 145 + 137 129 120 111 102 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 94 + 95 98 101 106 111 117 123 130 137 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 122 129 135 141 147 152 155 158 160 160 159 157 154 150 145 138 + 132 124 116 107 98 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 94 98 103 109 115 122 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 106 113 120 126 132 138 142 145 148 149 149 148 145 142 137 132 + 126 119 111 103 95 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 99 106 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 96 103 110 116 122 128 132 135 137 138 137 136 133 130 125 + 120 113 107 100 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 99 106 112 117 121 124 126 127 126 124 122 118 + 113 108 102 96 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 95 102 107 111 114 116 116 115 114 111 + 107 103 98 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 97 101 105 106 107 106 104 + 102 99 95 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 93 96 98 99 99 + 98 96 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 94 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 2 END OF TEC MAP + 3 START OF TEC MAP + 2019 1 15 2 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 93 94 94 94 + 93 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 94 95 95 96 96 + 96 96 95 95 94 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 94 95 96 97 98 98 + 99 99 98 98 97 96 95 94 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 96 95 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 94 96 97 99 100 101 + 101 102 102 101 101 100 99 97 96 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 100 98 97 95 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 95 97 99 101 102 104 + 105 105 105 105 105 104 103 101 100 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 103 101 99 97 96 94 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 95 98 101 103 105 107 + 108 109 110 110 109 108 107 106 104 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 109 107 105 103 101 99 97 96 94 93 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 96 99 102 105 108 110 + 112 113 114 114 114 113 112 111 109 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 114 112 110 107 105 103 101 99 97 96 94 94 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 97 101 104 108 111 113 + 115 117 118 119 119 118 117 116 114 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 119 117 115 112 109 107 104 102 100 98 96 95 94 93 93 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 97 102 106 110 114 117 + 119 121 123 124 124 123 123 121 119 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 125 122 120 117 114 111 108 106 103 101 99 97 95 94 93 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 98 103 108 112 117 120 + 123 126 128 129 129 129 128 127 125 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 131 128 125 122 119 116 112 109 106 103 101 99 97 95 94 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 95 100 105 111 116 120 124 + 128 130 133 134 135 135 134 132 131 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 136 134 131 128 124 121 117 113 110 106 103 101 98 96 94 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 96 102 108 114 119 124 129 + 132 136 138 140 140 140 140 138 136 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 143 140 137 133 130 126 121 117 113 110 106 103 100 97 95 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 98 105 111 117 123 129 133 + 137 141 143 145 146 146 146 145 143 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 146 143 139 135 131 126 122 117 113 109 105 102 98 95 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 93 100 107 114 121 127 133 138 + 142 146 149 151 152 153 152 151 149 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 155 152 149 145 141 136 131 126 121 116 112 107 103 100 96 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 95 103 110 118 125 131 137 143 + 148 152 155 157 158 159 158 157 155 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 161 159 155 151 147 142 136 131 125 120 115 110 105 101 97 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 98 105 113 121 129 136 142 148 + 153 157 160 163 164 165 165 163 161 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 168 165 162 157 153 147 142 136 130 124 118 112 107 102 97 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 100 108 117 125 133 140 147 153 + 158 162 166 169 170 171 171 170 168 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 174 171 168 164 159 153 147 141 135 128 122 115 109 103 98 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 93 102 111 120 128 137 144 151 158 + 163 168 172 174 176 177 177 176 174 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 180 178 174 170 165 159 153 147 140 133 126 119 112 106 100 94 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 96 105 114 123 132 141 149 156 162 + 168 173 177 180 182 183 183 182 180 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 187 184 180 176 171 165 159 152 145 138 130 123 116 109 102 96 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 98 107 117 127 136 145 153 160 167 + 173 178 183 186 188 189 189 188 187 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 190 186 182 177 171 164 157 150 143 135 127 119 112 105 98 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 100 110 120 130 139 148 157 165 172 + 178 183 188 191 194 195 195 194 193 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 199 196 192 188 183 177 170 163 155 147 139 131 123 115 107 100 + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 102 113 123 133 143 152 161 169 176 + 183 188 193 197 199 201 201 200 199 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 204 202 198 194 188 182 176 168 160 152 144 135 127 118 110 102 + 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 104 115 126 136 146 156 165 173 181 + 187 193 198 202 204 206 206 206 204 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 210 207 204 199 194 188 181 174 166 157 149 140 131 122 113 105 + 96 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 96 107 118 128 139 149 159 168 177 185 + 191 197 202 206 209 211 212 211 210 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 213 209 205 199 193 186 179 171 162 153 144 135 126 116 107 + 99 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 98 109 120 131 142 152 162 172 180 188 + 195 202 207 211 214 216 217 216 215 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 218 214 210 205 198 191 184 176 167 158 148 139 129 120 110 + 101 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 99 111 122 133 144 155 165 175 184 192 + 199 205 211 215 218 220 221 221 220 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 222 219 215 209 203 196 189 180 172 162 153 143 133 123 113 + 104 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 101 112 124 135 147 157 168 178 187 195 + 202 209 214 219 222 224 225 225 224 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 229 226 223 219 214 208 201 193 185 176 167 157 147 136 126 116 + 106 96 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 102 114 126 137 149 160 170 180 189 197 + 205 212 217 222 225 228 229 229 229 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 232 230 227 223 218 212 205 198 189 180 171 161 151 140 130 119 + 109 99 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 104 115 127 139 150 161 172 182 191 200 + 207 214 220 225 229 231 233 233 232 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 236 234 231 227 222 216 209 202 193 184 175 165 154 144 133 122 + 111 101 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 105 116 128 140 152 163 173 183 193 201 + 209 216 222 227 231 234 236 236 236 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 238 237 234 230 226 220 213 206 197 188 178 168 158 147 136 125 + 114 103 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 105 117 129 141 152 164 174 184 194 203 + 211 218 224 229 233 236 238 239 238 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 241 239 237 233 229 223 216 209 201 191 182 172 161 150 139 128 + 116 105 95 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 106 118 130 141 153 164 175 185 195 203 + 212 219 225 230 235 238 240 241 241 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 242 241 239 236 231 226 219 212 204 195 185 175 164 153 142 130 + 119 108 97 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 106 118 130 141 153 164 175 185 195 204 + 212 219 226 231 236 239 241 242 242 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 244 243 241 238 233 228 222 214 206 197 188 177 167 156 144 133 + 121 110 98 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 106 117 129 141 152 164 174 185 194 203 + 212 219 226 231 236 239 242 243 244 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 244 244 242 239 235 230 223 216 208 200 190 180 169 158 147 135 + 123 112 100 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 105 117 128 140 151 162 173 183 193 202 + 211 218 225 231 236 239 242 244 244 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 244 244 242 239 236 231 225 218 210 201 192 182 171 160 149 137 + 125 113 102 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 104 116 127 139 150 161 171 182 191 200 + 209 217 223 229 234 238 241 243 244 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 243 243 242 239 236 231 226 219 211 203 193 183 173 162 150 138 + 127 115 103 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 103 114 126 137 148 159 169 179 189 198 + 207 214 221 227 233 237 240 242 243 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 242 242 241 239 235 231 226 219 212 203 194 184 174 163 151 140 + 128 116 104 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 101 112 123 134 145 156 166 176 186 195 + 204 211 218 225 230 235 238 240 242 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 240 239 237 234 230 225 219 212 204 195 185 175 164 152 141 + 129 117 105 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 100 110 121 132 142 153 163 173 182 191 + 200 208 215 221 227 231 235 238 239 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 236 237 237 235 233 229 224 218 211 203 195 185 175 164 153 141 + 129 117 106 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 97 108 118 128 139 149 159 168 178 187 + 195 203 210 217 223 228 231 234 236 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 232 233 233 232 230 227 222 216 210 202 194 185 175 164 153 141 + 130 118 106 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 95 105 115 125 135 144 154 163 173 181 + 190 198 205 212 218 223 227 230 232 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 227 229 229 228 227 224 219 214 208 201 192 183 174 163 152 141 + 129 118 106 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 102 111 121 130 139 149 158 167 175 + 184 192 199 206 212 217 221 225 227 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 222 224 224 224 222 220 216 211 205 198 190 182 172 162 151 140 + 129 117 105 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 99 107 116 125 134 143 152 160 169 + 177 185 192 199 205 210 215 219 222 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 217 218 218 217 215 212 207 202 195 188 179 170 160 150 139 + 128 116 105 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 95 104 112 120 128 137 145 153 161 + 169 177 184 191 197 203 208 212 215 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 207 210 212 212 211 210 207 203 198 192 184 176 168 158 148 137 + 126 115 104 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 100 107 115 122 130 138 145 153 + 161 168 175 182 188 194 199 204 207 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 199 202 204 205 205 203 201 197 193 187 180 173 164 155 145 135 + 124 113 102 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 96 103 109 116 123 130 137 144 + 151 159 166 172 179 185 190 195 199 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 189 192 195 196 197 196 194 191 187 182 176 169 161 152 142 132 + 122 111 101 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 93 99 104 110 116 122 128 135 + 142 148 155 162 168 174 180 185 189 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 178 182 185 187 188 188 187 184 181 176 170 164 156 148 139 129 + 119 109 99 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 95 100 104 109 114 119 125 + 131 137 144 150 156 163 168 174 178 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 171 174 177 178 179 178 176 173 169 164 158 151 143 135 126 + 116 106 96 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 96 99 103 106 110 115 + 120 126 131 137 144 150 156 161 166 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 153 158 162 165 167 169 169 167 165 162 157 152 146 138 130 122 + 113 104 94 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 94 96 97 99 102 105 + 109 113 118 124 130 136 142 148 153 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 139 144 149 153 156 158 158 158 156 154 150 145 140 133 126 118 + 109 100 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 95 + 97 101 105 110 115 121 127 133 139 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 129 135 139 143 146 147 148 147 145 142 138 133 127 120 113 + 105 97 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 99 105 111 117 123 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 107 113 119 124 129 133 135 136 137 136 134 130 126 121 115 109 + 102 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 100 107 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 96 102 109 114 119 122 125 126 126 125 122 119 115 110 104 + 98 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 98 104 109 113 115 116 116 114 112 109 105 100 + 95 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 95 100 104 106 107 107 105 103 100 96 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 96 99 100 100 99 96 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 93 94 95 94 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 3 END OF TEC MAP + END OF FILE diff --git a/src/test/resources/ionex/split-2.19i b/src/test/resources/ionex/split-2.19i new file mode 100644 index 0000000000..02fd6ac839 --- /dev/null +++ b/src/test/resources/ionex/split-2.19i @@ -0,0 +1,1306 @@ + 1.0 IONOSPHERE MAPS GPS IONEX VERSION / TYPE +BIMINX V5.3 AIUB 16-JAN-19 07:26 PGM / RUN BY / DATE +BROADCAST IONOSPHERE MODEL FOR DAY 015, 2019 COMMENT + 2019 1 15 2 0 0 EPOCH OF FIRST MAP + 2019 1 15 4 0 0 EPOCH OF LAST MAP + 3600 INTERVAL + 3 # OF MAPS IN FILE + NONE MAPPING FUNCTION + 0.0 ELEVATION CUTOFF + OBSERVABLES USED + 6371.0 BASE RADIUS + 2 MAP DIMENSION + 350.0 350.0 0.0 HGT1 / HGT2 / DHGT + 87.5 -87.5 -2.5 LAT1 / LAT2 / DLAT + -180.0 180.0 5.0 LON1 / LON2 / DLON + -1 EXPONENT +TEC/RMS values in 0.1 TECU; 9999, if no value available COMMENT + END OF HEADER + 1 START OF TEC MAP + 2019 1 15 2 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 93 94 94 94 + 93 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 94 95 95 96 96 + 96 96 95 95 94 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 94 95 96 97 98 98 + 99 99 98 98 97 96 95 94 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 96 95 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 94 96 97 99 100 101 + 101 102 102 101 101 100 99 97 96 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 100 98 97 95 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 95 97 99 101 102 104 + 105 105 105 105 105 104 103 101 100 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 103 101 99 97 96 94 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 95 98 101 103 105 107 + 108 109 110 110 109 108 107 106 104 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 109 107 105 103 101 99 97 96 94 93 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 96 99 102 105 108 110 + 112 113 114 114 114 113 112 111 109 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 114 112 110 107 105 103 101 99 97 96 94 94 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 97 101 104 108 111 113 + 115 117 118 119 119 118 117 116 114 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 119 117 115 112 109 107 104 102 100 98 96 95 94 93 93 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 97 102 106 110 114 117 + 119 121 123 124 124 123 123 121 119 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 125 122 120 117 114 111 108 106 103 101 99 97 95 94 93 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 98 103 108 112 117 120 + 123 126 128 129 129 129 128 127 125 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 131 128 125 122 119 116 112 109 106 103 101 99 97 95 94 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 95 100 105 111 116 120 124 + 128 130 133 134 135 135 134 132 131 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 136 134 131 128 124 121 117 113 110 106 103 101 98 96 94 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 96 102 108 114 119 124 129 + 132 136 138 140 140 140 140 138 136 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 143 140 137 133 130 126 121 117 113 110 106 103 100 97 95 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 98 105 111 117 123 129 133 + 137 141 143 145 146 146 146 145 143 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 146 143 139 135 131 126 122 117 113 109 105 102 98 95 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 93 100 107 114 121 127 133 138 + 142 146 149 151 152 153 152 151 149 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 155 152 149 145 141 136 131 126 121 116 112 107 103 100 96 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 95 103 110 118 125 131 137 143 + 148 152 155 157 158 159 158 157 155 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 161 159 155 151 147 142 136 131 125 120 115 110 105 101 97 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 98 105 113 121 129 136 142 148 + 153 157 160 163 164 165 165 163 161 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 168 165 162 157 153 147 142 136 130 124 118 112 107 102 97 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 100 108 117 125 133 140 147 153 + 158 162 166 169 170 171 171 170 168 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 174 171 168 164 159 153 147 141 135 128 122 115 109 103 98 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 93 102 111 120 128 137 144 151 158 + 163 168 172 174 176 177 177 176 174 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 180 178 174 170 165 159 153 147 140 133 126 119 112 106 100 94 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 96 105 114 123 132 141 149 156 162 + 168 173 177 180 182 183 183 182 180 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 187 184 180 176 171 165 159 152 145 138 130 123 116 109 102 96 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 98 107 117 127 136 145 153 160 167 + 173 178 183 186 188 189 189 188 187 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 190 186 182 177 171 164 157 150 143 135 127 119 112 105 98 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 100 110 120 130 139 148 157 165 172 + 178 183 188 191 194 195 195 194 193 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 199 196 192 188 183 177 170 163 155 147 139 131 123 115 107 100 + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 102 113 123 133 143 152 161 169 176 + 183 188 193 197 199 201 201 200 199 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 204 202 198 194 188 182 176 168 160 152 144 135 127 118 110 102 + 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 104 115 126 136 146 156 165 173 181 + 187 193 198 202 204 206 206 206 204 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 210 207 204 199 194 188 181 174 166 157 149 140 131 122 113 105 + 96 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 96 107 118 128 139 149 159 168 177 185 + 191 197 202 206 209 211 212 211 210 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 213 209 205 199 193 186 179 171 162 153 144 135 126 116 107 + 99 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 98 109 120 131 142 152 162 172 180 188 + 195 202 207 211 214 216 217 216 215 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 218 214 210 205 198 191 184 176 167 158 148 139 129 120 110 + 101 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 99 111 122 133 144 155 165 175 184 192 + 199 205 211 215 218 220 221 221 220 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 222 219 215 209 203 196 189 180 172 162 153 143 133 123 113 + 104 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 101 112 124 135 147 157 168 178 187 195 + 202 209 214 219 222 224 225 225 224 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 229 226 223 219 214 208 201 193 185 176 167 157 147 136 126 116 + 106 96 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 102 114 126 137 149 160 170 180 189 197 + 205 212 217 222 225 228 229 229 229 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 232 230 227 223 218 212 205 198 189 180 171 161 151 140 130 119 + 109 99 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 104 115 127 139 150 161 172 182 191 200 + 207 214 220 225 229 231 233 233 232 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 236 234 231 227 222 216 209 202 193 184 175 165 154 144 133 122 + 111 101 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 105 116 128 140 152 163 173 183 193 201 + 209 216 222 227 231 234 236 236 236 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 238 237 234 230 226 220 213 206 197 188 178 168 158 147 136 125 + 114 103 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 105 117 129 141 152 164 174 184 194 203 + 211 218 224 229 233 236 238 239 238 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 241 239 237 233 229 223 216 209 201 191 182 172 161 150 139 128 + 116 105 95 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 106 118 130 141 153 164 175 185 195 203 + 212 219 225 230 235 238 240 241 241 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 242 241 239 236 231 226 219 212 204 195 185 175 164 153 142 130 + 119 108 97 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 106 118 130 141 153 164 175 185 195 204 + 212 219 226 231 236 239 241 242 242 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 244 243 241 238 233 228 222 214 206 197 188 177 167 156 144 133 + 121 110 98 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 106 117 129 141 152 164 174 185 194 203 + 212 219 226 231 236 239 242 243 244 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 244 244 242 239 235 230 223 216 208 200 190 180 169 158 147 135 + 123 112 100 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 105 117 128 140 151 162 173 183 193 202 + 211 218 225 231 236 239 242 244 244 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 244 244 242 239 236 231 225 218 210 201 192 182 171 160 149 137 + 125 113 102 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 104 116 127 139 150 161 171 182 191 200 + 209 217 223 229 234 238 241 243 244 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 243 243 242 239 236 231 226 219 211 203 193 183 173 162 150 138 + 127 115 103 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 103 114 126 137 148 159 169 179 189 198 + 207 214 221 227 233 237 240 242 243 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 242 242 241 239 235 231 226 219 212 203 194 184 174 163 151 140 + 128 116 104 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 101 112 123 134 145 156 166 176 186 195 + 204 211 218 225 230 235 238 240 242 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 240 239 237 234 230 225 219 212 204 195 185 175 164 152 141 + 129 117 105 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 100 110 121 132 142 153 163 173 182 191 + 200 208 215 221 227 231 235 238 239 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 236 237 237 235 233 229 224 218 211 203 195 185 175 164 153 141 + 129 117 106 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 97 108 118 128 139 149 159 168 178 187 + 195 203 210 217 223 228 231 234 236 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 232 233 233 232 230 227 222 216 210 202 194 185 175 164 153 141 + 130 118 106 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 95 105 115 125 135 144 154 163 173 181 + 190 198 205 212 218 223 227 230 232 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 227 229 229 228 227 224 219 214 208 201 192 183 174 163 152 141 + 129 118 106 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 102 111 121 130 139 149 158 167 175 + 184 192 199 206 212 217 221 225 227 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 222 224 224 224 222 220 216 211 205 198 190 182 172 162 151 140 + 129 117 105 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 99 107 116 125 134 143 152 160 169 + 177 185 192 199 205 210 215 219 222 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 217 218 218 217 215 212 207 202 195 188 179 170 160 150 139 + 128 116 105 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 95 104 112 120 128 137 145 153 161 + 169 177 184 191 197 203 208 212 215 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 207 210 212 212 211 210 207 203 198 192 184 176 168 158 148 137 + 126 115 104 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 100 107 115 122 130 138 145 153 + 161 168 175 182 188 194 199 204 207 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 199 202 204 205 205 203 201 197 193 187 180 173 164 155 145 135 + 124 113 102 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 96 103 109 116 123 130 137 144 + 151 159 166 172 179 185 190 195 199 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 189 192 195 196 197 196 194 191 187 182 176 169 161 152 142 132 + 122 111 101 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 93 99 104 110 116 122 128 135 + 142 148 155 162 168 174 180 185 189 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 178 182 185 187 188 188 187 184 181 176 170 164 156 148 139 129 + 119 109 99 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 95 100 104 109 114 119 125 + 131 137 144 150 156 163 168 174 178 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 171 174 177 178 179 178 176 173 169 164 158 151 143 135 126 + 116 106 96 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 96 99 103 106 110 115 + 120 126 131 137 144 150 156 161 166 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 153 158 162 165 167 169 169 167 165 162 157 152 146 138 130 122 + 113 104 94 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 94 96 97 99 102 105 + 109 113 118 124 130 136 142 148 153 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 139 144 149 153 156 158 158 158 156 154 150 145 140 133 126 118 + 109 100 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 95 + 97 101 105 110 115 121 127 133 139 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 129 135 139 143 146 147 148 147 145 142 138 133 127 120 113 + 105 97 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 99 105 111 117 123 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 107 113 119 124 129 133 135 136 137 136 134 130 126 121 115 109 + 102 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 100 107 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 96 102 109 114 119 122 125 126 126 125 122 119 115 110 104 + 98 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 98 104 109 113 115 116 116 114 112 109 105 100 + 95 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 95 100 104 106 107 107 105 103 100 96 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 96 99 100 100 99 96 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 93 94 95 94 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 1 END OF TEC MAP + 2 START OF TEC MAP + 2019 1 15 3 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 93 93 93 94 94 94 94 94 94 + 94 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 93 94 95 95 96 97 97 97 97 + 97 96 96 95 94 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 94 95 96 97 98 99 100 100 100 + 100 100 99 99 97 96 95 94 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 96 95 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 94 96 97 99 101 102 103 104 104 + 104 104 103 103 102 100 99 97 96 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 100 98 96 95 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 95 97 99 101 103 105 106 107 108 + 108 108 108 107 106 105 103 101 100 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 102 100 98 96 95 94 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 95 98 101 103 106 108 110 111 112 + 113 113 113 112 111 109 108 106 104 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 108 106 104 101 99 98 96 95 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 96 99 102 105 108 111 113 115 117 + 117 118 118 117 116 114 112 110 108 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 113 110 108 105 103 100 98 97 95 94 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 97 100 104 108 111 114 117 119 121 + 122 123 123 122 121 120 118 115 113 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 118 115 112 109 106 104 101 99 97 95 94 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 97 102 106 110 114 118 121 124 126 + 127 128 128 128 127 125 123 121 118 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 120 117 113 110 107 104 101 99 97 95 94 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 98 103 108 113 117 122 125 128 131 + 133 134 134 134 132 131 129 126 123 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 129 125 122 118 114 110 107 104 101 98 96 94 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 100 105 111 116 121 126 130 133 136 + 138 139 140 139 138 137 135 132 129 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 134 131 127 122 118 114 110 106 103 100 97 95 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 96 102 108 114 120 125 130 135 138 141 + 144 145 146 146 145 143 141 138 134 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 140 136 132 127 123 118 114 109 105 102 98 95 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 98 104 111 117 124 130 135 140 144 147 + 149 151 152 152 151 149 147 144 140 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 146 142 137 132 127 122 117 112 108 103 99 96 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 100 107 114 121 128 134 140 145 149 153 + 155 157 158 158 157 155 153 150 146 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 152 148 143 137 132 126 121 115 110 105 101 97 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 95 102 110 117 125 132 139 145 150 154 158 + 161 163 164 164 163 162 159 156 152 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 158 154 149 143 137 131 125 118 113 107 102 97 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 97 105 113 121 129 136 143 149 155 160 164 + 167 169 170 171 170 168 166 162 158 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 160 154 148 142 136 129 122 116 109 103 98 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 99 107 116 125 133 141 148 154 160 165 170 + 173 175 176 177 176 175 172 169 165 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 171 166 160 154 148 141 134 127 119 113 106 100 94 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 101 110 119 128 137 145 152 159 165 171 175 + 179 181 182 183 182 181 178 175 171 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 177 172 166 160 153 146 138 131 123 116 109 102 95 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 94 104 113 123 132 141 149 157 164 170 176 181 + 184 187 188 189 188 187 185 181 177 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 178 172 165 158 151 143 135 127 119 112 104 97 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 97 106 116 126 135 145 153 161 169 175 181 186 + 190 192 194 195 194 193 191 187 183 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 189 184 178 171 164 156 148 140 132 123 115 107 99 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 99 109 119 129 139 149 157 166 173 180 186 191 + 195 198 200 201 200 199 197 193 189 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 195 189 183 177 169 161 153 145 136 127 118 110 102 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 101 112 122 132 143 152 161 170 178 185 191 196 + 200 203 205 206 206 205 202 199 195 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 200 195 189 182 175 167 158 149 140 131 122 113 104 96 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 103 114 125 136 146 156 165 174 182 189 195 201 + 205 208 210 211 211 210 208 204 200 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 206 200 194 187 180 172 163 154 144 135 125 116 107 98 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 106 117 128 139 149 159 169 178 186 193 200 205 + 210 213 215 216 216 215 213 210 206 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 205 199 192 185 176 168 158 149 139 129 119 110 100 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 97 108 119 130 141 152 162 172 181 190 197 204 209 + 214 217 220 221 221 220 218 215 211 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 210 204 197 190 181 172 163 153 143 133 123 113 103 94 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 98 110 121 133 144 155 165 175 184 193 200 207 213 + 218 221 224 225 225 225 223 220 215 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 215 209 202 194 186 177 167 157 147 136 126 116 105 96 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 100 112 123 135 146 157 168 178 187 196 203 210 216 + 221 225 227 229 229 229 227 224 220 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 219 213 206 199 190 181 171 161 151 140 129 119 108 98 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 102 113 125 137 148 159 170 180 190 198 206 213 219 + 224 228 231 232 233 232 231 228 224 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 228 223 217 210 203 194 185 175 165 154 143 132 122 111 100 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 103 115 127 138 150 161 172 182 192 200 208 215 221 + 227 231 234 235 236 236 234 232 228 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 231 226 221 214 206 198 189 179 168 158 147 136 124 113 102 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 104 116 128 140 151 163 173 184 193 202 210 217 223 + 229 233 236 238 239 239 237 235 231 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 234 229 224 217 209 201 192 182 172 161 150 139 127 116 105 94 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 105 117 129 141 152 164 174 185 194 203 211 218 225 + 230 234 238 240 241 241 240 237 234 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 236 232 226 220 212 204 195 185 175 164 153 141 130 118 107 96 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 94 106 117 129 141 153 164 175 185 195 204 212 219 226 + 231 235 239 241 242 243 242 239 236 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 238 234 228 222 215 207 198 188 178 167 156 144 132 121 109 98 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 94 106 118 130 141 153 164 175 185 195 204 212 219 226 + 231 236 239 242 243 244 243 241 238 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 235 230 224 217 209 200 190 180 169 158 146 135 123 111 99 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 94 106 118 129 141 152 164 174 184 194 203 211 219 225 + 231 236 239 242 244 244 244 242 239 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 236 231 225 218 210 202 192 182 171 160 148 136 125 113 101 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 94 105 117 129 140 152 163 173 183 193 202 210 217 224 + 230 235 239 241 243 244 244 242 239 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 236 231 226 219 211 203 193 183 173 162 150 138 126 114 102 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 105 116 128 139 150 161 171 181 191 200 208 215 222 + 228 233 237 240 242 243 243 242 239 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 239 235 231 226 219 212 204 194 184 174 163 151 140 128 116 104 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 104 115 126 137 148 159 169 179 188 197 205 213 220 + 226 231 235 238 240 242 242 241 239 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 237 234 230 225 219 212 204 195 185 175 164 152 141 129 117 105 + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 102 113 124 135 146 156 166 176 185 194 202 209 216 + 222 228 232 235 238 239 240 239 237 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 235 232 228 224 218 211 203 195 185 175 164 153 141 129 117 105 + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 101 111 122 132 143 153 163 172 181 190 198 205 212 + 218 224 228 232 234 236 237 236 235 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 232 229 226 222 216 210 202 194 184 175 164 153 141 130 118 106 + 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 99 109 119 129 139 149 159 168 177 185 193 200 207 + 213 219 223 227 230 232 233 233 232 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 228 226 223 219 214 207 200 192 183 174 163 152 141 130 118 106 + 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 96 106 116 126 135 145 154 163 171 180 187 195 201 + 208 213 218 222 225 227 228 229 228 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 223 221 219 215 210 205 198 190 182 172 162 152 140 129 117 106 + 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 94 103 113 122 131 140 149 157 165 173 181 188 195 + 201 206 211 216 219 221 223 223 223 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 217 216 214 211 207 201 195 187 179 170 161 150 139 128 117 105 + 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 100 109 118 126 135 143 151 159 166 174 181 187 + 193 199 204 208 212 215 217 217 217 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 210 208 206 202 197 191 184 176 168 158 148 138 127 116 104 + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 97 105 113 121 129 136 144 151 159 166 172 179 + 185 190 195 200 204 207 209 210 211 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 203 202 200 196 192 186 180 173 164 155 146 136 125 114 103 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 94 101 109 116 123 130 137 143 150 157 163 169 + 175 181 186 191 195 198 201 203 203 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 195 195 194 193 190 186 181 175 168 161 152 143 133 123 112 101 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 98 104 110 117 123 129 135 141 147 153 159 + 165 170 176 180 185 188 191 194 195 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 185 186 186 185 183 179 175 170 163 156 148 139 130 120 110 100 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 94 100 105 111 116 121 126 132 137 142 148 + 153 159 164 169 174 178 181 184 185 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 176 177 176 175 172 168 163 158 151 144 135 126 117 107 97 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 96 100 105 109 113 117 122 126 131 136 + 141 146 151 156 161 166 169 172 175 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 163 165 166 167 166 164 161 157 151 145 139 131 123 114 105 95 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 96 100 102 105 108 112 115 119 123 + 128 133 138 143 148 152 157 160 163 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 151 153 155 156 156 155 152 149 145 139 133 126 118 110 101 93 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 94 96 97 99 100 102 104 107 110 + 114 118 123 128 133 138 143 147 151 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 137 140 143 145 145 145 144 141 137 133 127 121 114 106 98 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 96 + 99 103 107 112 117 122 128 132 137 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 122 126 130 133 134 135 134 133 130 126 121 116 109 103 95 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 100 106 111 117 122 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 106 111 116 120 122 124 124 124 122 119 115 110 105 99 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 100 106 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 95 101 106 110 113 114 115 114 112 109 105 101 95 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 97 102 105 106 106 105 103 100 97 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 94 97 99 100 99 96 94 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 94 95 94 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 2 END OF TEC MAP + 3 START OF TEC MAP + 2019 1 15 4 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 93 94 94 94 95 95 95 95 95 94 + 94 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 93 94 94 95 96 97 97 98 98 98 98 98 + 97 97 96 95 94 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 93 94 96 97 98 99 100 101 102 102 102 102 + 101 101 100 99 97 96 95 94 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 95 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 94 95 97 99 100 102 103 104 105 106 106 106 + 106 105 104 103 101 100 98 97 95 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 99 97 95 94 93 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 94 96 99 101 103 105 107 108 109 110 111 111 + 110 110 109 107 106 104 102 100 99 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 102 100 98 96 95 94 93 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 95 98 100 103 105 108 110 112 114 115 115 116 + 115 115 114 112 110 108 106 104 102 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 106 103 101 99 97 96 94 93 93 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 96 99 102 105 108 111 114 116 118 120 120 121 + 121 120 119 117 115 113 111 108 106 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 110 107 104 102 99 97 96 94 93 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 96 100 104 107 111 115 118 120 123 124 126 126 + 126 126 124 123 121 118 116 113 110 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 114 111 108 105 102 99 97 95 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 97 101 106 110 114 118 122 125 128 130 131 132 + 132 131 130 128 126 123 121 117 114 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 119 115 111 108 104 101 99 96 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 98 103 107 112 117 122 126 129 132 135 137 138 + 138 137 136 134 132 129 126 122 119 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 119 115 111 107 103 100 97 95 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 99 104 110 115 121 126 130 134 138 140 142 143 + 144 143 142 140 138 135 131 127 123 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 128 123 119 114 110 106 102 98 95 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 101 107 113 119 125 130 135 140 143 146 148 149 + 150 150 148 146 144 140 137 132 128 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 133 128 123 118 113 108 104 100 96 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 97 103 110 117 123 129 135 140 145 149 152 154 156 + 156 156 155 153 150 146 142 138 133 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 138 133 127 121 116 110 105 101 97 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 99 106 113 120 127 134 140 145 150 154 158 160 162 + 162 162 161 159 156 153 148 144 138 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 144 138 132 125 119 113 107 102 97 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 101 108 116 124 131 138 145 151 156 160 164 166 168 + 169 169 167 165 162 159 154 149 144 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 143 137 130 123 116 110 104 98 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 95 103 111 120 128 135 143 150 156 161 166 170 172 174 + 175 175 174 172 169 165 160 155 149 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 155 149 142 135 127 120 113 106 100 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 97 106 115 123 132 140 147 155 161 167 171 175 178 180 + 181 181 180 178 175 171 166 161 155 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 161 154 147 139 132 124 116 109 102 95 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 100 109 118 127 136 144 152 159 166 172 177 181 184 186 + 187 187 186 184 181 177 172 167 161 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 159 152 144 136 128 120 112 105 97 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 102 112 121 130 140 148 157 164 171 177 182 187 190 192 + 193 193 192 190 187 183 178 173 166 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 172 165 157 149 141 132 124 115 107 99 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 95 105 114 124 134 144 153 161 169 176 182 188 192 195 198 + 199 199 198 196 193 189 184 179 172 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 178 170 162 154 145 137 128 119 110 102 94 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 97 107 117 128 138 147 157 165 174 181 187 193 197 201 203 + 205 205 204 202 199 195 190 184 178 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 176 168 159 150 141 132 122 113 104 96 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 99 110 120 131 141 151 161 170 178 185 192 198 202 206 208 + 210 210 209 207 205 201 196 190 183 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 188 181 173 164 155 145 136 126 116 107 98 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 102 112 123 134 145 155 165 174 182 190 196 202 207 211 213 + 215 215 215 213 210 206 201 195 188 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 186 177 169 159 149 140 130 120 110 100 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 104 115 126 137 148 158 168 177 186 194 201 207 211 215 218 + 220 220 219 218 215 211 206 200 193 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 198 191 182 173 164 154 143 133 123 113 103 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 95 106 117 129 140 151 162 172 181 190 198 204 210 215 219 222 + 224 225 224 222 220 216 211 205 198 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 195 187 178 168 158 147 137 126 116 105 96 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 97 108 120 131 143 154 165 175 184 193 201 208 214 219 223 226 + 228 229 228 227 224 220 215 209 203 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 207 199 191 182 172 162 151 140 129 119 108 98 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 99 110 122 134 145 156 167 177 187 196 204 211 217 222 227 230 + 232 232 232 231 228 224 219 214 207 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 203 195 186 176 166 155 144 133 122 111 100 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 100 112 124 136 147 159 170 180 189 198 206 214 220 225 229 233 + 235 236 235 234 232 228 223 218 211 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 214 207 198 189 179 169 158 147 136 124 113 102 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 102 114 126 138 149 161 171 182 191 200 209 216 222 228 232 235 + 237 238 238 237 235 231 227 221 214 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 218 210 202 193 183 172 162 150 139 127 116 104 94 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 103 115 127 139 151 162 173 183 193 202 210 217 224 229 234 237 + 239 241 241 239 237 234 230 224 218 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 213 205 196 186 175 164 153 142 130 118 107 95 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 104 116 128 140 152 163 174 184 194 203 211 219 225 231 235 239 + 241 242 242 241 239 236 232 227 220 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 222 215 207 198 188 178 167 156 144 132 120 109 97 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 105 117 129 141 153 164 175 185 195 204 212 219 226 231 236 239 + 242 243 244 243 241 238 234 229 222 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 217 209 200 191 180 169 158 146 135 123 111 99 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 106 118 130 141 153 164 175 185 195 204 212 219 226 231 236 239 + 242 244 244 244 242 239 235 230 224 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 225 218 211 202 192 182 171 160 148 137 125 112 101 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 106 118 130 141 153 164 174 185 194 203 211 218 225 231 235 239 + 242 243 244 244 242 239 236 231 225 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 226 219 211 203 194 184 173 162 150 138 126 114 102 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 106 118 129 141 152 163 173 183 193 202 210 217 224 229 234 238 + 241 242 243 243 242 239 236 231 226 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 226 219 212 204 194 185 174 163 151 140 128 115 103 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 105 117 128 140 151 162 172 182 191 200 208 215 221 227 232 236 + 239 241 242 242 241 238 235 231 226 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 225 219 212 204 195 185 175 164 152 141 129 116 104 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 105 116 127 138 149 160 170 179 189 197 205 212 219 224 229 233 + 236 238 240 240 239 237 234 230 225 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 223 218 211 203 194 185 175 164 153 141 129 117 105 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 103 115 126 136 147 157 167 176 185 194 201 209 215 221 226 230 + 233 235 236 237 236 235 232 228 223 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 221 216 209 202 193 184 174 164 153 141 130 118 106 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 102 113 124 134 144 154 164 173 182 190 197 204 211 216 221 225 + 229 231 233 233 233 231 229 226 221 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 218 213 207 200 192 183 174 163 152 141 130 118 106 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 100 111 121 131 141 151 160 169 177 185 192 199 205 211 216 220 + 223 226 228 229 229 228 225 222 218 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 210 204 197 190 181 172 162 151 140 129 117 106 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 98 108 118 128 137 146 155 164 172 179 186 193 199 205 210 214 + 217 220 222 223 223 223 221 218 215 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 210 206 200 194 187 179 170 160 150 139 128 117 105 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 96 105 115 124 133 142 150 158 166 173 180 186 192 198 202 207 + 210 213 216 217 217 217 216 213 210 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 205 201 196 190 183 176 167 158 148 138 127 116 104 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 94 102 111 120 128 137 144 152 159 166 173 179 184 190 194 199 + 202 206 208 210 210 210 209 208 205 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 198 195 191 185 179 172 164 155 146 136 125 114 103 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 99 108 116 123 131 138 145 152 158 164 170 176 181 185 190 + 193 197 199 201 203 203 202 201 198 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 191 189 185 180 174 167 160 152 143 133 123 112 102 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 96 104 111 118 125 132 138 144 150 155 161 166 171 175 180 + 183 187 190 192 194 194 194 193 191 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 181 178 174 168 162 155 148 139 130 120 110 100 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 100 107 113 119 125 130 136 141 146 151 155 160 164 168 + 172 176 179 182 184 185 185 185 183 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 173 170 167 162 157 150 143 135 126 117 108 98 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 96 102 108 113 118 122 127 131 136 140 144 148 152 156 + 160 164 167 170 172 174 175 175 175 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 164 162 159 155 150 145 138 131 122 114 105 95 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 98 103 107 111 114 118 121 125 128 132 135 139 143 + 147 151 154 157 160 163 164 165 165 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 154 154 153 151 148 143 138 132 126 118 110 102 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 98 101 104 107 109 111 114 116 119 122 125 129 + 132 136 140 144 147 150 152 154 154 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 143 143 143 142 139 136 132 127 121 114 106 99 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 95 97 99 100 101 102 102 104 105 107 110 113 + 117 121 125 129 132 136 139 141 143 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 130 132 133 132 131 128 125 121 115 109 103 95 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 93 94 93 93 92 92 92 92 92 92 94 97 + 100 104 108 112 117 121 125 128 130 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 117 120 122 122 122 120 118 114 110 105 99 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 100 105 110 114 117 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 108 111 112 113 112 111 108 105 100 95 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 99 104 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 95 99 102 104 105 104 103 100 97 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 96 98 99 98 96 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 93 95 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 3 END OF TEC MAP + END OF FILE diff --git a/src/test/resources/ionex/split-3.19i b/src/test/resources/ionex/split-3.19i new file mode 100644 index 0000000000..6629b07421 --- /dev/null +++ b/src/test/resources/ionex/split-3.19i @@ -0,0 +1,1306 @@ + 1.0 IONOSPHERE MAPS GPS IONEX VERSION / TYPE +BIMINX V5.3 AIUB 16-JAN-19 07:26 PGM / RUN BY / DATE +BROADCAST IONOSPHERE MODEL FOR DAY 015, 2019 COMMENT + 2019 1 15 4 0 0 EPOCH OF FIRST MAP + 2019 1 15 6 0 0 EPOCH OF LAST MAP + 3600 INTERVAL + 3 # OF MAPS IN FILE + NONE MAPPING FUNCTION + 0.0 ELEVATION CUTOFF + OBSERVABLES USED + 6371.0 BASE RADIUS + 2 MAP DIMENSION + 350.0 350.0 0.0 HGT1 / HGT2 / DHGT + 87.5 -87.5 -2.5 LAT1 / LAT2 / DLAT + -180.0 180.0 5.0 LON1 / LON2 / DLON + -1 EXPONENT +TEC/RMS values in 0.1 TECU; 9999, if no value available COMMENT + END OF HEADER + 1 START OF TEC MAP + 2019 1 15 4 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 93 93 94 94 94 95 95 95 95 95 94 + 94 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 93 94 94 95 96 97 97 98 98 98 98 98 + 97 97 96 95 94 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 93 94 96 97 98 99 100 101 102 102 102 102 + 101 101 100 99 97 96 95 94 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 95 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 94 95 97 99 100 102 103 104 105 106 106 106 + 106 105 104 103 101 100 98 97 95 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 99 97 95 94 93 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 94 96 99 101 103 105 107 108 109 110 111 111 + 110 110 109 107 106 104 102 100 99 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 102 100 98 96 95 94 93 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 95 98 100 103 105 108 110 112 114 115 115 116 + 115 115 114 112 110 108 106 104 102 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 106 103 101 99 97 96 94 93 93 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 96 99 102 105 108 111 114 116 118 120 120 121 + 121 120 119 117 115 113 111 108 106 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 110 107 104 102 99 97 96 94 93 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 96 100 104 107 111 115 118 120 123 124 126 126 + 126 126 124 123 121 118 116 113 110 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 114 111 108 105 102 99 97 95 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 97 101 106 110 114 118 122 125 128 130 131 132 + 132 131 130 128 126 123 121 117 114 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 119 115 111 108 104 101 99 96 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 98 103 107 112 117 122 126 129 132 135 137 138 + 138 137 136 134 132 129 126 122 119 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 123 119 115 111 107 103 100 97 95 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 99 104 110 115 121 126 130 134 138 140 142 143 + 144 143 142 140 138 135 131 127 123 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 128 123 119 114 110 106 102 98 95 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 101 107 113 119 125 130 135 140 143 146 148 149 + 150 150 148 146 144 140 137 132 128 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 133 128 123 118 113 108 104 100 96 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 97 103 110 117 123 129 135 140 145 149 152 154 156 + 156 156 155 153 150 146 142 138 133 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 138 133 127 121 116 110 105 101 97 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 99 106 113 120 127 134 140 145 150 154 158 160 162 + 162 162 161 159 156 153 148 144 138 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 144 138 132 125 119 113 107 102 97 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 101 108 116 124 131 138 145 151 156 160 164 166 168 + 169 169 167 165 162 159 154 149 144 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 143 137 130 123 116 110 104 98 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 95 103 111 120 128 135 143 150 156 161 166 170 172 174 + 175 175 174 172 169 165 160 155 149 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 155 149 142 135 127 120 113 106 100 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 97 106 115 123 132 140 147 155 161 167 171 175 178 180 + 181 181 180 178 175 171 166 161 155 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 161 154 147 139 132 124 116 109 102 95 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 100 109 118 127 136 144 152 159 166 172 177 181 184 186 + 187 187 186 184 181 177 172 167 161 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 159 152 144 136 128 120 112 105 97 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 102 112 121 130 140 148 157 164 171 177 182 187 190 192 + 193 193 192 190 187 183 178 173 166 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 172 165 157 149 141 132 124 115 107 99 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 95 105 114 124 134 144 153 161 169 176 182 188 192 195 198 + 199 199 198 196 193 189 184 179 172 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 178 170 162 154 145 137 128 119 110 102 94 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 97 107 117 128 138 147 157 165 174 181 187 193 197 201 203 + 205 205 204 202 199 195 190 184 178 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 176 168 159 150 141 132 122 113 104 96 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 99 110 120 131 141 151 161 170 178 185 192 198 202 206 208 + 210 210 209 207 205 201 196 190 183 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 188 181 173 164 155 145 136 126 116 107 98 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 102 112 123 134 145 155 165 174 182 190 196 202 207 211 213 + 215 215 215 213 210 206 201 195 188 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 186 177 169 159 149 140 130 120 110 100 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 104 115 126 137 148 158 168 177 186 194 201 207 211 215 218 + 220 220 219 218 215 211 206 200 193 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 198 191 182 173 164 154 143 133 123 113 103 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 95 106 117 129 140 151 162 172 181 190 198 204 210 215 219 222 + 224 225 224 222 220 216 211 205 198 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 195 187 178 168 158 147 137 126 116 105 96 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 97 108 120 131 143 154 165 175 184 193 201 208 214 219 223 226 + 228 229 228 227 224 220 215 209 203 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 207 199 191 182 172 162 151 140 129 119 108 98 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 99 110 122 134 145 156 167 177 187 196 204 211 217 222 227 230 + 232 232 232 231 228 224 219 214 207 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 211 203 195 186 176 166 155 144 133 122 111 100 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 100 112 124 136 147 159 170 180 189 198 206 214 220 225 229 233 + 235 236 235 234 232 228 223 218 211 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 214 207 198 189 179 169 158 147 136 124 113 102 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 102 114 126 138 149 161 171 182 191 200 209 216 222 228 232 235 + 237 238 238 237 235 231 227 221 214 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 218 210 202 193 183 172 162 150 139 127 116 104 94 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 103 115 127 139 151 162 173 183 193 202 210 217 224 229 234 237 + 239 241 241 239 237 234 230 224 218 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 220 213 205 196 186 175 164 153 142 130 118 107 95 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 104 116 128 140 152 163 174 184 194 203 211 219 225 231 235 239 + 241 242 242 241 239 236 232 227 220 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 222 215 207 198 188 178 167 156 144 132 120 109 97 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 105 117 129 141 153 164 175 185 195 204 212 219 226 231 236 239 + 242 243 244 243 241 238 234 229 222 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 224 217 209 200 191 180 169 158 146 135 123 111 99 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 106 118 130 141 153 164 175 185 195 204 212 219 226 231 236 239 + 242 244 244 244 242 239 235 230 224 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 225 218 211 202 192 182 171 160 148 137 125 112 101 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 106 118 130 141 153 164 174 185 194 203 211 218 225 231 235 239 + 242 243 244 244 242 239 236 231 225 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 226 219 211 203 194 184 173 162 150 138 126 114 102 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 106 118 129 141 152 163 173 183 193 202 210 217 224 229 234 238 + 241 242 243 243 242 239 236 231 226 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 226 219 212 204 194 185 174 163 151 140 128 115 103 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 105 117 128 140 151 162 172 182 191 200 208 215 221 227 232 236 + 239 241 242 242 241 238 235 231 226 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 225 219 212 204 195 185 175 164 152 141 129 116 104 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 105 116 127 138 149 160 170 179 189 197 205 212 219 224 229 233 + 236 238 240 240 239 237 234 230 225 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 223 218 211 203 194 185 175 164 153 141 129 117 105 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 103 115 126 136 147 157 167 176 185 194 201 209 215 221 226 230 + 233 235 236 237 236 235 232 228 223 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 221 216 209 202 193 184 174 164 153 141 130 118 106 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 102 113 124 134 144 154 164 173 182 190 197 204 211 216 221 225 + 229 231 233 233 233 231 229 226 221 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 218 213 207 200 192 183 174 163 152 141 130 118 106 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 100 111 121 131 141 151 160 169 177 185 192 199 205 211 216 220 + 223 226 228 229 229 228 225 222 218 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 215 210 204 197 190 181 172 162 151 140 129 117 106 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 98 108 118 128 137 146 155 164 172 179 186 193 199 205 210 214 + 217 220 222 223 223 223 221 218 215 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 210 206 200 194 187 179 170 160 150 139 128 117 105 94 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 96 105 115 124 133 142 150 158 166 173 180 186 192 198 202 207 + 210 213 216 217 217 217 216 213 210 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 205 201 196 190 183 176 167 158 148 138 127 116 104 93 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 94 102 111 120 128 137 144 152 159 166 173 179 184 190 194 199 + 202 206 208 210 210 210 209 208 205 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 198 195 191 185 179 172 164 155 146 136 125 114 103 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 99 108 116 123 131 138 145 152 158 164 170 176 181 185 190 + 193 197 199 201 203 203 202 201 198 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 191 189 185 180 174 167 160 152 143 133 123 112 102 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 96 104 111 118 125 132 138 144 150 155 161 166 171 175 180 + 183 187 190 192 194 194 194 193 191 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 183 181 178 174 168 162 155 148 139 130 120 110 100 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 100 107 113 119 125 130 136 141 146 151 155 160 164 168 + 172 176 179 182 184 185 185 185 183 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 173 170 167 162 157 150 143 135 126 117 108 98 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 96 102 108 113 118 122 127 131 136 140 144 148 152 156 + 160 164 167 170 172 174 175 175 175 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 164 162 159 155 150 145 138 131 122 114 105 95 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 93 98 103 107 111 114 118 121 125 128 132 135 139 143 + 147 151 154 157 160 163 164 165 165 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 154 154 153 151 148 143 138 132 126 118 110 102 93 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 98 101 104 107 109 111 114 116 119 122 125 129 + 132 136 140 144 147 150 152 154 154 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 143 143 143 142 139 136 132 127 121 114 106 99 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 95 97 99 100 101 102 102 104 105 107 110 113 + 117 121 125 129 132 136 139 141 143 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 130 132 133 132 131 128 125 121 115 109 103 95 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 93 94 93 93 92 92 92 92 92 92 94 97 + 100 104 108 112 117 121 125 128 130 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 117 120 122 122 122 120 118 114 110 105 99 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 100 105 110 114 117 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 108 111 112 113 112 111 108 105 100 95 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 94 99 104 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 95 99 102 104 105 104 103 100 97 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 96 98 99 98 96 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 93 95 94 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 1 END OF TEC MAP + 2 START OF TEC MAP + 2019 1 15 5 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 93 93 94 94 95 95 95 96 96 95 95 95 + 94 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 93 94 94 95 96 97 98 98 99 99 99 99 99 98 + 98 97 96 95 94 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 94 95 96 97 98 100 101 102 103 103 103 103 103 102 + 102 101 99 98 97 96 94 93 93 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 95 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 93 95 96 98 99 101 103 104 105 107 107 108 108 107 107 + 106 105 103 102 100 99 97 96 95 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 97 95 94 93 93 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 94 96 98 100 102 104 106 108 109 111 112 112 113 112 112 + 111 109 108 106 104 102 100 98 97 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 99 97 96 95 93 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 95 97 99 102 104 107 110 112 114 115 117 117 118 117 117 + 116 114 113 110 108 106 104 101 99 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 102 100 98 96 94 93 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 95 98 101 104 107 110 113 116 118 120 122 123 123 123 122 + 121 120 118 115 113 110 107 105 102 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 105 102 100 97 95 94 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 96 99 103 106 110 114 117 120 123 125 127 128 129 129 128 + 127 125 123 120 117 114 111 108 105 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 108 105 102 99 96 94 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 97 100 104 109 113 117 121 125 128 130 132 134 134 134 134 + 133 131 128 125 122 119 115 112 108 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 111 108 104 100 97 95 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 97 102 106 111 116 121 125 129 133 136 138 140 140 140 140 + 138 136 134 131 127 123 120 115 111 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 115 110 106 102 99 96 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 98 103 108 114 119 125 130 134 138 141 144 145 146 147 146 + 145 142 140 136 132 128 124 119 115 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 118 113 109 104 100 96 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 99 105 111 117 123 129 134 139 143 147 150 152 153 153 152 + 151 149 146 142 138 133 128 123 118 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 122 116 111 106 101 97 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 95 101 108 115 121 128 134 139 145 149 153 156 158 159 159 159 + 157 155 152 148 144 139 133 128 122 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 126 120 114 108 102 98 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 97 104 111 118 125 132 138 145 150 155 159 162 164 165 166 165 + 164 161 158 154 149 144 138 132 126 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 131 124 117 110 104 98 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 99 106 114 122 129 136 143 150 155 160 164 168 170 171 172 171 + 170 167 164 160 155 150 144 137 131 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 135 128 121 114 107 100 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 + 101 109 117 125 133 141 148 155 161 166 170 174 176 178 178 178 + 176 174 170 166 161 155 149 142 135 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 140 133 125 117 110 102 96 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 95 + 104 112 121 129 138 146 153 160 166 172 176 180 182 184 184 184 + 182 180 176 172 167 161 155 148 140 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 145 137 129 121 113 105 98 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 98 + 106 115 124 133 142 150 158 165 172 177 182 186 188 190 190 190 + 189 186 182 178 173 167 160 153 145 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 150 142 133 125 116 108 100 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 100 + 109 119 128 137 146 155 163 170 177 183 187 191 194 196 196 196 + 195 192 188 184 179 172 165 158 150 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 155 146 137 128 119 111 102 94 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 102 + 112 122 132 141 151 159 168 175 182 188 193 197 200 201 202 202 + 200 198 194 190 184 178 171 163 155 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 160 151 142 132 123 114 105 96 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 95 105 + 115 125 135 145 155 164 172 180 187 193 198 202 205 207 208 207 + 206 203 200 195 190 183 176 168 160 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 156 146 136 127 117 107 98 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 97 107 + 118 128 139 149 159 168 177 185 192 198 203 207 210 212 213 213 + 211 209 205 201 195 189 181 173 165 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 169 160 150 140 130 120 110 101 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 99 110 + 121 132 142 153 163 172 181 189 196 202 208 212 215 217 218 218 + 216 214 210 206 200 194 186 178 169 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 174 164 155 144 134 123 113 103 94 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 102 113 + 124 135 146 156 167 176 185 193 200 207 212 216 219 222 223 222 + 221 219 215 211 205 198 191 183 174 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 178 169 159 148 138 127 116 106 96 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 104 115 + 127 138 149 160 170 180 189 197 204 211 216 220 224 226 227 227 + 225 223 220 215 209 203 196 187 178 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 182 173 163 152 141 130 119 108 98 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 95 106 118 + 129 141 152 163 173 183 192 200 208 214 220 224 227 230 231 231 + 229 227 224 219 214 207 200 191 182 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 186 177 166 156 144 133 122 111 100 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 97 108 120 + 132 143 155 166 176 186 195 203 211 217 223 227 231 233 234 234 + 233 231 227 223 218 211 204 195 186 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 190 180 170 159 148 136 125 114 102 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 99 110 122 + 134 146 157 168 179 189 198 206 213 220 226 230 234 236 237 237 + 236 234 231 226 221 215 207 199 190 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 183 173 162 151 139 128 116 105 94 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 100 112 124 + 136 148 159 170 181 191 200 208 216 222 228 232 236 238 240 240 + 239 237 234 229 224 218 210 202 193 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 196 186 176 165 154 142 130 118 107 95 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 102 114 126 + 138 150 161 172 183 192 202 210 217 224 230 234 238 240 241 242 + 241 239 236 232 227 220 213 205 196 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 199 189 179 168 156 145 133 121 109 97 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 103 115 127 + 139 151 163 174 184 194 203 211 219 225 231 235 239 241 243 243 + 242 241 238 234 229 222 215 207 199 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 201 191 181 170 159 147 135 123 111 99 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 104 116 128 + 140 152 164 174 185 195 204 212 219 226 231 236 239 242 244 244 + 243 242 239 235 230 224 217 209 201 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 202 193 183 172 160 149 137 125 113 101 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 105 117 129 + 141 153 164 175 185 195 204 212 219 226 231 236 239 242 244 244 + 244 242 239 236 231 225 218 211 202 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 194 184 173 162 150 138 126 114 102 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 106 118 130 + 141 153 164 175 185 194 203 211 218 225 230 235 239 241 243 244 + 243 242 239 236 231 226 219 212 203 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 204 195 185 174 163 152 140 128 116 103 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 106 118 130 + 141 153 164 174 184 193 202 210 217 223 229 234 237 240 242 242 + 242 241 239 235 231 226 219 212 204 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 204 195 185 175 164 152 141 129 117 104 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 106 118 129 + 141 152 163 173 183 192 200 208 215 221 227 231 235 238 240 240 + 240 239 237 234 230 225 219 212 204 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 203 194 185 175 164 153 141 129 117 105 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 94 105 117 128 + 140 151 161 171 181 190 198 205 212 218 224 228 232 235 237 238 + 238 237 235 232 228 223 217 211 203 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 202 193 184 174 164 153 141 130 118 106 94 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 105 116 127 + 138 149 159 169 178 187 195 202 209 215 220 225 228 231 233 234 + 234 234 232 229 226 221 215 209 202 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 200 192 183 173 163 152 141 130 118 106 94 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 104 115 125 + 136 146 156 166 175 183 191 198 205 210 216 220 224 226 228 230 + 230 230 228 226 222 218 213 207 200 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 197 189 181 172 162 151 140 129 117 106 94 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 102 113 123 + 134 144 153 162 171 179 186 193 199 205 210 214 218 221 223 224 + 225 225 223 221 218 214 209 204 197 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 193 186 178 169 160 150 139 128 117 105 94 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 100 111 121 + 131 140 149 158 166 174 181 188 194 199 204 208 212 214 217 218 + 219 219 218 216 213 210 205 200 193 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 189 183 175 167 157 148 137 127 116 104 93 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 98 108 118 + 127 136 145 153 161 168 175 181 187 192 197 201 204 207 209 211 + 212 212 211 210 208 204 200 195 189 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 184 178 171 163 154 145 135 125 114 103 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 96 105 115 + 123 132 140 148 155 162 168 174 179 184 188 192 196 199 201 203 + 204 204 204 203 201 198 194 190 184 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 179 173 166 159 151 142 132 123 112 102 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 94 102 111 + 119 127 135 142 149 155 161 166 171 175 179 183 186 189 192 193 + 195 195 195 195 193 191 188 184 179 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 173 167 161 154 147 138 129 120 110 100 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 99 107 + 115 122 129 136 142 147 152 157 162 166 169 173 176 179 181 183 + 185 186 186 186 185 183 180 177 173 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 165 161 155 149 142 134 126 117 107 98 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 96 103 + 110 117 123 129 134 139 143 148 151 155 158 162 164 167 170 172 + 173 175 176 176 175 174 172 169 165 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 158 154 149 143 137 130 122 113 105 95 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 100 + 106 112 117 122 126 130 134 137 141 144 146 149 152 154 157 159 + 161 163 164 165 165 164 163 161 158 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 146 142 137 131 125 118 110 102 93 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 96 + 101 106 111 115 118 121 124 127 129 131 134 136 138 141 143 145 + 148 150 151 153 154 154 153 152 149 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 140 138 135 130 125 120 113 106 98 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 + 97 101 105 108 110 112 114 116 117 118 120 122 123 126 128 130 + 133 135 138 140 141 142 142 142 140 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 131 129 127 124 119 114 109 102 95 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 94 97 100 102 103 104 104 105 105 105 105 106 108 109 112 114 + 117 120 123 126 128 130 131 131 131 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 121 120 119 116 113 109 104 98 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 95 96 96 96 95 94 93 92 92 92 92 92 92 94 97 + 100 103 107 111 114 117 119 120 121 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 111 111 111 109 107 104 100 95 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 95 99 103 107 109 111 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 101 103 104 103 102 99 96 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 98 101 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 94 97 98 98 96 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 94 94 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 2 END OF TEC MAP + 3 START OF TEC MAP + 2019 1 15 6 0 0 EPOCH OF CURRENT MAP + 87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 93 93 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 93 93 94 94 95 95 96 96 96 96 95 95 94 + 94 93 92 92 92 92 92 92 92 + 70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 93 93 94 95 96 97 97 98 99 99 100 100 100 99 99 98 + 97 96 95 94 93 93 92 92 92 + 67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 93 + 94 95 96 97 98 100 101 102 103 103 104 104 104 103 103 102 + 101 100 98 97 96 95 94 93 92 + 65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 93 94 + 95 96 98 100 101 103 104 106 107 108 108 109 108 108 107 106 + 105 103 102 100 99 97 96 94 94 + 62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 95 94 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 94 95 + 96 98 100 102 104 106 108 110 111 113 113 114 113 113 112 111 + 109 107 105 103 101 100 98 96 95 + 60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 96 95 93 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 94 96 + 98 100 103 105 108 110 113 115 116 118 118 119 119 118 117 116 + 114 112 110 107 105 102 100 98 96 + 57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 98 96 94 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 95 97 + 100 102 105 108 111 114 117 119 121 123 124 124 124 124 122 121 + 119 116 114 111 108 105 102 100 98 + 55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 99 97 94 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 95 98 + 101 105 108 112 115 118 122 124 126 128 129 130 130 129 128 126 + 124 121 118 115 112 108 105 102 99 + 52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 101 98 95 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 96 99 + 103 107 111 115 119 123 126 129 132 134 135 136 136 135 134 132 + 129 126 123 119 115 112 108 104 101 + 50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 103 99 96 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 97 100 + 105 109 114 119 123 127 131 135 137 140 141 142 142 141 140 138 + 135 131 128 124 119 115 111 106 103 + 47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 104 100 96 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 97 102 + 107 112 117 122 127 132 136 140 143 146 147 148 148 147 146 143 + 140 137 133 128 123 119 114 109 104 + 45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 106 101 97 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 98 103 + 109 115 121 126 132 137 142 146 149 152 154 154 154 154 152 149 + 146 142 138 133 128 122 117 111 106 + 42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 108 103 98 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 99 105 + 112 118 125 131 137 142 147 152 155 158 160 161 161 160 158 156 + 152 148 143 138 132 126 120 114 108 + 40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 111 105 99 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 95 101 108 + 115 122 129 136 142 148 153 158 161 164 166 167 167 166 164 162 + 158 154 149 143 137 131 124 118 111 + 37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 114 107 101 94 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 97 104 111 + 119 126 133 141 147 153 159 163 167 170 172 173 174 173 171 168 + 164 160 155 149 142 136 129 121 114 + 35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 118 110 103 96 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 99 106 114 + 122 130 138 145 152 159 164 169 173 176 179 180 180 179 177 174 + 170 166 160 154 148 140 133 125 118 + 32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 121 113 106 98 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 101 109 118 + 126 134 143 150 158 164 170 175 179 182 185 186 186 185 183 180 + 176 172 166 160 153 145 138 130 121 + 30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 125 117 108 100 93 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 95 104 112 121 + 130 139 147 155 163 170 176 181 185 188 191 192 192 191 189 186 + 182 177 172 165 158 150 142 134 125 + 27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 129 120 111 103 95 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 97 106 115 125 + 134 143 152 160 168 175 181 186 191 194 197 198 198 197 195 192 + 188 183 177 171 163 155 147 138 129 + 25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 133 124 114 105 97 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 100 109 119 128 + 138 147 156 165 173 180 186 192 196 200 202 204 204 203 201 198 + 194 189 183 176 168 160 151 142 133 + 22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 137 127 118 108 99 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 93 102 112 122 132 + 142 152 161 170 178 185 192 197 202 205 208 209 209 208 206 203 + 199 194 188 181 173 165 156 147 137 + 20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 141 131 121 111 101 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 95 105 115 125 136 + 146 156 165 174 182 190 196 202 207 210 213 214 214 214 212 208 + 204 199 193 186 178 170 160 151 141 + 17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 145 135 124 114 104 94 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 97 107 118 129 139 + 150 160 170 179 187 194 201 207 212 215 218 219 219 218 216 213 + 209 204 198 191 183 174 165 155 145 + 15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 149 138 127 117 106 96 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 99 110 121 132 143 + 153 164 174 183 191 199 205 211 216 220 222 224 224 223 221 218 + 214 209 202 195 187 179 169 159 149 + 12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 153 142 131 120 109 98 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 101 113 124 135 146 + 157 167 177 187 195 203 210 215 220 224 226 228 228 227 225 222 + 218 213 207 199 191 183 173 163 153 + 10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 156 145 134 123 111 101 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 93 104 115 127 138 149 + 160 171 181 190 199 206 213 219 224 227 230 232 232 231 229 226 + 222 217 211 203 195 187 177 167 156 + 7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 160 148 137 125 114 103 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 95 106 118 129 141 152 + 163 174 184 193 202 210 216 222 227 231 233 235 235 235 233 230 + 226 220 214 207 199 190 180 170 160 + 5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 163 151 140 128 117 105 94 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 97 108 120 132 143 155 + 166 177 187 196 205 213 219 225 230 234 236 238 238 237 236 233 + 229 223 217 210 202 193 184 173 163 + 2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 154 143 131 119 107 96 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 98 110 122 134 146 157 + 169 179 189 199 207 215 222 227 232 236 239 240 241 240 238 235 + 231 226 220 213 205 196 187 176 166 + 0.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 168 157 145 133 121 109 98 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 100 112 124 136 148 160 + 171 181 191 201 209 217 224 229 234 238 240 242 242 242 240 237 + 233 228 222 215 207 199 189 179 168 + -2.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 170 159 147 135 123 111 99 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 102 114 126 138 150 161 + 172 183 193 202 211 218 225 231 235 239 241 243 244 243 241 239 + 235 230 224 217 209 201 191 181 170 + -5.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 172 161 149 137 125 113 101 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 103 115 127 139 151 163 + 174 184 194 203 212 219 226 231 236 239 242 244 244 244 242 239 + 236 231 225 218 211 202 193 183 172 + -7.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 173 162 151 139 127 114 102 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 104 116 128 140 152 164 + 175 185 195 204 212 219 226 231 236 239 242 244 244 244 242 239 + 236 231 226 219 212 203 194 184 173 + -10.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 174 163 152 140 128 116 104 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 105 117 129 141 153 164 + 175 185 195 204 212 219 225 231 235 239 241 243 243 243 241 239 + 236 231 226 219 212 204 195 185 174 + -12.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 164 153 141 129 117 105 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 94 106 118 130 141 153 164 + 175 185 194 203 211 218 224 229 234 237 240 241 242 241 240 238 + 234 230 225 219 212 204 195 185 175 + -15.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 175 164 153 141 129 117 105 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 94 106 118 130 141 153 164 + 174 184 193 201 209 216 222 227 232 235 237 239 240 239 238 236 + 233 229 223 217 211 203 194 185 175 + -17.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 174 164 153 141 130 118 106 94 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 94 106 118 129 141 152 162 + 173 182 191 199 207 213 219 224 229 232 234 236 237 236 235 233 + 230 226 221 216 209 201 193 184 174 + -20.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 173 163 152 141 129 118 106 94 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 94 105 117 128 140 150 161 + 171 180 189 197 204 210 216 221 225 228 230 232 233 233 232 230 + 227 223 218 213 207 199 191 183 173 + -22.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 171 161 151 140 129 117 106 94 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 105 116 127 138 149 159 + 168 177 186 193 200 206 212 216 220 223 226 227 228 228 227 225 + 223 219 215 210 204 197 189 180 171 + -25.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 169 159 149 139 128 117 105 94 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 93 104 115 126 136 146 156 + 165 174 182 189 196 202 207 211 215 218 220 222 222 222 222 220 + 218 214 210 205 200 193 186 178 169 + -27.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 166 157 147 137 126 115 104 93 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 102 113 123 134 143 153 + 161 170 177 184 190 196 201 205 209 211 214 215 216 216 215 214 + 212 209 205 201 195 189 182 174 166 + -30.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 162 154 144 135 124 114 103 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 101 111 121 131 140 149 + 157 165 172 178 184 189 194 198 201 204 206 207 208 208 208 207 + 205 202 199 195 190 184 178 170 162 + -32.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 158 150 141 132 122 112 101 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 99 108 118 127 136 144 + 152 159 166 172 177 182 186 190 193 195 197 199 200 200 200 199 + 197 195 192 188 184 179 173 166 158 + -35.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 153 146 138 129 119 110 100 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 96 106 115 124 132 140 + 147 153 160 165 170 174 178 181 184 186 188 189 190 190 190 190 + 188 187 184 181 177 172 167 160 153 + -37.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 148 141 133 125 116 107 97 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 94 103 111 119 127 134 + 141 147 152 157 161 165 168 171 174 176 177 178 179 180 180 180 + 179 177 175 173 169 165 160 155 148 + -40.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 142 136 129 121 113 104 95 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 100 108 115 122 129 + 134 140 144 149 152 155 158 160 162 164 165 167 168 168 169 168 + 168 167 166 164 161 157 153 148 142 + -42.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 136 130 124 117 109 101 93 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 96 104 110 117 123 + 128 132 136 140 142 145 147 149 150 151 153 154 155 155 156 156 + 156 156 155 154 152 149 145 141 136 + -45.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 129 124 119 112 105 98 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 93 100 106 111 116 + 121 124 127 130 132 134 135 136 137 138 139 140 140 141 142 143 + 144 144 144 143 142 140 137 134 129 + -47.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 122 118 113 108 101 95 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 96 102 106 110 + 114 116 118 120 121 122 122 122 123 123 123 124 125 126 127 129 + 130 131 131 132 131 130 128 126 122 + -50.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 115 112 108 103 98 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 93 98 101 104 + 107 108 109 110 110 110 109 108 108 107 107 108 108 110 111 113 + 115 117 118 119 120 120 119 118 115 + -52.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 108 106 103 99 94 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 94 97 99 + 101 101 101 100 98 97 95 93 92 92 92 92 92 92 94 97 + 99 102 105 107 109 110 110 110 108 + -55.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 102 101 98 95 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 95 96 + 95 94 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 94 98 100 102 103 102 + -57.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 97 97 96 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 93 93 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 93 96 97 + -60.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 93 94 93 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -62.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -65.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -67.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -70.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -72.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -75.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -77.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -80.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -82.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -85.0-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + -87.5-180.0 180.0 5.0 350.0 LAT/LON1/LON2/DLON/H + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 92 + 92 92 92 92 92 92 92 92 92 + 3 END OF TEC MAP + END OF FILE diff --git a/src/test/resources/linear-EOP/UTC-TAI.history b/src/test/resources/linear-EOP/UTC-TAI.history index 394bde0be4..6103c31ce2 100644 --- a/src/test/resources/linear-EOP/UTC-TAI.history +++ b/src/test/resources/linear-EOP/UTC-TAI.history @@ -45,5 +45,6 @@ 2006 Jan. 1.- 2009 Jan. 1 33s 2009 Jan. 1.- 2012 Jul 1 34s 2012 Jul 1 - 2015 Jul 1 35s - 2015 Jul 1 - 36s + 2015 Jul 1 - 2017 Jan 1 36s + 2017 Jan 1 - 37s ---------------------------------------------------------------------- diff --git a/src/test/resources/linear-EOP/eopc04_08_IAU2000.00 b/src/test/resources/linear-EOP/eopc04_08_IAU2000.00 index 8c66846960..a26a9fbdc4 100644 --- a/src/test/resources/linear-EOP/eopc04_08_IAU2000.00 +++ b/src/test/resources/linear-EOP/eopc04_08_IAU2000.00 @@ -1,6 +1,8 @@ THIS IS A FAKE EOP FILE, WITH LINEAR MODEL FOR POLE AND DUT1 THIS FILE IS INTENTED FOR TEST PURPOSES ONLY! + EOP (IERS) 05 C04 + Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err 2000 2 10 51584 0.069150 0.000032 0.3233738 0.0011000 0.000000 0.000000 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 2000 2 11 51585 0.069100 0.000034 0.3222738 0.0011000 0.000000 0.000000 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 diff --git a/src/test/resources/missing-months/UTC-TAI.history.gz b/src/test/resources/missing-months/UTC-TAI.history.gz index 19a39943cd..037facd8f0 100644 Binary files a/src/test/resources/missing-months/UTC-TAI.history.gz and b/src/test/resources/missing-months/UTC-TAI.history.gz differ diff --git a/src/test/resources/new-bulletinB/UTC-TAI.history b/src/test/resources/new-bulletinB/UTC-TAI.history index 394bde0be4..6103c31ce2 100644 --- a/src/test/resources/new-bulletinB/UTC-TAI.history +++ b/src/test/resources/new-bulletinB/UTC-TAI.history @@ -45,5 +45,6 @@ 2006 Jan. 1.- 2009 Jan. 1 33s 2009 Jan. 1.- 2012 Jul 1 34s 2012 Jul 1 - 2015 Jul 1 35s - 2015 Jul 1 - 36s + 2015 Jul 1 - 2017 Jan 1 36s + 2017 Jan 1 - 37s ---------------------------------------------------------------------- diff --git a/src/test/resources/old-bulletinB/UTC-TAI.history b/src/test/resources/old-bulletinB/UTC-TAI.history index 394bde0be4..6103c31ce2 100644 --- a/src/test/resources/old-bulletinB/UTC-TAI.history +++ b/src/test/resources/old-bulletinB/UTC-TAI.history @@ -45,5 +45,6 @@ 2006 Jan. 1.- 2009 Jan. 1 33s 2009 Jan. 1.- 2012 Jul 1 34s 2012 Jul 1 - 2015 Jul 1 35s - 2015 Jul 1 - 36s + 2015 Jul 1 - 2017 Jan 1 36s + 2017 Jan 1 - 37s ---------------------------------------------------------------------- diff --git a/src/test/resources/orbit-determination/W3B/tai-utc.dat b/src/test/resources/orbit-determination/W3B/tai-utc.dat index dad80f2116..89d16226f3 100644 --- a/src/test/resources/orbit-determination/W3B/tai-utc.dat +++ b/src/test/resources/orbit-determination/W3B/tai-utc.dat @@ -38,3 +38,4 @@ 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S + 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S diff --git a/src/test/resources/orbit-determination/february-2016/tai-utc.dat b/src/test/resources/orbit-determination/february-2016/tai-utc.dat index 46d3e48beb..239e233ddb 100644 --- a/src/test/resources/orbit-determination/february-2016/tai-utc.dat +++ b/src/test/resources/orbit-determination/february-2016/tai-utc.dat @@ -43,3 +43,4 @@ 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0 S + (MJD - 41317.) X 0.0 S 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0 S + (MJD - 41317.) X 0.0 S 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S + 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0 S diff --git a/src/test/resources/potential/egm-format/EGM96-truncated-21x21 b/src/test/resources/potential/egm-format/EGM96-truncated-21x21 new file mode 100644 index 0000000000..18b356a6c9 --- /dev/null +++ b/src/test/resources/potential/egm-format/EGM96-truncated-21x21 @@ -0,0 +1,251 @@ + 0 0 1.000000000000e+00 0.000000000000e+00 0.00000000e+00 0.00000000e+00 + 2 0 -0.484165371736e-03 0.000000000000e+00 0.35610635e-10 0.00000000e+00 + 2 1 -0.186987635955e-09 0.119528012031e-08 0.10000000e-29 0.10000000e-29 + 2 2 0.243914352398e-05 -0.140016683654e-05 0.53739154e-10 0.54353269e-10 + 3 0 0.957254173792e-06 0.000000000000e+00 0.18094237e-10 0.00000000e+00 + 3 1 0.202998882184e-05 0.248513158716e-06 0.13965165e-09 0.13645882e-09 + 3 2 0.904627768605e-06 -0.619025944205e-06 0.10962329e-09 0.11182866e-09 + 3 3 0.721072657057e-06 0.141435626958e-05 0.95156281e-10 0.93285090e-10 + 4 0 0.539873863789e-06 0.000000000000e+00 0.10423678e-09 0.00000000e+00 + 4 1 -0.536321616971e-06 -0.473440265853e-06 0.85674404e-10 0.82408489e-10 + 4 2 0.350694105785e-06 0.662671572540e-06 0.16000186e-09 0.16390576e-09 + 4 3 0.990771803829e-06 -0.200928369177e-06 0.84657802e-10 0.82662506e-10 + 4 4 -0.188560802735e-06 0.308853169333e-06 0.87315359e-10 0.87852819e-10 + 5 0 0.685323475630e-07 0.000000000000e+00 0.54383090e-10 0.00000000e+00 + 5 1 -0.621012128528e-07 -0.944226127525e-07 0.27996887e-09 0.28082882e-09 + 5 2 0.652438297612e-06 -0.323349612668e-06 0.23747375e-09 0.24356998e-09 + 5 3 -0.451955406071e-06 -0.214847190624e-06 0.17111636e-09 0.16810647e-09 + 5 4 -0.295301647654e-06 0.496658876769e-07 0.11981266e-09 0.11849793e-09 + 5 5 0.174971983203e-06 -0.669384278219e-06 0.11642563e-09 0.11590031e-09 + 6 0 -0.149957994714e-06 0.000000000000e+00 0.14497863e-09 0.00000000e+00 + 6 1 -0.760879384947e-07 0.262890545501e-07 0.22415138e-09 0.21957296e-09 + 6 2 0.481732442832e-07 -0.373728201347e-06 0.27697363e-09 0.28105811e-09 + 6 3 0.571730990516e-07 0.902694517163e-08 0.19432407e-09 0.18682712e-09 + 6 4 -0.862142660109e-07 -0.471408154267e-06 0.15229150e-09 0.15328004e-09 + 6 5 -0.267133325490e-06 -0.536488432483e-06 0.89838470e-10 0.87820905e-10 + 6 6 0.967616121092e-08 -0.237192006935e-06 0.11332010e-09 0.11518036e-09 + 7 0 0.909789371450e-07 0.000000000000e+00 0.13919821e-09 0.00000000e+00 + 7 1 0.279872910488e-06 0.954336911867e-07 0.43500231e-09 0.42584820e-09 + 7 2 0.329743816488e-06 0.930667596042e-07 0.39034671e-09 0.39858085e-09 + 7 3 0.250398657706e-06 -0.217198608738e-06 0.32646930e-09 0.31799577e-09 + 7 4 -0.275114355257e-06 -0.123800392323e-06 0.23251698e-09 0.22964717e-09 + 7 5 0.193765507243e-08 0.177377719872e-07 0.16792139e-09 0.17201828e-09 + 7 6 -0.358856860645e-06 0.151789817739e-06 0.89799183e-10 0.89575785e-10 + 7 7 0.109185148045e-08 0.244415707993e-07 0.13599934e-09 0.13564720e-09 + 8 0 0.496711667324e-07 0.000000000000e+00 0.22663861e-09 0.00000000e+00 + 8 1 0.233422047893e-07 0.590060493411e-07 0.40634230e-09 0.39490843e-09 + 8 2 0.802978722615e-07 0.654175425859e-07 0.36512930e-09 0.36825158e-09 + 8 3 -0.191877757009e-07 -0.863454445021e-07 0.29352566e-09 0.28069012e-09 + 8 4 -0.244600105471e-06 0.700233016934e-07 0.25244455e-09 0.26262799e-09 + 8 5 -0.255352403037e-07 0.891462164788e-07 0.20021311e-09 0.19479275e-09 + 8 6 -0.657361610961e-07 0.309238461807e-06 0.16673572e-09 0.16680807e-09 + 8 7 0.672811580072e-07 0.747440473633e-07 0.94505589e-10 0.95246237e-10 + 8 8 -0.124092493016e-06 0.120533165603e-06 0.15695393e-09 0.15769999e-09 + 9 0 0.276714300853e-07 0.000000000000e+00 0.22214648e-09 0.00000000e+00 + 9 1 0.143387502749e-06 0.216834947618e-07 0.58335018e-09 0.55609313e-09 + 9 2 0.222288318564e-07 -0.322196647116e-07 0.48353378e-09 0.48392082e-09 + 9 3 -0.160811502143e-06 -0.742287409462e-07 0.46594220e-09 0.45810990e-09 + 9 4 -0.900179225336e-08 0.194666779475e-07 0.36909229e-09 0.36635390e-09 + 9 5 -0.166165092924e-07 -0.541113191483e-07 0.29221604e-09 0.30036229e-09 + 9 6 0.626941938248e-07 0.222903525945e-06 0.22052623e-09 0.22142953e-09 + 9 7 -0.118366323475e-06 -0.965152667886e-07 0.18155999e-09 0.18063564e-09 + 9 8 0.188436022794e-06 -0.308566220421e-08 0.13135894e-09 0.13157891e-09 + 9 9 -0.477475386132e-07 0.966412847714e-07 0.18551471e-09 0.18432444e-09 +10 0 0.526222488569e-07 0.000000000000e+00 0.30890035e-09 0.00000000e+00 +10 1 0.835115775652e-07 -0.131314331796e-06 0.52560148e-09 0.50053305e-09 +10 2 -0.942413882081e-07 -0.515791657390e-07 0.44292785e-09 0.44624108e-09 +10 3 -0.689895048176e-08 -0.153768828694e-06 0.37051454e-09 0.36174988e-09 +10 4 -0.840764549716e-07 -0.792806255331e-07 0.31088626e-09 0.32213352e-09 +10 5 -0.493395938185e-07 -0.505370221897e-07 0.32221821e-09 0.32202850e-09 +10 6 -0.375885236598e-07 -0.795667053872e-07 0.28294160e-09 0.27566976e-09 +10 7 0.811460540925e-08 -0.336629641314e-08 0.18704768e-09 0.18901334e-09 +10 8 0.404927981694e-07 -0.918705975922e-07 0.18275291e-09 0.18362644e-09 +10 9 0.125491334939e-06 -0.376516222392e-07 0.90465977e-10 0.90959794e-10 +10 10 0.100538634409e-06 -0.240148449520e-07 0.15964045e-09 0.15956547e-09 +11 0 -0.509613707522e-07 0.000000000000e+00 0.34552145e-09 0.00000000e+00 +11 1 0.151687209933e-07 -0.268604146166e-07 0.64201268e-09 0.61048276e-09 +11 2 0.186309749878e-07 -0.990693862047e-07 0.57068253e-09 0.57276720e-09 +11 3 -0.309871239854e-07 -0.148131804260e-06 0.50157135e-09 0.49518511e-09 +11 4 -0.389580205051e-07 -0.636666511980e-07 0.48810597e-09 0.49707446e-09 +11 5 0.377848029452e-07 0.494736238169e-07 0.38720715e-09 0.39094668e-09 +11 6 -0.118676592395e-08 0.344769584593e-07 0.34564774e-09 0.34776352e-09 +11 7 0.411565188074e-08 -0.898252808977e-07 0.29198446e-09 0.29066089e-09 +11 8 -0.598410841300e-08 0.243989612237e-07 0.21462767e-09 0.21335842e-09 +11 9 -0.314231072723e-07 0.417731829829e-07 0.19485030e-09 0.19528860e-09 +11 10 -0.521882681927e-07 -0.183364561788e-07 0.12830605e-09 0.12848978e-09 +11 11 0.460344448746e-07 -0.696662308185e-07 0.19818678e-09 0.19713732e-09 +12 0 0.377252636558e-07 0.000000000000e+00 0.43588608e-09 0.00000000e+00 +12 1 -0.540654977836e-07 -0.435675748979e-07 0.54452693e-09 0.51580423e-09 +12 2 0.142979642253e-07 0.320975937619e-07 0.53272875e-09 0.54168090e-09 +12 3 0.393995876403e-07 0.244264863505e-07 0.45756501e-09 0.44928026e-09 +12 4 -0.686908127934e-07 0.415081109011e-08 0.37303817e-09 0.39051408e-09 +12 5 0.309411128730e-07 0.782536279033e-08 0.33134311e-09 0.33356660e-09 +12 6 0.341523275208e-08 0.391765484449e-07 0.41142705e-09 0.40153046e-09 +12 7 -0.186909958587e-07 0.356131849382e-07 0.33782181e-09 0.33541151e-09 +12 8 -0.253769398865e-07 0.169361024629e-07 0.25681411e-09 0.25872950e-09 +12 9 0.422880630662e-07 0.252692598301e-07 0.16406791e-09 0.16446989e-09 +12 10 -0.617619654902e-08 0.308375794212e-07 0.16918996e-09 0.16790941e-09 +12 11 0.112502994122e-07 -0.637946501558e-08 0.67006102e-10 0.66840672e-10 +12 12 -0.249532607390e-08 -0.111780601900e-07 0.12164740e-09 0.12112058e-09 +13 0 0.422982206413e-07 0.000000000000e+00 0.48735174e-09 0.00000000e+00 +13 1 -0.513569699124e-07 0.390510386685e-07 0.57337833e-09 0.54235587e-09 +13 2 0.559217667099e-07 -0.627337565381e-07 0.62482536e-09 0.62901869e-09 +13 3 -0.219360927945e-07 0.974829362237e-07 0.53388870e-09 0.52773007e-09 +13 4 -0.313762599666e-08 -0.119627874492e-07 0.54039679e-09 0.54783125e-09 +13 5 0.590049394905e-07 0.664975958036e-07 0.47950608e-09 0.48088793e-09 +13 6 -0.359038073075e-07 -0.657280613686e-08 0.40896517e-09 0.40884469e-09 +13 7 0.253002147087e-08 -0.621470822331e-08 0.39155909e-09 0.39410761e-09 +13 8 -0.983150822695e-08 -0.104740222825e-07 0.35854029e-09 0.35240648e-09 +13 9 0.247325771791e-07 0.452870369936e-07 0.26046428e-09 0.26319103e-09 +13 10 0.410324653930e-07 -0.368121029480e-07 0.18490873e-09 0.18422246e-09 +13 11 -0.443869677399e-07 -0.476507804288e-08 0.18376153e-09 0.18244164e-09 +13 12 -0.312622200222e-07 0.878405809267e-07 0.61371381e-10 0.61572440e-10 +13 13 -0.612759553199e-07 0.685261488594e-07 0.14093183e-09 0.14043508e-09 +14 0 -0.242786502921e-07 0.000000000000e+00 0.54599206e-09 0.00000000e+00 +14 1 -0.186968616381e-07 0.294747542249e-07 0.51130059e-09 0.48389874e-09 +14 2 -0.367789379502e-07 -0.516779392055e-08 0.54973862e-09 0.56994964e-09 +14 3 0.358875097333e-07 0.204618827833e-07 0.49957192e-09 0.49552362e-09 +14 4 0.183865617792e-08 -0.226780613566e-07 0.46147419e-09 0.49345647e-09 +14 5 0.287344273542e-07 -0.163882249728e-07 0.39151067e-09 0.39536505e-09 +14 6 -0.194810485574e-07 0.247831272781e-08 0.39714930e-09 0.38064240e-09 +14 7 0.375003839415e-07 -0.417291319429e-08 0.43406164e-09 0.42974545e-09 +14 8 -0.350946485865e-07 -0.153515265203e-07 0.42197834e-09 0.42204868e-09 +14 9 0.320284939341e-07 0.288804922064e-07 0.27047158e-09 0.26799755e-09 +14 10 0.390329180008e-07 -0.144308452469e-08 0.21493467e-09 0.21309132e-09 +14 11 0.153970516502e-07 -0.390548173245e-07 0.11626513e-09 0.11585650e-09 +14 12 0.840829163869e-08 -0.311327189117e-07 0.10883382e-09 0.10845404e-09 +14 13 0.322147043964e-07 0.451897224960e-07 0.29804138e-10 0.29985311e-10 +14 14 -0.518980794309e-07 -0.481506636748e-08 0.32054988e-10 0.32450504e-10 +15 0 0.147910068708e-08 0.000000000000e+00 0.53683772e-09 0.00000000e+00 +15 1 0.100817268177e-07 0.109773066324e-07 0.52974731e-09 0.50086796e-09 +15 2 -0.213942673775e-07 -0.308914875777e-07 0.60403581e-09 0.61012404e-09 +15 3 0.521392929041e-07 0.172892926103e-07 0.58231646e-09 0.55913415e-09 +15 4 -0.408150084078e-07 0.650174707794e-08 0.52298193e-09 0.53982246e-09 +15 5 0.124935723108e-07 0.808375563996e-08 0.55310203e-09 0.55318757e-09 +15 6 0.331211643896e-07 -0.368246004304e-07 0.46497321e-09 0.47095028e-09 +15 7 0.596210699259e-07 0.531841171879e-08 0.41280301e-09 0.41673871e-09 +15 8 -0.322428691498e-07 0.221523579587e-07 0.41946092e-09 0.41393325e-09 +15 9 0.128788268085e-07 0.375629820829e-07 0.38464822e-09 0.38605019e-09 +15 10 0.104688722521e-07 0.147222147015e-07 0.29362674e-09 0.29535457e-09 +15 11 -0.111675061934e-08 0.180996198432e-07 0.19651296e-09 0.19498687e-09 +15 12 -0.323962134415e-07 0.155243104746e-07 0.98639991e-10 0.98725357e-10 +15 13 -0.283933019117e-07 -0.422066791103e-08 0.12015236e-09 0.11969380e-09 +15 14 0.519168859330e-08 -0.243752739666e-07 0.31187284e-10 0.31381782e-10 +15 15 -0.190930538322e-07 -0.471139421558e-08 0.48306488e-10 0.47133713e-10 +16 0 -0.315322986722e-08 0.000000000000e+00 0.53130421e-09 0.00000000e+00 +16 1 0.258360856231e-07 0.325447560859e-07 0.52980947e-09 0.50249031e-09 +16 2 -0.233671404512e-07 0.288799363439e-07 0.51389747e-09 0.54074935e-09 +16 3 -0.336019429391e-07 -0.220418988010e-07 0.48485480e-09 0.48575056e-09 +16 4 0.402316284314e-07 0.483837716909e-07 0.53085070e-09 0.56684012e-09 +16 5 -0.129501939245e-07 -0.319458578129e-08 0.50235685e-09 0.49883081e-09 +16 6 0.140239252323e-07 -0.350760208303e-07 0.43608217e-09 0.42561981e-09 +16 7 -0.708412635136e-08 -0.881581561131e-08 0.41560133e-09 0.41334452e-09 +16 8 -0.209018868094e-07 0.500527390530e-08 0.46375155e-09 0.46442894e-09 +16 9 -0.218588720643e-07 -0.395012419994e-07 0.42203404e-09 0.42207754e-09 +16 10 -0.117529900814e-07 0.114211582961e-07 0.33663249e-09 0.33627437e-09 +16 11 0.187574042592e-07 -0.303161919925e-08 0.17642883e-09 0.17706550e-09 +16 12 0.195400194038e-07 0.666983574071e-08 0.12893049e-09 0.12847491e-09 +16 13 0.138196369576e-07 0.102778499508e-08 0.49645374e-10 0.49771036e-10 +16 14 -0.193182168856e-07 -0.386174893776e-07 0.31733130e-10 0.32124180e-10 +16 15 -0.145149060142e-07 -0.327443078739e-07 0.38814590e-10 0.39089765e-10 +16 16 -0.379671710746e-07 0.302155372655e-08 0.17759112e-09 0.17693092e-09 +17 0 0.197605066395e-07 0.000000000000e+00 0.45595926e-09 0.00000000e+00 +17 1 -0.254177575118e-07 -0.306630529689e-07 0.61168597e-09 0.60590338e-09 +17 2 -0.195988656721e-07 0.649265893410e-08 0.50202766e-09 0.51481373e-09 +17 3 0.564123066224e-08 0.678327095529e-08 0.63292562e-09 0.59927568e-09 +17 4 0.707457075637e-08 0.249437600834e-07 0.50206810e-09 0.52913860e-09 +17 5 -0.154987006052e-07 0.660021551851e-08 0.56101689e-09 0.55070220e-09 +17 6 -0.118194012847e-07 -0.289770975177e-07 0.54939081e-09 0.54334128e-09 +17 7 0.242149702381e-07 -0.422222973697e-08 0.45746637e-09 0.45401208e-09 +17 8 0.388442097559e-07 0.358904095943e-08 0.43511790e-09 0.42576205e-09 +17 9 0.381356493231e-08 -0.281466943714e-07 0.41146573e-09 0.41119523e-09 +17 10 -0.388216085542e-08 0.181328176508e-07 0.39641987e-09 0.40000371e-09 +17 11 -0.157356600363e-07 0.106560649404e-07 0.29773487e-09 0.29741388e-09 +17 12 0.288013010655e-07 0.203450136084e-07 0.14784485e-09 0.14706358e-09 +17 13 0.165503425731e-07 0.204667531435e-07 0.11998713e-09 0.11955420e-09 +17 14 -0.141983872649e-07 0.114948025244e-07 0.49034053e-10 0.50069006e-10 +17 15 0.542100361657e-08 0.532610369811e-08 0.45617974e-10 0.44691651e-10 +17 16 -0.301992205043e-07 0.365331918531e-08 0.71252579e-10 0.70742716e-10 +17 17 -0.343086856041e-07 -0.198523455381e-07 0.20333152e-09 0.20282665e-09 +18 0 0.508691038332e-08 0.000000000000e+00 0.46789230e-09 0.00000000e+00 +18 1 0.721098449649e-08 -0.388714473013e-07 0.52798106e-09 0.50885673e-09 +18 2 0.140631771205e-07 0.100093396253e-07 0.50681599e-09 0.54650317e-09 +18 3 -0.507232520873e-08 -0.490865931335e-08 0.54681971e-09 0.54400738e-09 +18 4 0.548759308217e-07 -0.135267117720e-08 0.49090676e-09 0.52102762e-09 +18 5 0.548710485555e-08 0.264338629459e-07 0.58920322e-09 0.58703487e-09 +18 6 0.146570755271e-07 -0.136438019951e-07 0.52538248e-09 0.51113456e-09 +18 7 0.675812328417e-08 0.688577494235e-08 0.45391343e-09 0.44506212e-09 +18 8 0.307619845144e-07 0.417827734107e-08 0.45250150e-09 0.45442385e-09 +18 9 -0.188470601880e-07 0.368302736953e-07 0.44309926e-09 0.44072646e-09 +18 10 0.527535358934e-08 -0.466091535881e-08 0.43962755e-09 0.43706812e-09 +18 11 -0.729628518960e-08 0.195215208020e-08 0.30269058e-09 0.30532107e-09 +18 12 -0.297449412422e-07 -0.164497878395e-07 0.17926913e-09 0.17863254e-09 +18 13 -0.627919717152e-08 -0.348383939938e-07 0.62153473e-10 0.62044245e-10 +18 14 -0.815605336410e-08 -0.128636585027e-07 0.45358449e-10 0.46080793e-10 +18 15 -0.405003412879e-07 -0.202684998021e-07 0.75716039e-10 0.76852783e-10 +18 16 0.104141042028e-07 0.661468817624e-08 0.15974982e-09 0.15840268e-09 +18 17 0.358771586841e-08 0.448065587564e-08 0.10867064e-09 0.10857707e-09 +18 18 0.312351953717e-08 -0.109906032543e-07 0.28770747e-09 0.28596208e-09 +19 0 -0.325780965394e-08 0.000000000000e+00 0.39880617e-09 0.00000000e+00 +19 1 -0.759903885319e-08 0.126835472605e-08 0.55285072e-09 0.55819254e-09 +19 2 0.353541528655e-07 -0.131346303514e-08 0.53996618e-09 0.56700245e-09 +19 3 -0.974103607309e-08 0.150662259043e-08 0.57760045e-09 0.54562054e-09 +19 4 0.157039009057e-07 -0.761677383811e-08 0.55266991e-09 0.59527084e-09 +19 5 0.109629213379e-07 0.283172176438e-07 0.54725130e-09 0.52424864e-09 +19 6 -0.408745178658e-08 0.186219430719e-07 0.54648203e-09 0.53804795e-09 +19 7 0.478275337044e-08 -0.717283455900e-08 0.55134865e-09 0.54439979e-09 +19 8 0.294908364280e-07 -0.993037002883e-08 0.44281042e-09 0.42972762e-09 +19 9 0.307961427159e-08 0.694110477214e-08 0.41895916e-09 0.41630624e-09 +19 10 -0.338415069043e-07 -0.737981767136e-08 0.41590690e-09 0.41338509e-09 +19 11 0.160443652916e-07 0.996673453483e-08 0.36350417e-09 0.36418173e-09 +19 12 -0.247106581581e-08 0.916852310642e-08 0.26437342e-09 0.26193982e-09 +19 13 -0.744717379980e-08 -0.282584466742e-07 0.13571784e-09 0.13572279e-09 +19 14 -0.470502589215e-08 -0.129526697983e-07 0.60064446e-10 0.60016480e-10 +19 15 -0.176580549771e-07 -0.140350990039e-07 0.69036188e-10 0.67819564e-10 +19 16 -0.216950096188e-07 -0.724534721567e-08 0.12086256e-09 0.12099640e-09 +19 17 0.290444936079e-07 -0.153456531070e-07 0.17867126e-09 0.17929347e-09 +19 18 0.348382199593e-07 -0.954146344917e-08 0.11549444e-09 0.11520432e-09 +19 19 -0.257349349430e-08 0.483151822363e-08 0.29444927e-09 0.29634084e-09 +20 0 0.222384610651e-07 0.000000000000e+00 0.46908617e-09 0.00000000e+00 +20 1 0.516303125218e-08 0.669626726966e-08 0.45465090e-09 0.44522409e-09 +20 2 0.198831128238e-07 0.175183843257e-07 0.53799405e-09 0.57729565e-09 +20 3 -0.362601436785e-08 0.379590724141e-07 0.52764671e-09 0.52364203e-09 +20 4 0.242238118652e-08 -0.211057611874e-07 0.53778329e-09 0.57080135e-09 +20 5 -0.107042562564e-07 -0.771860083169e-08 0.55963913e-09 0.55066230e-09 +20 6 0.110474837570e-07 -0.217720365898e-08 0.58502067e-09 0.56422287e-09 +20 7 -0.210090282728e-07 -0.223491503969e-10 0.54119158e-09 0.52526828e-09 +20 8 0.442419185637e-08 0.183035804593e-08 0.44722111e-09 0.44959414e-09 +20 9 0.178846216942e-07 -0.663940865358e-08 0.44435385e-09 0.44772500e-09 +20 10 -0.325394919988e-07 -0.512308873621e-08 0.43477963e-09 0.43629438e-09 +20 11 0.138992707697e-07 -0.187706454942e-07 0.37824380e-09 0.38409415e-09 +20 12 -0.635750600750e-08 0.180260853103e-07 0.28133033e-09 0.27918368e-09 +20 13 0.275222725997e-07 0.690887077588e-08 0.99129615e-10 0.98552906e-10 +20 14 0.115841169405e-07 -0.143176160143e-07 0.89023003e-10 0.90791190e-10 +20 15 -0.260130744291e-07 -0.784379672413e-09 0.98594967e-10 0.10072429e-09 +20 16 -0.124137147118e-07 -0.277500443628e-09 0.19230790e-09 0.19182381e-09 +20 17 0.436909667960e-08 -0.137420446198e-07 0.19107891e-09 0.18937308e-09 +20 18 0.151842883022e-07 -0.808429903142e-09 0.25237894e-09 0.25083660e-09 +20 19 -0.314942002852e-08 0.106505202245e-07 0.17603753e-09 0.17721403e-09 +20 20 0.401448327968e-08 -0.120450644785e-07 0.36744902e-09 0.36712141e-09 +21 0 0.587820252575e-08 0.000000000000e+00 0.35059239e-09 0.00000000e+00 +21 1 -0.161000670141e-07 0.284359400791e-07 0.55788514e-09 0.57242322e-09 +21 2 -0.654460482558e-08 0.378474868508e-08 0.47278653e-09 0.51015036e-09 +21 3 0.195491995260e-07 0.226286963716e-07 0.62401293e-09 0.58774631e-09 +21 4 -0.576604339239e-08 0.194493782631e-07 0.46551912e-09 0.50884469e-09 +21 5 0.258856303016e-08 0.170850368669e-08 0.60228094e-09 0.56420606e-09 +21 6 -0.140168810589e-07 -0.273814826381e-11 0.48546483e-09 0.47785557e-09 +21 7 -0.864357168475e-08 0.442612277119e-08 0.52627825e-09 0.51313235e-09 +21 8 -0.170477278237e-07 0.150711192630e-08 0.47808382e-09 0.46269297e-09 +21 9 0.164489062394e-07 0.830113196365e-08 0.40203600e-09 0.39977144e-09 +21 10 -0.109928976409e-07 -0.146913794684e-08 0.35476361e-09 0.35131278e-09 +21 11 0.699300364214e-08 -0.353590565124e-07 0.34524249e-09 0.34473749e-09 +21 12 -0.319300109594e-08 0.145786917947e-07 0.29912612e-09 0.29819216e-09 +21 13 -0.189854524590e-07 0.140514791436e-07 0.16733290e-09 0.16886633e-09 +21 14 0.203580785674e-07 0.755772462840e-08 0.12151988e-09 0.12078084e-09 +21 15 0.175530220278e-07 0.104533886832e-07 0.11000758e-09 0.11061503e-09 +21 16 0.786969109367e-08 -0.656089715279e-08 0.14373524e-09 0.14571655e-09 +21 17 -0.699484489981e-08 -0.736064901147e-08 0.19645139e-09 0.19755939e-09 +21 18 0.259643291521e-07 -0.111560806130e-07 0.20785335e-09 0.20599439e-09 +21 19 -0.273741636410e-07 0.163958190052e-07 0.23937699e-09 0.24278605e-09 +21 20 -0.268682473584e-07 0.162086057168e-07 0.15204607e-09 0.15340995e-09 +21 21 0.830374873932e-08 -0.375546121742e-08 0.31118611e-09 0.31332759e-09 \ No newline at end of file diff --git a/src/test/resources/rapid-data-columns/UTC-TAI.history.gz b/src/test/resources/rapid-data-columns/UTC-TAI.history.gz index 19a39943cd..020bfc2978 100644 Binary files a/src/test/resources/rapid-data-columns/UTC-TAI.history.gz and b/src/test/resources/rapid-data-columns/UTC-TAI.history.gz differ diff --git a/src/test/resources/rapid-data-xml/UTC-TAI.history.gz b/src/test/resources/rapid-data-xml/UTC-TAI.history.gz deleted file mode 100644 index 19a39943cd..0000000000 Binary files a/src/test/resources/rapid-data-xml/UTC-TAI.history.gz and /dev/null differ diff --git a/src/test/resources/regular-data/UTC-TAI.history b/src/test/resources/regular-data/UTC-TAI.history index 394bde0be4..6103c31ce2 100644 --- a/src/test/resources/regular-data/UTC-TAI.history +++ b/src/test/resources/regular-data/UTC-TAI.history @@ -45,5 +45,6 @@ 2006 Jan. 1.- 2009 Jan. 1 33s 2009 Jan. 1.- 2012 Jul 1 34s 2012 Jul 1 - 2015 Jul 1 35s - 2015 Jul 1 - 36s + 2015 Jul 1 - 2017 Jan 1 36s + 2017 Jan 1 - 37s ---------------------------------------------------------------------- diff --git a/src/test/resources/rinex/YEBE00ESP_R_20230891800_01H_30S_MO.crx.gz b/src/test/resources/rinex/YEBE00ESP_R_20230891800_01H_30S_MO.crx.gz new file mode 100644 index 0000000000..5fe3bb0cd8 Binary files /dev/null and b/src/test/resources/rinex/YEBE00ESP_R_20230891800_01H_30S_MO.crx.gz differ diff --git a/src/test/resources/rinex/continuation-phase-shift.23o b/src/test/resources/rinex/continuation-phase-shift.23o new file mode 100644 index 0000000000..2bb3902ac5 --- /dev/null +++ b/src/test/resources/rinex/continuation-phase-shift.23o @@ -0,0 +1,32 @@ + 3.02 OBSERVATION DATA M (MIXED) RINEX VERSION / TYPE +Convert 2.6.3 NovAtel 20230801 163928 UTC PGM / RUN BY / DATE +RINEX Version 3.02 Observation File COMMENT + MARKER NAME +800 MARKER NUMBER + MARKER TYPE + OBSERVER / AGENCY + NovAtel GPSCard REC # / TYPE / VERS + ANT # / TYPE + 0.0000 0.0000 0.0000 APPROX POSITION XYZ + 0.0000 0.0000 0.0000 ANTENNA: DELTA H/E/N +G 8 C1C L1C D1C S1C C2W L2W D2W S2W SYS / # / OBS TYPES +J 8 C1C L1C D1C S1C C2S L2S D2S S2S SYS / # / OBS TYPES +DBHZ SIGNAL STRENGTH UNIT + 30.000 INTERVAL + 2023 7 31 23 43 0.0000000 GPS TIME OF FIRST OBS + 2023 8 1 0 42 0.0000000 GPS TIME OF LAST OBS +G L1C 0.00000 30 G19 G17 G06 G11 G25 G24 G32 G03 G15 G14 SYS / PHASE SHIFT + G02 G10 G21 G12 G28 G23 G31 G04 G08 G26 SYS / PHASE SHIFT + G27 G09 G16 G07 G18 G30 G05 G20 G13 G29 SYS / PHASE SHIFT +G L2W 0.00000 30 G19 G17 G06 G32 G03 G15 G14 G02 G21 G12 SYS / PHASE SHIFT + G28 G11 G25 G24 G10 G23 G31 G04 G08 G26 SYS / PHASE SHIFT + G09 G27 G16 G07 G18 G30 G05 G20 G13 G29 SYS / PHASE SHIFT +J L1C 0.00000 03 J03 J02 J04 SYS / PHASE SHIFT +J L2S 0.00000 03 J03 J02 J04 SYS / PHASE SHIFT + GLONASS SLOT / FRQ # + C1C -71.950 C2C -71.950 C2P -71.950 GLONASS COD/PHS/BIS +Leap Seconds Unknown COMMENT + END OF HEADER +> 2023 07 31 23 43 0.0000000 0 2 -0.000000000000 +G17 20414955.500 7 107649171.699 7 -6355.480 42.000 20484958.734 6 83882479.008 6 -4952.344 38.000 +G09 22017506.930 6 115703297.859 6 4579.813 41.000 22017611.406 6 90158433.855 6 3568.652 41.000 diff --git a/src/test/resources/rinex/cycle-slip.00o b/src/test/resources/rinex/cycle-slip.00o new file mode 100644 index 0000000000..767d34d95d --- /dev/null +++ b/src/test/resources/rinex/cycle-slip.00o @@ -0,0 +1,111 @@ + 3.02 OBSERVATION DATA M (MIXED) RINEX VERSION / TYPE +NetR9 5.03 Receiver Operator 11-JAN-16 00:00:00 PGM / RUN BY / DATE +RDLT MARKER NAME +RDLT MARKER NUMBER +GEODETIC MARKER TYPE +OBS AGENCY OBSERVER / AGENCY +5035K69749 Trimble NetR9 5.03 REC # / TYPE / VERS +1912118081 TRM57971.00 NONE ANT # / TYPE + 2104228.6921 -5642017.3992 2095406.0835 APPROX POSITION XYZ + 0.0001 0.0000 0.0000 ANTENNA: DELTA H/E/N +G 12 C1C L1C S1C C2W L2W S2W C2X L2X S2X C5X L5X S5X SYS / # / OBS TYPES +R 12 C1C L1C S1C C1P L1P S1P C2C L2C S2C C2P L2P S2P SYS / # / OBS TYPES +E 12 C1X L1X S1X C5X L5X S5X C7X L7X S7X C8X L8X S8X SYS / # / OBS TYPES +C 9 C1I L1I S1I C7I L7I S7I C6I L6I S6I SYS / # / OBS TYPES + 15.000 INTERVAL + 2016 1 11 0 0 0.0000000 GPS TIME OF FIRST OBS +G pcvs-program-name http://example.com/GPS SYS / PCVS APPLIED +R pcvs-program-name http://example.com/GLONASS SYS / PCVS APPLIED +G L2X -0.25000 SYS / PHASE SHIFT +R L1P 0.25000 SYS / PHASE SHIFT +R L2C -0.25000 SYS / PHASE SHIFT +GIOVE-A if present is mapped to satellite ID 51 COMMENT +GIOVE-B if present is mapped to satellite ID 52 COMMENT +DBHZ SIGNAL STRENGTH UNIT + END OF HEADER +> 2016 1 11 0 0 0.0000000 0 26 0.000000000000 +R10 23544632.969 6 125506152.806 6 40.400 23544630.660 6 125506177.799 6 38.200 23544639.055 5 97615985.468 5 33.900 23544639.875 5 97615969.480 5 33.200 +G27 22399181.883 7 117708573.580 7 42.600 22399189.590 5 91721088.991 5 34.400 22399189.578 7 91721082.998 7 45.700 22399187.391 7 87899374.902 7 47.600 +R18 22459713.383 7 119891661.142 7 44.100 22459712.777 7 119891647.122 7 43.200 22459716.922 6 93249167.486 6 41.400 22459717.043 6 93249171.479 6 41.600 +G26 21002873.820 8 110371028.142 8 49.700 21002880.418 6 86003466.709 6 40.200 21002880.090 8 86003474.708 8 48.300 21002878.551 8 82420003.098 8 53.200 +G16 20350144.750 8 106940828.293 8 49.800 20350148.430 6 83330612.134 6 39.600 +R07 20830975.727 7 111510119.514 7 47.800 20830975.551 7 111509863.515 7 46.600 20830979.195 7 86729922.894 7 46.300 20830978.676 7 86729929.892 7 45.600 +G14 22921945.594 6 120455516.003 6 41.300 22921950.918 4 93861685.861 4 26.400 +R05 23430711.711 5 125250603.179 5 34.400 23430712.027 5 125250587.198 5 34.600 23430719.555 6 97417139.009 6 37.000 23430719.219 5 97417153.011 5 34.800 +R09 21444793.883 7 114514115.091 7 42.500 21444793.320 6 114514102.117 6 40.900 21444796.418 5 89066599.089 5 34.400 21444795.195 5 89066589.095 5 33.600 +G03 22823830.469 7 119939966.415 7 45.600 22823837.336 5 93459853.737 5 30.800 22823837.398 7 93459852.747 7 44.200 22823837.645 7 89565695.138 7 47.600 +E14 19173395.055 7 100756875.857 7 45.100 19173407.766 7 75240567.283 7 47.300 19173407.156 7 77203363.242 7 46.100 19173407.398 8 76221970.869 8 52.200 +E30 28191538.719 5 148147536.609 5 35.800 28191544.449 6 110629668.765 6 39.800 28191542.387 6 113515659.769 6 37.400 28191544.000 7 112072668.374 7 43.800 +E24 27394988.180 7 143961787.735 7 43.000 27395002.262 6 107504079.295 6 40.200 27395002.543 6 110308516.760 6 40.600 27395003.180 7 108906207.636 7 45.600 +E12 28549417.164 6 150028195.176 6 36.800 28549418.230 6 112034046.171 6 37.000 28549417.949 5 114956671.071 5 35.800 28549418.559 6 113495360.723 6 41.900 +E11 26195806.102 7 137660018.204 7 42.800 26195805.918 6 102798202.535 6 41.100 26195805.262 6 105479883.186 6 40.700 26195805.965 7 104139046.461 7 46.200 +R19 21754879.125 7 116374037.990 7 43.700 21754878.410 7 116374142.011 7 43.200 21754881.102 6 90513346.912 6 41.100 21754880.621 6 90513347.913 6 41.400 +G08 24653272.297 6 129553766.670 6 39.100 24653281.848 3 100951111.072 3 19.300 24653282.684 6 100951113.068 6 37.300 24653281.391 7 96744819.278 7 43.300 +G23 24165410.805 7 126990121.981 7 43.900 24165412.516 3 98953344.619 3 23.200 +G31 22537994.766 7 118437881.004 7 45.800 22537998.863 5 92289523.605 5 33.600 22537998.477 7 92289525.601 7 43.500 +R16 22214341.023 7 118665135.521 7 42.500 22214340.969 6 118665156.536 6 41.300 22214345.293 6 92295230.311 6 41.300 22214345.281 6 92295230.299 6 40.600 +G32 22246710.531 7 116907229.731 7 46.800 22246715.789 5 91096640.622 5 33.800 +G29 24862375.516 6 130652886.557 6 39.200 24862380.328 3 101807535.937 3 21.300 24862379.980 6 101807546.945 6 37.300 +G22 24093232.633 6 126610779.502 6 39.900 24093238.410 3 98657779.269 3 22.000 +R06 20075472.813 8 107126646.309 8 51.100 20075471.590 8 107126603.318 8 49.500 20075473.988 7 83320762.575 7 46.800 20075474.137 7 83320741.569 7 45.800 +C11 23697750.031 7 123400577.080 7 45.600 23697747.023 7 95421307.577 7 45.100 23697740.434 8 100273172.763 8 48.000 +C12 27062482.750 5 140921544.15415 32.400 27062480.508 5 31.100 27062473.086 5 35.500 +> 2016 1 11 0 0 5.0000000 3 5 0.000000000000 +R10 23544632.969 6 125506152.806 6 40.400 23544630.660 6 125506177.799 6 38.200 23544639.055 5 97615985.468 5 33.900 23544639.875 5 97615969.480 5 33.200 +G27 22399181.883 7 117708573.580 7 42.600 22399189.590 5 91721088.991 5 34.400 22399189.578 7 91721082.998 7 45.700 22399187.391 7 87899374.902 7 47.600 +R18 22459713.383 7 119891661.142 7 44.100 22459712.777 7 119891647.122 7 43.200 22459716.922 6 93249167.486 6 41.400 22459717.043 6 93249171.479 6 41.600 +G26 21002873.820 8 110371028.142 8 49.700 21002880.418 6 86003466.709 6 40.200 21002880.090 8 86003474.708 8 48.300 21002878.551 8 82420003.098 8 53.200 +G16 20350144.750 8 106940828.293 8 49.800 20350148.430 6 83330612.134 6 39.600 +> 2016 1 11 0 0 10.0000000 6 26 0.000000000000 +R10 23544632.969 6 125506152.806 6 40.400 23544630.660 6 125506177.799 6 38.200 23544639.055 5 97615985.468 5 33.900 23544639.875 5 97615969.480 5 33.200 +G27 22399181.883 7 117708573.580 7 42.600 22399189.590 5 91721088.991 5 34.400 22399189.578 7 91721082.998 7 45.700 22399187.391 7 87899374.902 7 47.600 +R18 22459713.383 7 119891661.142 7 44.100 22459712.777 7 119891647.122 7 43.200 22459716.922 6 93249167.486 6 41.400 22459717.043 6 93249171.479 6 41.600 +G26 21002873.820 8 110371028.142 8 49.700 21002880.418 6 86003466.709 6 40.200 21002880.090 8 86003474.708 8 48.300 21002878.551 8 82420003.098 8 53.200 +G16 20350144.750 8 106940828.293 8 49.800 20350148.430 6 83330612.134 6 39.600 +R07 20830975.727 7 111510119.514 7 47.800 20830975.551 7 111509863.515 7 46.600 20830979.195 7 86729922.894 7 46.300 20830978.676 7 86729929.892 7 45.600 +G14 22921945.594 6 120455516.003 6 41.300 22921950.918 4 93861685.861 4 26.400 +R05 23430711.711 5 125250603.179 5 34.400 23430712.027 5 125250587.198 5 34.600 23430719.555 6 97417139.009 6 37.000 23430719.219 5 97417153.011 5 34.800 +R09 21444793.883 7 114514115.091 7 42.500 21444793.320 6 114514102.117 6 40.900 21444796.418 5 89066599.089 5 34.400 21444795.195 5 89066589.095 5 33.600 +G03 22823830.469 7 119939966.415 7 45.600 22823837.336 5 93459853.737 5 30.800 22823837.398 7 93459852.747 7 44.200 22823837.645 7 89565695.138 7 47.600 +E14 19173395.055 7 100756875.857 7 45.100 19173407.766 7 75240567.283 7 47.300 19173407.156 7 77203363.242 7 46.100 19173407.398 8 76221970.869 8 52.200 +E30 28191538.719 5 148147536.609 5 35.800 28191544.449 6 110629668.765 6 39.800 28191542.387 6 113515659.769 6 37.400 28191544.000 7 112072668.374 7 43.800 +E24 27394988.180 7 143961787.735 7 43.000 27395002.262 6 107504079.295 6 40.200 27395002.543 6 110308516.760 6 40.600 27395003.180 7 108906207.636 7 45.600 +E12 28549417.164 6 150028195.176 6 36.800 28549418.230 6 112034046.171 6 37.000 28549417.949 5 114956671.071 5 35.800 28549418.559 6 113495360.723 6 41.900 +E11 26195806.102 7 137660018.204 7 42.800 26195805.918 6 102798202.535 6 41.100 26195805.262 6 105479883.186 6 40.700 26195805.965 7 104139046.461 7 46.200 +R19 21754879.125 7 116374037.990 7 43.700 21754878.410 7 116374142.011 7 43.200 21754881.102 6 90513346.912 6 41.100 21754880.621 6 90513347.913 6 41.400 +G08 24653272.297 6 129553766.670 6 39.100 24653281.848 3 100951111.072 3 19.300 24653282.684 6 100951113.068 6 37.300 24653281.391 7 96744819.278 7 43.300 +G23 24165410.805 7 126990121.981 7 43.900 24165412.516 3 98953344.619 3 23.200 +G31 22537994.766 7 118437881.004 7 45.800 22537998.863 5 92289523.605 5 33.600 22537998.477 7 92289525.601 7 43.500 +R16 22214341.023 7 118665135.521 7 42.500 22214340.969 6 118665156.536 6 41.300 22214345.293 6 92295230.311 6 41.300 22214345.281 6 92295230.299 6 40.600 +G32 22246710.531 7 116907229.731 7 46.800 22246715.789 5 91096640.622 5 33.800 +G29 24862375.516 6 130652886.557 6 39.200 24862380.328 3 101807535.937 3 21.300 24862379.980 6 101807546.945 6 37.300 +G22 24093232.633 6 126610779.502 6 39.900 24093238.410 3 98657779.269 3 22.000 +R06 20075472.813 8 107126646.309 8 51.100 20075471.590 8 107126603.318 8 49.500 20075473.988 7 83320762.575 7 46.800 20075474.137 7 83320741.569 7 45.800 +C11 23697750.031 7 123400577.080 7 45.600 23697747.023 7 95421307.577 7 45.100 23697740.434 8 100273172.763 8 48.000 +C12 27062482.750 5 140921544.15415 32.400 27062480.508 5 31.100 27062473.086 5 35.500 +> 2016 1 11 0 0 15.0000000 0 25 0.000000000000 +R10 23541822.883 6 125491179.667 6 39.900 23541821.980 6 125491204.714 6 38.600 23541830.535 5 97604339.702 5 33.800 23541830.195 5 97604323.698 5 32.500 +G27 22390089.813 7 117660791.303 7 43.700 22390097.008 5 91683856.088 5 34.200 22390097.258 7 91683850.084 7 45.700 22390094.574 8 87863693.364 8 48.200 +R18 22461330.953 7 119900293.784 7 44.500 22461330.230 7 119900279.790 7 43.200 22461333.328 6 93255881.752 6 40.700 22461333.949 6 93255885.746 6 40.400 +G26 21007765.734 8 110396735.006 8 48.100 21007772.539 6 86023498.042 6 40.000 21007772.422 8 86023506.041 8 48.800 21007770.496 8 82439199.789 8 53.200 +G16 20350618.766 8 106943319.813 8 49.800 20350622.262 6 83332553.587 6 39.900 +R07 20826810.797 8 111487820.536 8 48.300 20826810.035 7 111487564.567 7 46.700 20826813.758 7 86712579.234 7 45.600 20826813.270 7 86712586.247 7 45.600 +G14 22929588.117 6 120495679.654 6 41.100 22929593.246 4 93892982.204 4 26.900 +R05 23442718.047 5 125314775.373 5 35.100 23442716.484 5 125314759.352 5 34.600 23442724.129 6 97467050.427 6 37.000 23442724.004 5 97467064.444 5 35.600 +R09 21445428.398 7 114517507.148 7 42.100 21445428.305 6 114517494.145 6 40.800 21445430.496 5 89069237.329 5 32.800 21445430.223 5 89069227.342 5 32.700 +G03 22824353.281 7 119942712.783 7 44.800 22824359.664 5 93461993.762 5 30.200 22824360.504 7 93461992.763 7 42.500 22824360.266 7 89567745.983 7 47.500 +E14 19179711.938 7 100790074.692 7 44.900 19179724.559 7 75265358.616 7 47.200 19179724.277 7 77228801.308 7 45.600 19179724.961 8 76247085.572 8 52.000 +E30 28188658.539 5 148132401.860 5 35.000 28188663.875 6 110618366.880 6 38.300 28188663.313 6 113504063.049 6 37.600 28188664.043 7 112061219.072 7 43.200 +E24 27398635.359 7 143980954.498 7 42.400 27398650.797 6 107518392.117 6 41.000 27398650.703 6 110323202.975 6 40.900 27398650.543 7 108920707.147 7 46.100 +E12 28551796.906 6 150040700.172 6 36.800 28551797.871 6 112043384.301 6 36.000 28551798.059 6 114966252.791 6 36.200 28551797.938 6 113504820.648 6 41.800 +E11 26193471.602 7 137647749.667 7 42.700 26193471.422 6 102789040.978 6 41.200 26193471.188 6 105470482.629 6 40.900 26193471.313 7 104129765.404 7 46.400 +R19 21747571.195 7 116334944.874 7 44.700 21747569.777 7 116335048.882 7 43.800 21747573.406 7 90482941.173 7 42.200 21747572.980 6 90482942.168 6 41.800 +G08 24643301.820 6 129501375.983 6 38.200 24643311.973 3 100910287.287 3 18.300 24643312.762 6 100910289.320 6 37.900 24643311.574 7 96705696.559 7 43.100 +G23 24155592.938 7 126938527.803 7 43.900 24155594.582 3 98913141.378 3 22.300 +G31 22540182.320 7 118449375.793 7 45.100 22540186.367 5 92298480.581 5 33.500 22540186.055 7 92298482.585 7 43.400 +R16 22218050.789 7 118684949.165 7 42.100 22218049.680 6 118684970.174 6 41.600 22218054.215 6 92310640.920 6 41.600 22218054.367 6 92310640.917 6 40.200 +G32 22251707.039 7 116933488.548 7 45.300 22251712.820 5 91117102.036 5 34.200 +G29 24869774.320 6 130691766.672 6 39.500 24869778.559 3 101837832.142 3 20.100 24869778.586 6 101837843.157 6 36.300 +G22 24101437.422 6 126653896.112 6 39.100 24101442.555 3 98691376.495 3 21.700 +R06 20082117.195 8 107162099.573 8 50.100 20082115.621 8 107162056.568 8 49.200 20082118.176 7 83348337.306 7 47.600 20082118.441 7 83348316.313 7 45.700 +C11 23697974.297 7 123401746.690 7 45.300 23697971.738 7 95422212.006 7 45.200 23697965.219 7 100274123.185 7 47.900 diff --git a/src/test/resources/rinex/inconsistent-number-of-sats.00o b/src/test/resources/rinex/inconsistent-number-of-sats.00o new file mode 100644 index 0000000000..7f3cea9bf0 --- /dev/null +++ b/src/test/resources/rinex/inconsistent-number-of-sats.00o @@ -0,0 +1,72 @@ + 2.10 OBSERVATION DATA M (MIXED) RINEX VERSION / TYPE +BLANK OR G = GPS, R = GLONASS, T = TRANSIT, M = MIXED COMMENT +XXRINEXO V9.9 AIUB 24-MAR-01 14:43 PGM / RUN BY / DATE +EXAMPLE OF A MIXED RINEX FILE COMMENT +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER +BILL SMITH ABC INSTITUTE OBSERVER / AGENCY +X1234A123 XX ZZZ REC # / TYPE / VERS +234 YY ANT # / TYPE + 4375274. 587466. 4589095. APPROX POSITION XYZ + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + 1 1 WAVELENGTH FACT L1/2 + 1 2 6 G14 G15 G16 G17 G18 G19 WAVELENGTH FACT L1/2 + 0 RCV CLOCK OFFS APPL + 2 # OF SATELLITES + 4 P1 L1 L2 P2 # / TYPES OF OBSERV + 18.000 INTERVAL + 2001 3 24 13 10 36.0000000 GPS TIME OF FIRST OBS + END OF HEADER + 01 3 24 13 10 36.0000000 0 3G12G 9G 6 -.123456789 + 23629347.915 .300 8 -.353 23629364.158 + 20891534.648 -.120 9 -.358 20891541.292 + 20607600.189 -.430 9 .394 20607605.84844 + 01 3 24 13 10 50.0000000 4 4 + 1 2 2 G 9 G12 WAVELENGTH FACT L1/2 + *** WAVELENGTH FACTOR CHANGED FOR 2 SATELLITES *** COMMENT + NOW 8 SATELLITES HAVE WL FACT 1 AND 2! COMMENT + COMMENT + 01 3 24 13 10 54.0000000 0 5G12G 9G 6R21R22 -.123456789 + 23619095.450 -53875.632 8 -41981.375 23619112.008 + 20886075.667 -28688.027 9 -22354.535 20886082.101 + 20611072.689 18247.789 9 14219.770 20611078.410 + 21345678.576 12345.567 5 + 22123456.789 23456.789 5 + 01 3 24 13 11 0.0000000 2 1 + *** FROM NOW ON KINEMATIC DATA! *** COMMENT + 01 3 24 13 11 48.0000000 0 4G16G12G 9G 6 -.123456789 + 21110991.756 16119.980 7 12560.510 21110998.441 + 23588424.398 -215050.557 6 -167571.734 23588439.570 + 20869878.790 -113803.187 8 -88677.926 20869884.938 + 20621643.727 73797.462 7 57505.177 20621649.276 + 3 4 +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + --> THIS IS THE START OF A NEW SITE <-- COMMENT + 01 3 24 13 12 6.0000000 0 4G16G12G 6G 9 -.123456987 + 21112589.384 24515.877 6 19102.763 3 21112596.187 + 23578228.338 -268624.234 7 -209317.284 4 23578244.398 + 20625218.088 92581.207 7 72141.846 4 20625223.795 + 20864539.693 -141858.836 8 -110539.435 5 20864545.943 + 01 3 24 13 13 1.2345678 5 0 + 4 1 + (AN EVENT FLAG WITH SIGNIFICANT EPOCH) COMMENT + 01 3 24 13 14 12.0000000 0 4G16G12G 9G 6 -.123456012 + 21124965.133 89551.30216 69779.62654 21124972.2754 + 23507272.372 -212616.150 7 -165674.789 5 23507288.421 + 20828010.354 -333820.093 6 -260119.395 5 20828017.129 + 20650944.902 227775.130 7 177487.651 4 20650950.363 + 4 1 + *** ANTISPOOFING ON G 16 AND LOST LOCK COMMENT + 01 3 24 13 14 12.0000000 6 2G16G 9 + 123456789.0 -9876543.5 + 0.0 -0.5 + 4 2 + ---> CYCLE SLIPS THAT HAVE BEEN APPLIED TO COMMENT + THE OBSERVATIONS COMMENT + 01 3 24 13 14 48.0000000 0 4G16G12G 9G 6 -.123456234 + 21128884.159 110143.144 7 85825.18545 21128890.7764 + 23487131.045 -318463.297 7 -248152.72824 23487146.149 + 20817844.743 -387242.571 6 -301747.22925 20817851.322 + 20658519.895 267583.67817 208507.26234 20658525.869 diff --git a/src/test/resources/rinex/inconsistent-satellite-system.00o b/src/test/resources/rinex/inconsistent-satellite-system.00o new file mode 100644 index 0000000000..098893e53f --- /dev/null +++ b/src/test/resources/rinex/inconsistent-satellite-system.00o @@ -0,0 +1,71 @@ + 2.10 OBSERVATION DATA E RINEX VERSION / TYPE +BLANK OR G = GPS, R = GLONASS, T = TRANSIT, M = MIXED COMMENT +XXRINEXO V9.9 AIUB 24-MAR-01 14:43 PGM / RUN BY / DATE +EXAMPLE OF A MIXED RINEX FILE COMMENT +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER +BILL SMITH ABC INSTITUTE OBSERVER / AGENCY +X1234A123 XX ZZZ REC # / TYPE / VERS +234 YY ANT # / TYPE + 4375274. 587466. 4589095. APPROX POSITION XYZ + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + 1 1 WAVELENGTH FACT L1/2 + 1 2 6 G14 G15 G16 G17 G18 G19 WAVELENGTH FACT L1/2 + 0 RCV CLOCK OFFS APPL + 4 P1 L1 L2 P2 # / TYPES OF OBSERV + 18.000 INTERVAL + 2001 3 24 13 10 36.0000000 GPS TIME OF FIRST OBS + END OF HEADER + 01 3 24 13 10 36.0000000 0 3G12G 9G 6 -.123456789 + 23629347.915 .300 8 -.353 23629364.158 + 20891534.648 -.120 9 -.358 20891541.292 + 20607600.189 -.430 9 .394 20607605.84844 + 01 3 24 13 10 50.0000000 4 4 + 1 2 2 G 9 G12 WAVELENGTH FACT L1/2 + *** WAVELENGTH FACTOR CHANGED FOR 2 SATELLITES *** COMMENT + NOW 8 SATELLITES HAVE WL FACT 1 AND 2! COMMENT + COMMENT + 01 3 24 13 10 54.0000000 0 5G12G 9G 6R21R22 -.123456789 + 23619095.450 -53875.632 8 -41981.375 23619112.008 + 20886075.667 -28688.027 9 -22354.535 20886082.101 + 20611072.689 18247.789 9 14219.770 20611078.410 + 21345678.576 12345.567 5 + 22123456.789 23456.789 5 + 01 3 24 13 11 0.0000000 2 1 + *** FROM NOW ON KINEMATIC DATA! *** COMMENT + 01 3 24 13 11 48.0000000 0 4G16G12G 9G 6 -.123456789 + 21110991.756 16119.980 7 12560.510 21110998.441 + 23588424.398 -215050.557 6 -167571.734 23588439.570 + 20869878.790 -113803.187 8 -88677.926 20869884.938 + 20621643.727 73797.462 7 57505.177 20621649.276 + 3 4 +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + --> THIS IS THE START OF A NEW SITE <-- COMMENT + 01 3 24 13 12 6.0000000 0 4G16G12G 6G 9 -.123456987 + 21112589.384 24515.877 6 19102.763 3 21112596.187 + 23578228.338 -268624.234 7 -209317.284 4 23578244.398 + 20625218.088 92581.207 7 72141.846 4 20625223.795 + 20864539.693 -141858.836 8 -110539.435 5 20864545.943 + 01 3 24 13 13 1.2345678 5 0 + 4 1 + (AN EVENT FLAG WITH SIGNIFICANT EPOCH) COMMENT + 01 3 24 13 14 12.0000000 0 4G16G12G 9G 6 -.123456012 + 21124965.133 89551.30216 69779.62654 21124972.2754 + 23507272.372 -212616.150 7 -165674.789 5 23507288.421 + 20828010.354 -333820.093 6 -260119.395 5 20828017.129 + 20650944.902 227775.130 7 177487.651 4 20650950.363 + 4 1 + *** ANTISPOOFING ON G 16 AND LOST LOCK COMMENT + 01 3 24 13 14 12.0000000 6 2G16G 9 + 123456789.0 -9876543.5 + 0.0 -0.5 + 4 2 + ---> CYCLE SLIPS THAT HAVE BEEN APPLIED TO COMMENT + THE OBSERVATIONS COMMENT + 01 3 24 13 14 48.0000000 0 4G16G12G 9G 6 -.123456234 + 21128884.159 110143.144 7 85825.18545 21128890.7764 + 23487131.045 -318463.297 7 -248152.72824 23487146.149 + 20817844.743 -387242.571 6 -301747.22925 20817851.322 + 20658519.895 267583.67817 208507.26234 20658525.869 diff --git a/src/test/resources/rinex/no-time-scale.07o b/src/test/resources/rinex/no-time-scale.07o new file mode 100644 index 0000000000..c835897876 --- /dev/null +++ b/src/test/resources/rinex/no-time-scale.07o @@ -0,0 +1,35 @@ + 2.20 OBSERVATION DATA SBAS RINEX VERSION / TYPE +edited 20191216 135342 UTC PGM / RUN BY / DATE +MANUALLY EDITED RINEX FILE ONLY FOR TEST PURPOSES COMMENT +XYZ9 MARKER NAME +9999 MARKER NUMBER +SPACEBORNE MARKER TYPE +NOBODY OREKIT OBSERVER / AGENCY +9876543 MY_TYPE 0.0.1 REC # / TYPE / VERS +17 MY_ANTENNA ABC ANT # / TYPE + 0.0083 0.0000 0.0000 ANTENNA: DELTA H/E/N + 1.1111 2.2222 3.3333 ANTENNA: DELTA X/Y/Z + 9.9999 8.8888 7.7777 ANTENNA: B.SIGHT XYZ + 0.1455 -0.3421 0.0024 CENTER OF MASS: XYZ + 1 1 0 WAVELENGTH FACT L1/2 + 9 L1 L2 P1 P2 C1 LA S1 S2 SA# / TYPES OF OBSERV + 10.000 INTERVAL + 9 # OF SATELLITES + 2007 9 29 0 0 0.0000000 TIME OF FIRST OBS + 2007 9 29 0 0 0.0000000 TIME OF LAST OBS + END OF HEADER + 07 9 29 0 0 0.0000000 0 7 1 5 9 12 14 22 30 + 467614.70106 364374.76806 24554538.88000 24554544.98800 24554539.18900 + 467614.71300 30.00000 40.00000 142.00000 + -9534398.75908 -7429403.18808 20429237.66700 20429243.48400 20429238.30300 + -9534398.75900 359.00000 353.00000 659.00000 + 8159697.08906 6358202.92807 23305299.04600 23305303.73300 23305298.21400 + 8159697.08800 46.00000 63.00000 203.00000 + 11013515.41508 8581958.92308 22201472.20200 22201476.86900 22201472.33900 + 11013515.41500 171.00000 205.00000 443.00000 + -14800471.86108 -11532836.37808 21466044.52200 21466049.85800 21466044.96800 + -14800471.86100 183.00000 197.00000 446.00000 + -7326510.50108 -5823497.87408 21168675.13800 21168678.78200 21168675.41500 + -7326510.50100 231.00000 336.00000 561.00000 + 1370942.80909 1068266.89609 19786351.71000 19786356.76200 19786351.44800 + 1370942.80900 631.00000 630.00000 918.00000 diff --git a/src/test/resources/rinex/number-format-error.06o b/src/test/resources/rinex/number-format-error.06o new file mode 100644 index 0000000000..0c599cf56e --- /dev/null +++ b/src/test/resources/rinex/number-format-error.06o @@ -0,0 +1,36 @@ + 3.04 OBSERVATION DATA M RINEX VERSION / TYPE +G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED COMMENT +XXRINEXO V9.9 AIUB 20060324 144333 UTC PGM / RUN BY / DATE +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER +BILL SMITH ABC INSTITUTE OBSERVER / AGENCY +X1234A123 GEODETIC 1.3.1 REC # / TYPE / VERS +G1234 ROVER ANT # / TYPE + 2104228. -5642017. 2095406. APPROX POSITION XYZ + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + 0 RCV CLOCK OFFS APPL +G 5 C1C L1W L2W C1W S2W SYS / # / OBS TYPES +R 2 C1C L1C SYS / # / OBS TYPES +E 2 L1B L5I SYS / # / OBS TYPES +S 2 C1C L1C SYS / # / OBS TYPES + 18.000 INTERVAL +DBHZ SIGNAL STRENGTH UNIT + 2006 03 24 13 10 36.0000000 GPS TIME OF FIRST OBS + 18 R01 1 R02 2 R03 3 R04 4 R05 5 R06 -6 R07 -5 R08 -4 GLONASS SLOT / FRQ # + R09 -3 R10 -2 R11 -1 R12 0 R13 1 R14 2 R15 0 R16 4 GLONASS SLOT / FRQ # + R17 5 R18 -5 GLONASS SLOT / FRQ # +G L1C SYS / PHASE SHIFT +G L1W SYS / PHASE SHIFT +G L2W 0.00000 SYS / PHASE SHIFT +R L1C 0.00000 SYS / PHASE SHIFT +E L1B 0.00000 SYS / PHASE SHIFT +E L5I 0.00000 SYS / PHASE SHIFT +S L1C 0.00000 SYS / PHASE SHIFT + C1C 0.000 C2C 0.000 C2P 0.000 GLONASS COD/PHS/BIS + END OF HEADER +> 2016 03 24 13 10 36.0000000 0 5 0.000000000000 +G06 23629347.915 651379.300 8 23629347.158 24.158 +G09 20891534.648 -45####.120 9 20891545.292 38.123 +G12 20607600.189 -756349.430 9 20891545.848 35.234 +E11 20742800.324 8 378849.178 7 +S20 38137559.506 8 335849.135 9 \ No newline at end of file diff --git a/src/test/resources/rinex/unknown-event-flag-3.00o b/src/test/resources/rinex/unknown-event-flag-3.00o new file mode 100644 index 0000000000..19d177e677 --- /dev/null +++ b/src/test/resources/rinex/unknown-event-flag-3.00o @@ -0,0 +1,71 @@ + 2.10 OBSERVATION DATA M (MIXED) RINEX VERSION / TYPE +BLANK OR G = GPS, R = GLONASS, T = TRANSIT, M = MIXED COMMENT +XXRINEXO V9.9 AIUB 24-MAR-01 14:43 PGM / RUN BY / DATE +EXAMPLE OF A MIXED RINEX FILE COMMENT +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER +BILL SMITH ABC INSTITUTE OBSERVER / AGENCY +X1234A123 XX ZZZ REC # / TYPE / VERS +234 YY ANT # / TYPE + 4375274. 587466. 4589095. APPROX POSITION XYZ + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + 1 1 WAVELENGTH FACT L1/2 + 1 2 6 G14 G15 G16 G17 G18 G19 WAVELENGTH FACT L1/2 + 0 RCV CLOCK OFFS APPL + 4 P1 L1 L2 P2 # / TYPES OF OBSERV + 18.000 INTERVAL + 2001 3 24 13 10 36.0000000 GPS TIME OF FIRST OBS + END OF HEADER + 01 3 24 13 10 36.0000000 0 3G12G 9G 6 -.123456789 + 23629347.915 .300 8 -.353 23629364.158 + 20891534.648 -.120 9 -.358 20891541.292 + 20607600.189 -.430 9 .394 20607605.84844 + 01 3 24 13 10 50.0000000 4 4 + 1 2 2 G 9 G12 WAVELENGTH FACT L1/2 + *** WAVELENGTH FACTOR CHANGED FOR 2 SATELLITES *** COMMENT + NOW 8 SATELLITES HAVE WL FACT 1 AND 2! COMMENT + COMMENT + 01 3 24 13 10 54.0000000 0 5G12G 9G 6R21R22 -.123456789 + 23619095.450 -53875.632 8 -41981.375 23619112.008 + 20886075.667 -28688.027 9 -22354.535 20886082.101 + 20611072.689 18247.789 9 14219.770 20611078.410 + 21345678.576 12345.567 5 + 22123456.789 23456.789 5 + 01 3 24 13 11 0.0000000 2 1 + *** FROM NOW ON KINEMATIC DATA! *** COMMENT + 01 3 24 13 11 48.0000000 0 4G16G12G 9G 6 -.123456789 + 21110991.756 16119.980 7 12560.510 21110998.441 + 23588424.398 -215050.557 6 -167571.734 23588439.570 + 20869878.790 -113803.187 8 -88677.926 20869884.938 + 20621643.727 73797.462 7 57505.177 20621649.276 + 3 4 +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + --> THIS IS THE START OF A NEW SITE <-- COMMENT + 01 3 24 13 12 6.0000000 0 4G16G12G 6G 9 -.123456987 + 21112589.384 24515.877 6 19102.763 3 21112596.187 + 23578228.338 -268624.234 7 -209317.284 4 23578244.398 + 20625218.088 92581.207 7 72141.846 4 20625223.795 + 20864539.693 -141858.836 8 -110539.435 5 20864545.943 + 01 3 24 13 13 1.2345678 5 0 + 4 1 + (AN EVENT FLAG WITH SIGNIFICANT EPOCH) COMMENT + 01 3 24 13 14 12.0000000 0 4G16G12G 9G 6 -.123456012 + 21124965.133 89551.30216 69779.62654 21124972.2754 + 23507272.372 -212616.150 7 -165674.789 5 23507288.421 + 20828010.354 -333820.093 6 -260119.395 5 20828017.129 + 20650944.902 227775.130 7 177487.651 4 20650950.363 + 4 1 + *** ANTISPOOFING ON G 16 AND LOST LOCK COMMENT + 01 3 24 13 14 12.0000000 8 2G16G 9 + 123456789.0 -9876543.5 + 0.0 -0.5 + 4 2 + ---> CYCLE SLIPS THAT HAVE BEEN APPLIED TO COMMENT + THE OBSERVATIONS COMMENT + 01 3 24 13 14 48.0000000 0 4G16G12G 9G 6 -.123456234 + 21128884.159 110143.144 7 85825.18545 21128890.7764 + 23487131.045 -318463.297 7 -248152.72824 23487146.149 + 20817844.743 -387242.571 6 -301747.22925 20817851.322 + 20658519.895 267583.67817 208507.26234 20658525.869 diff --git a/src/test/resources/rinex/unknown-event-flag-4.00o b/src/test/resources/rinex/unknown-event-flag-4.00o new file mode 100644 index 0000000000..80e3f86e4d --- /dev/null +++ b/src/test/resources/rinex/unknown-event-flag-4.00o @@ -0,0 +1,111 @@ + 3.02 OBSERVATION DATA M (MIXED) RINEX VERSION / TYPE +NetR9 5.03 Receiver Operator 11-JAN-16 00:00:00 PGM / RUN BY / DATE +RDLT MARKER NAME +RDLT MARKER NUMBER +GEODETIC MARKER TYPE +OBS AGENCY OBSERVER / AGENCY +5035K69749 Trimble NetR9 5.03 REC # / TYPE / VERS +1912118081 TRM57971.00 NONE ANT # / TYPE + 2104228.6921 -5642017.3992 2095406.0835 APPROX POSITION XYZ + 0.0001 0.0000 0.0000 ANTENNA: DELTA H/E/N +G 12 C1C L1C S1C C2W L2W S2W C2X L2X S2X C5X L5X S5X SYS / # / OBS TYPES +R 12 C1C L1C S1C C1P L1P S1P C2C L2C S2C C2P L2P S2P SYS / # / OBS TYPES +E 12 C1X L1X S1X C5X L5X S5X C7X L7X S7X C8X L8X S8X SYS / # / OBS TYPES +C 9 C1I L1I S1I C7I L7I S7I C6I L6I S6I SYS / # / OBS TYPES + 15.000 INTERVAL + 2016 1 11 0 0 0.0000000 GPS TIME OF FIRST OBS +G pcvs-program-name http://example.com/GPS SYS / PCVS APPLIED +R pcvs-program-name http://example.com/GLONASS SYS / PCVS APPLIED +G L2X -0.25000 SYS / PHASE SHIFT +R L1P 0.25000 SYS / PHASE SHIFT +R L2C -0.25000 SYS / PHASE SHIFT +GIOVE-A if present is mapped to satellite ID 51 COMMENT +GIOVE-B if present is mapped to satellite ID 52 COMMENT +DBHZ SIGNAL STRENGTH UNIT + END OF HEADER +> 2016 1 11 0 0 0.0000000 0 26 0.000000000000 +R10 23544632.969 6 125506152.806 6 40.400 23544630.660 6 125506177.799 6 38.200 23544639.055 5 97615985.468 5 33.900 23544639.875 5 97615969.480 5 33.200 +G27 22399181.883 7 117708573.580 7 42.600 22399189.590 5 91721088.991 5 34.400 22399189.578 7 91721082.998 7 45.700 22399187.391 7 87899374.902 7 47.600 +R18 22459713.383 7 119891661.142 7 44.100 22459712.777 7 119891647.122 7 43.200 22459716.922 6 93249167.486 6 41.400 22459717.043 6 93249171.479 6 41.600 +G26 21002873.820 8 110371028.142 8 49.700 21002880.418 6 86003466.709 6 40.200 21002880.090 8 86003474.708 8 48.300 21002878.551 8 82420003.098 8 53.200 +G16 20350144.750 8 106940828.293 8 49.800 20350148.430 6 83330612.134 6 39.600 +R07 20830975.727 7 111510119.514 7 47.800 20830975.551 7 111509863.515 7 46.600 20830979.195 7 86729922.894 7 46.300 20830978.676 7 86729929.892 7 45.600 +G14 22921945.594 6 120455516.003 6 41.300 22921950.918 4 93861685.861 4 26.400 +R05 23430711.711 5 125250603.179 5 34.400 23430712.027 5 125250587.198 5 34.600 23430719.555 6 97417139.009 6 37.000 23430719.219 5 97417153.011 5 34.800 +R09 21444793.883 7 114514115.091 7 42.500 21444793.320 6 114514102.117 6 40.900 21444796.418 5 89066599.089 5 34.400 21444795.195 5 89066589.095 5 33.600 +G03 22823830.469 7 119939966.415 7 45.600 22823837.336 5 93459853.737 5 30.800 22823837.398 7 93459852.747 7 44.200 22823837.645 7 89565695.138 7 47.600 +E14 19173395.055 7 100756875.857 7 45.100 19173407.766 7 75240567.283 7 47.300 19173407.156 7 77203363.242 7 46.100 19173407.398 8 76221970.869 8 52.200 +E30 28191538.719 5 148147536.609 5 35.800 28191544.449 6 110629668.765 6 39.800 28191542.387 6 113515659.769 6 37.400 28191544.000 7 112072668.374 7 43.800 +E24 27394988.180 7 143961787.735 7 43.000 27395002.262 6 107504079.295 6 40.200 27395002.543 6 110308516.760 6 40.600 27395003.180 7 108906207.636 7 45.600 +E12 28549417.164 6 150028195.176 6 36.800 28549418.230 6 112034046.171 6 37.000 28549417.949 5 114956671.071 5 35.800 28549418.559 6 113495360.723 6 41.900 +E11 26195806.102 7 137660018.204 7 42.800 26195805.918 6 102798202.535 6 41.100 26195805.262 6 105479883.186 6 40.700 26195805.965 7 104139046.461 7 46.200 +R19 21754879.125 7 116374037.990 7 43.700 21754878.410 7 116374142.011 7 43.200 21754881.102 6 90513346.912 6 41.100 21754880.621 6 90513347.913 6 41.400 +G08 24653272.297 6 129553766.670 6 39.100 24653281.848 3 100951111.072 3 19.300 24653282.684 6 100951113.068 6 37.300 24653281.391 7 96744819.278 7 43.300 +G23 24165410.805 7 126990121.981 7 43.900 24165412.516 3 98953344.619 3 23.200 +G31 22537994.766 7 118437881.004 7 45.800 22537998.863 5 92289523.605 5 33.600 22537998.477 7 92289525.601 7 43.500 +R16 22214341.023 7 118665135.521 7 42.500 22214340.969 6 118665156.536 6 41.300 22214345.293 6 92295230.311 6 41.300 22214345.281 6 92295230.299 6 40.600 +G32 22246710.531 7 116907229.731 7 46.800 22246715.789 5 91096640.622 5 33.800 +G29 24862375.516 6 130652886.557 6 39.200 24862380.328 3 101807535.937 3 21.300 24862379.980 6 101807546.945 6 37.300 +G22 24093232.633 6 126610779.502 6 39.900 24093238.410 3 98657779.269 3 22.000 +R06 20075472.813 8 107126646.309 8 51.100 20075471.590 8 107126603.318 8 49.500 20075473.988 7 83320762.575 7 46.800 20075474.137 7 83320741.569 7 45.800 +C11 23697750.031 7 123400577.080 7 45.600 23697747.023 7 95421307.577 7 45.100 23697740.434 8 100273172.763 8 48.000 +C12 27062482.750 5 140921544.15415 32.400 27062480.508 5 31.100 27062473.086 5 35.500 +> 2016 1 11 0 0 5.0000000 8 5 0.000000000000 +R10 23544632.969 6 125506152.806 6 40.400 23544630.660 6 125506177.799 6 38.200 23544639.055 5 97615985.468 5 33.900 23544639.875 5 97615969.480 5 33.200 +G27 22399181.883 7 117708573.580 7 42.600 22399189.590 5 91721088.991 5 34.400 22399189.578 7 91721082.998 7 45.700 22399187.391 7 87899374.902 7 47.600 +R18 22459713.383 7 119891661.142 7 44.100 22459712.777 7 119891647.122 7 43.200 22459716.922 6 93249167.486 6 41.400 22459717.043 6 93249171.479 6 41.600 +G26 21002873.820 8 110371028.142 8 49.700 21002880.418 6 86003466.709 6 40.200 21002880.090 8 86003474.708 8 48.300 21002878.551 8 82420003.098 8 53.200 +G16 20350144.750 8 106940828.293 8 49.800 20350148.430 6 83330612.134 6 39.600 +> 2016 1 11 0 0 10.0000000 6 26 0.000000000000 +R10 23544632.969 6 125506152.806 6 40.400 23544630.660 6 125506177.799 6 38.200 23544639.055 5 97615985.468 5 33.900 23544639.875 5 97615969.480 5 33.200 +G27 22399181.883 7 117708573.580 7 42.600 22399189.590 5 91721088.991 5 34.400 22399189.578 7 91721082.998 7 45.700 22399187.391 7 87899374.902 7 47.600 +R18 22459713.383 7 119891661.142 7 44.100 22459712.777 7 119891647.122 7 43.200 22459716.922 6 93249167.486 6 41.400 22459717.043 6 93249171.479 6 41.600 +G26 21002873.820 8 110371028.142 8 49.700 21002880.418 6 86003466.709 6 40.200 21002880.090 8 86003474.708 8 48.300 21002878.551 8 82420003.098 8 53.200 +G16 20350144.750 8 106940828.293 8 49.800 20350148.430 6 83330612.134 6 39.600 +R07 20830975.727 7 111510119.514 7 47.800 20830975.551 7 111509863.515 7 46.600 20830979.195 7 86729922.894 7 46.300 20830978.676 7 86729929.892 7 45.600 +G14 22921945.594 6 120455516.003 6 41.300 22921950.918 4 93861685.861 4 26.400 +R05 23430711.711 5 125250603.179 5 34.400 23430712.027 5 125250587.198 5 34.600 23430719.555 6 97417139.009 6 37.000 23430719.219 5 97417153.011 5 34.800 +R09 21444793.883 7 114514115.091 7 42.500 21444793.320 6 114514102.117 6 40.900 21444796.418 5 89066599.089 5 34.400 21444795.195 5 89066589.095 5 33.600 +G03 22823830.469 7 119939966.415 7 45.600 22823837.336 5 93459853.737 5 30.800 22823837.398 7 93459852.747 7 44.200 22823837.645 7 89565695.138 7 47.600 +E14 19173395.055 7 100756875.857 7 45.100 19173407.766 7 75240567.283 7 47.300 19173407.156 7 77203363.242 7 46.100 19173407.398 8 76221970.869 8 52.200 +E30 28191538.719 5 148147536.609 5 35.800 28191544.449 6 110629668.765 6 39.800 28191542.387 6 113515659.769 6 37.400 28191544.000 7 112072668.374 7 43.800 +E24 27394988.180 7 143961787.735 7 43.000 27395002.262 6 107504079.295 6 40.200 27395002.543 6 110308516.760 6 40.600 27395003.180 7 108906207.636 7 45.600 +E12 28549417.164 6 150028195.176 6 36.800 28549418.230 6 112034046.171 6 37.000 28549417.949 5 114956671.071 5 35.800 28549418.559 6 113495360.723 6 41.900 +E11 26195806.102 7 137660018.204 7 42.800 26195805.918 6 102798202.535 6 41.100 26195805.262 6 105479883.186 6 40.700 26195805.965 7 104139046.461 7 46.200 +R19 21754879.125 7 116374037.990 7 43.700 21754878.410 7 116374142.011 7 43.200 21754881.102 6 90513346.912 6 41.100 21754880.621 6 90513347.913 6 41.400 +G08 24653272.297 6 129553766.670 6 39.100 24653281.848 3 100951111.072 3 19.300 24653282.684 6 100951113.068 6 37.300 24653281.391 7 96744819.278 7 43.300 +G23 24165410.805 7 126990121.981 7 43.900 24165412.516 3 98953344.619 3 23.200 +G31 22537994.766 7 118437881.004 7 45.800 22537998.863 5 92289523.605 5 33.600 22537998.477 7 92289525.601 7 43.500 +R16 22214341.023 7 118665135.521 7 42.500 22214340.969 6 118665156.536 6 41.300 22214345.293 6 92295230.311 6 41.300 22214345.281 6 92295230.299 6 40.600 +G32 22246710.531 7 116907229.731 7 46.800 22246715.789 5 91096640.622 5 33.800 +G29 24862375.516 6 130652886.557 6 39.200 24862380.328 3 101807535.937 3 21.300 24862379.980 6 101807546.945 6 37.300 +G22 24093232.633 6 126610779.502 6 39.900 24093238.410 3 98657779.269 3 22.000 +R06 20075472.813 8 107126646.309 8 51.100 20075471.590 8 107126603.318 8 49.500 20075473.988 7 83320762.575 7 46.800 20075474.137 7 83320741.569 7 45.800 +C11 23697750.031 7 123400577.080 7 45.600 23697747.023 7 95421307.577 7 45.100 23697740.434 8 100273172.763 8 48.000 +C12 27062482.750 5 140921544.15415 32.400 27062480.508 5 31.100 27062473.086 5 35.500 +> 2016 1 11 0 0 15.0000000 0 25 0.000000000000 +R10 23541822.883 6 125491179.667 6 39.900 23541821.980 6 125491204.714 6 38.600 23541830.535 5 97604339.702 5 33.800 23541830.195 5 97604323.698 5 32.500 +G27 22390089.813 7 117660791.303 7 43.700 22390097.008 5 91683856.088 5 34.200 22390097.258 7 91683850.084 7 45.700 22390094.574 8 87863693.364 8 48.200 +R18 22461330.953 7 119900293.784 7 44.500 22461330.230 7 119900279.790 7 43.200 22461333.328 6 93255881.752 6 40.700 22461333.949 6 93255885.746 6 40.400 +G26 21007765.734 8 110396735.006 8 48.100 21007772.539 6 86023498.042 6 40.000 21007772.422 8 86023506.041 8 48.800 21007770.496 8 82439199.789 8 53.200 +G16 20350618.766 8 106943319.813 8 49.800 20350622.262 6 83332553.587 6 39.900 +R07 20826810.797 8 111487820.536 8 48.300 20826810.035 7 111487564.567 7 46.700 20826813.758 7 86712579.234 7 45.600 20826813.270 7 86712586.247 7 45.600 +G14 22929588.117 6 120495679.654 6 41.100 22929593.246 4 93892982.204 4 26.900 +R05 23442718.047 5 125314775.373 5 35.100 23442716.484 5 125314759.352 5 34.600 23442724.129 6 97467050.427 6 37.000 23442724.004 5 97467064.444 5 35.600 +R09 21445428.398 7 114517507.148 7 42.100 21445428.305 6 114517494.145 6 40.800 21445430.496 5 89069237.329 5 32.800 21445430.223 5 89069227.342 5 32.700 +G03 22824353.281 7 119942712.783 7 44.800 22824359.664 5 93461993.762 5 30.200 22824360.504 7 93461992.763 7 42.500 22824360.266 7 89567745.983 7 47.500 +E14 19179711.938 7 100790074.692 7 44.900 19179724.559 7 75265358.616 7 47.200 19179724.277 7 77228801.308 7 45.600 19179724.961 8 76247085.572 8 52.000 +E30 28188658.539 5 148132401.860 5 35.000 28188663.875 6 110618366.880 6 38.300 28188663.313 6 113504063.049 6 37.600 28188664.043 7 112061219.072 7 43.200 +E24 27398635.359 7 143980954.498 7 42.400 27398650.797 6 107518392.117 6 41.000 27398650.703 6 110323202.975 6 40.900 27398650.543 7 108920707.147 7 46.100 +E12 28551796.906 6 150040700.172 6 36.800 28551797.871 6 112043384.301 6 36.000 28551798.059 6 114966252.791 6 36.200 28551797.938 6 113504820.648 6 41.800 +E11 26193471.602 7 137647749.667 7 42.700 26193471.422 6 102789040.978 6 41.200 26193471.188 6 105470482.629 6 40.900 26193471.313 7 104129765.404 7 46.400 +R19 21747571.195 7 116334944.874 7 44.700 21747569.777 7 116335048.882 7 43.800 21747573.406 7 90482941.173 7 42.200 21747572.980 6 90482942.168 6 41.800 +G08 24643301.820 6 129501375.983 6 38.200 24643311.973 3 100910287.287 3 18.300 24643312.762 6 100910289.320 6 37.900 24643311.574 7 96705696.559 7 43.100 +G23 24155592.938 7 126938527.803 7 43.900 24155594.582 3 98913141.378 3 22.300 +G31 22540182.320 7 118449375.793 7 45.100 22540186.367 5 92298480.581 5 33.500 22540186.055 7 92298482.585 7 43.400 +R16 22218050.789 7 118684949.165 7 42.100 22218049.680 6 118684970.174 6 41.600 22218054.215 6 92310640.920 6 41.600 22218054.367 6 92310640.917 6 40.200 +G32 22251707.039 7 116933488.548 7 45.300 22251712.820 5 91117102.036 5 34.200 +G29 24869774.320 6 130691766.672 6 39.500 24869778.559 3 101837832.142 3 20.100 24869778.586 6 101837843.157 6 36.300 +G22 24101437.422 6 126653896.112 6 39.100 24101442.555 3 98691376.495 3 21.700 +R06 20082117.195 8 107162099.573 8 50.100 20082115.621 8 107162056.568 8 49.200 20082118.176 7 83348337.306 7 47.600 20082118.441 7 83348316.313 7 45.700 +C11 23697974.297 7 123401746.690 7 45.300 23697971.738 7 95422212.006 7 45.200 23697965.219 7 100274123.185 7 47.900 diff --git a/src/test/resources/rinex/unknown-satsystem.00o b/src/test/resources/rinex/unknown-satsystem.00o index ef383fdea1..5e7768351d 100644 --- a/src/test/resources/rinex/unknown-satsystem.00o +++ b/src/test/resources/rinex/unknown-satsystem.00o @@ -46,8 +46,8 @@ G32 22246710.531 7 116907229.731 7 46.800 22246715.789 5 91096640.62 G29 24862375.516 6 130652886.557 6 39.200 24862380.328 3 101807535.937 3 21.300 24862379.980 6 101807546.945 6 37.300 G22 24093232.633 6 126610779.502 6 39.900 24093238.410 3 98657779.269 3 22.000 R06 20075472.813 8 107126646.309 8 51.100 20075471.590 8 107126603.318 8 49.500 20075473.988 7 83320762.575 7 46.800 20075474.137 7 83320741.569 7 45.800 -C11 23697750.031 7 123400577.080 7 45.600 23697747.023 7 95421307.577 7 45.100 23697740.434 8 100273172.763 8 48.000 -C12 27062482.750 5 140921544.15415 32.400 27062480.508 5 31.100 27062473.086 5 35.500 +Z11 23697750.031 7 123400577.080 7 45.600 23697747.023 7 95421307.577 7 45.100 23697740.434 8 100273172.763 8 48.000 +Z12 27062482.750 5 140921544.15415 32.400 27062480.508 5 31.100 27062473.086 5 35.500 > 2016 1 11 0 0 15.0000000 0 25 0.000000000000 R10 23541822.883 6 125491179.667 6 39.900 23541821.980 6 125491204.714 6 38.600 23541830.535 5 97604339.702 5 33.800 23541830.195 5 97604323.698 5 32.500 G27 22390089.813 7 117660791.303 7 43.700 22390097.008 5 91683856.088 5 34.200 22390097.258 7 91683850.084 7 45.700 22390094.574 8 87863693.364 8 48.200 @@ -73,4 +73,4 @@ G32 22251707.039 7 116933488.548 7 45.300 22251712.820 5 91117102.03 G29 24869774.320 6 130691766.672 6 39.500 24869778.559 3 101837832.142 3 20.100 24869778.586 6 101837843.157 6 36.300 G22 24101437.422 6 126653896.112 6 39.100 24101442.555 3 98691376.495 3 21.700 R06 20082117.195 8 107162099.573 8 50.100 20082115.621 8 107162056.568 8 49.200 20082118.176 7 83348337.306 7 47.600 20082118.441 7 83348316.313 7 45.700 -C11 23697974.297 7 123401746.690 7 45.300 23697971.738 7 95422212.006 7 45.200 23697965.219 7 100274123.185 7 47.900 +Z11 23697974.297 7 123401746.690 7 45.300 23697971.738 7 95422212.006 7 45.200 23697965.219 7 100274123.185 7 47.900 diff --git a/src/test/resources/rinex/unsupported-time-scale.06o b/src/test/resources/rinex/unsupported-time-scale.06o new file mode 100644 index 0000000000..644a402a04 --- /dev/null +++ b/src/test/resources/rinex/unsupported-time-scale.06o @@ -0,0 +1,36 @@ + 3.04 OBSERVATION DATA M RINEX VERSION / TYPE +G = GPS R = GLONASS E = GALILEO S = GEO M = MIXED COMMENT +XXRINEXO V9.9 AIUB 20060324 144333 UTC PGM / RUN BY / DATE +A 9080 MARKER NAME +9080.1.34 MARKER NUMBER +BILL SMITH ABC INSTITUTE OBSERVER / AGENCY +X1234A123 GEODETIC 1.3.1 REC # / TYPE / VERS +G1234 ROVER ANT # / TYPE + 2104228. -5642017. 2095406. APPROX POSITION XYZ + .9030 .0000 .0000 ANTENNA: DELTA H/E/N + 0 RCV CLOCK OFFS APPL +G 5 C1C L1W L2W C1W S2W SYS / # / OBS TYPES +R 2 C1C L1C SYS / # / OBS TYPES +E 2 L1B L5I SYS / # / OBS TYPES +S 2 C1C L1C SYS / # / OBS TYPES + 18.000 INTERVAL +DBHZ SIGNAL STRENGTH UNIT + 2006 03 24 13 10 36.0000000 XYZ TIME OF FIRST OBS + 18 R01 1 R02 2 R03 3 R04 4 R05 5 R06 -6 R07 -5 R08 -4 GLONASS SLOT / FRQ # + R09 -3 R10 -2 R11 -1 R12 0 R13 1 R14 2 R15 0 R16 4 GLONASS SLOT / FRQ # + R17 5 R18 -5 GLONASS SLOT / FRQ # +G L1C SYS / PHASE SHIFT +G L1W SYS / PHASE SHIFT +G L2W 0.00000 SYS / PHASE SHIFT +R L1C 0.00000 SYS / PHASE SHIFT +E L1B 0.00000 SYS / PHASE SHIFT +E L5I 0.00000 SYS / PHASE SHIFT +S L1C 0.00000 SYS / PHASE SHIFT + C1C 0.000 C2C 0.000 C2P 0.000 GLONASS COD/PHS/BIS + END OF HEADER +> 2016 03 24 13 10 36.0000000 0 5 0.000000000000 +G06 23629347.915 651379.300 8 23629347.158 24.158 +G09 20891534.648 -453349.120 9 20891545.292 38.123 +G12 20607600.189 -756349.430 9 20891545.848 35.234 +E11 20742800.324 8 378849.178 7 +S20 38137559.506 8 335849.135 9 \ No newline at end of file diff --git a/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_UTC.BSX b/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_UTC.BSX new file mode 100644 index 0000000000..943d73d521 --- /dev/null +++ b/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_UTC.BSX @@ -0,0 +1,51 @@ +%=BIA 1.00 DLR 2022:011:58414 DLR 2021:274:00000 2022:001:00000 R 00246696 +*------------------------------------------------------------------------------- ++FILE/REFERENCE +*INFO_TYPE_________ INFO________________________________________________________ + DESCRIPTION German Space Operations Center (DLR/GSOC) + INPUT Daily DCB solutions in bias SINEX format + OUTPUT Daily IGS MGEX DCB solution for satelites and receivers + HARDWARE localhost.localdomain Linux x86_64 5.3.18-59.37-preempt + SOFTWARE IONDCB/BSXmerge +-FILE/REFERENCE +*------------------------------------------------------------------------------- ++FILE/COMMENT +- Multi-GNSS differential code biases (DCBs) in this product have been derived + from observations of the IGS MGEX network. Details of the DCB estimation + process are described in + Montenbruck O., Hauschild A., Steigenberger P., "Differential Code Bias + Estimation using Multi-GNSS Observations and Global Ionosphere Maps"; + Navigation - Journal of the ION 61(3):191-201 (2014). + DOI 10.1002/navi.64 +- A zero-mean constellation condition is applied to separate satellite and + receiver biases on a daily basis. +- Standard deviations reflect the uncertainty of individual satellite and + station biases adjusted from the observed set of satellite+station biases. +- This file provides the following DCBs + GPS C1C-C1W,C1C-C2W,C2W-C2S,C2W-C2L,C2W-C2X,C1C-C5Q,C1C-C5X,C1C-C1L, + C1C-C1X + GLONASS C1C-C1P,C1C-C2C,C1C-C2P + GALILEO C1C-C5Q,C1X-C5X,C1C-C7Q,C1X-C7X,C1C-C8Q,C1X-C8X,C1C-C6C + BEIDOU C2I-C7I,C2I-C6I,C2I-C1X,C2I-C2P,C2I-C5X,C2I-C5P,C2I-C7D,C2I-C7Z, + C2I-C8X + QZSS C1C-C1X,C1C-C2L,C1C-C5Q,C1X-C2X,C1X-C5X +-FILE/COMMENT +*------------------------------------------------------------------------------- ++INPUT/ACKNOWLEDGMENTS +*AGY DESCRIPTION________________________________________________________________ + DLR Deutsches Zentrum fuer Luft- und Raumfahrt, Oberpfaffenhofen, Germany + IGS International GNSS Service +-INPUT/ACKNOWLEDGMENTS +*------------------------------------------------------------------------------- ++BIAS/DESCRIPTION +*KEYWORD________________________________ VALUE (S) _____________________________ + OBSERVATION_SAMPLING 30 + PARAMETER_SPACING 86400 + DETERMINATION_METHOD INTER-FREQUENCY_BIAS_ESTIMATION + BIAS_MODE RELATIVE + TIME_SYSTEM UTC + test test +-BIAS/DESCRIPTION +* +*------------------------------------------------------------------------------------------------------ +%=ENDBIA diff --git a/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_default_description.BSX b/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_default_description.BSX new file mode 100644 index 0000000000..7bc73df36f --- /dev/null +++ b/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_default_description.BSX @@ -0,0 +1,321 @@ +%=BIA 1.00 DLR 2022:011:58414 DLR 2021:274:00000 2022:001:00000 R 00246696 +*------------------------------------------------------------------------------- ++FILE/REFERENCE +*INFO_TYPE_________ INFO________________________________________________________ + DESCRIPTION German Space Operations Center (DLR/GSOC) + INPUT Daily DCB solutions in bias SINEX format + OUTPUT Daily IGS MGEX DCB solution for satelites and receivers + HARDWARE localhost.localdomain Linux x86_64 5.3.18-59.37-preempt + SOFTWARE IONDCB/BSXmerge +-FILE/REFERENCE +*------------------------------------------------------------------------------- ++FILE/COMMENT +- Multi-GNSS differential code biases (DCBs) in this product have been derived + from observations of the IGS MGEX network. Details of the DCB estimation + process are described in + Montenbruck O., Hauschild A., Steigenberger P., "Differential Code Bias + Estimation using Multi-GNSS Observations and Global Ionosphere Maps"; + Navigation - Journal of the ION 61(3):191-201 (2014). + DOI 10.1002/navi.64 +- A zero-mean constellation condition is applied to separate satellite and + receiver biases on a daily basis. +- Standard deviations reflect the uncertainty of individual satellite and + station biases adjusted from the observed set of satellite+station biases. +- This file provides the following DCBs + GPS C1C-C1W,C1C-C2W,C2W-C2S,C2W-C2L,C2W-C2X,C1C-C5Q,C1C-C5X,C1C-C1L, + C1C-C1X + GLONASS C1C-C1P,C1C-C2C,C1C-C2P + GALILEO C1C-C5Q,C1X-C5X,C1C-C7Q,C1X-C7X,C1C-C8Q,C1X-C8X,C1C-C6C + BEIDOU C2I-C7I,C2I-C6I,C2I-C1X,C2I-C2P,C2I-C5X,C2I-C5P,C2I-C7D,C2I-C7Z, + C2I-C8X + QZSS C1C-C1X,C1C-C2L,C1C-C5Q,C1X-C2X,C1X-C5X +-FILE/COMMENT +*------------------------------------------------------------------------------- ++INPUT/ACKNOWLEDGMENTS +*AGY DESCRIPTION________________________________________________________________ + DLR Deutsches Zentrum fuer Luft- und Raumfahrt, Oberpfaffenhofen, Germany + IGS International GNSS Service +-INPUT/ACKNOWLEDGMENTS +*------------------------------------------------------------------------------- ++BIAS/DESCRIPTION +*KEYWORD________________________________ VALUE (S) _____________________________ + OBSERVATION_SAMPLING 30 + PARAMETER_SPACING 86400 + DETERMINATION_METHOD INTER-FREQUENCY_BIAS_ESTIMATION + BIAS_MODE RELATIVE + TIME_SYSTEM G + test test +-BIAS/DESCRIPTION +* +*------------------------------------------------------------------------------------------------------ ++BIAS/SOLUTION +*BIAS SVN_ PRN STATION__ OBS1 OBS2 BIAS_START____ BIAS_END______ UNIT __ESTIMATED_VALUE____ _STD_DEV___ + DSB G063 G01 C1C C1W 2021:274:00000 2021:275:00000 ns -1.0454 0.0117 + DSB G063 G01 C1C C1W 2021:275:00000 2021:276:00000 ns -1.0597 0.0122 + DSB G063 G01 C1C C1W 2021:276:00000 2021:277:00000 ns -1.0669 0.0115 + DSB G063 G01 C1C C1W 2021:277:00000 2021:278:00000 ns -1.0845 0.0119 + DSB G063 G01 C1C C1W 2021:278:00000 2021:279:00000 ns -1.0679 0.0123 + DSB G063 G01 C1C C1W 2021:279:00000 2021:280:00000 ns -1.0713 0.0112 + DSB G063 G01 C1C C1W 2021:280:00000 2021:281:00000 ns -1.0697 0.0119 + DSB G063 G01 C1C C1W 2021:281:00000 2021:282:00000 ns -1.0739 0.0116 + DSB G063 G01 C1C C1W 2021:282:00000 2021:283:00000 ns -1.0694 0.0119 + DSB G063 G01 C1C C2W 2021:274:00000 2021:275:00000 ns -7.9863 0.0457 + DSB G063 G01 C1C C2W 2021:275:00000 2021:276:00000 ns -7.9692 0.0476 + DSB G063 G01 C1C C2W 2021:276:00000 2021:277:00000 ns -8.0252 0.0451 + DSB G063 G01 C1C C2W 2021:277:00000 2021:278:00000 ns -7.9378 0.0425 + DSB G063 G01 C1C C2W 2021:278:00000 2021:279:00000 ns -8.0441 0.0411 + DSB G063 G01 C1C C2W 2021:279:00000 2021:280:00000 ns -8.0465 0.0436 + DSB G063 G01 C1C C2W 2021:280:00000 2021:281:00000 ns -8.0182 0.0442 + DSB G063 G01 C1C C2W 2021:281:00000 2021:282:00000 ns -8.0229 0.0425 + DSB G063 G01 C1C C5Q 2021:274:00000 2021:275:00000 ns 2.9398 0.0567 + DSB G063 G01 C1C C5Q 2021:275:00000 2021:276:00000 ns 2.9253 0.0589 + DSB G063 G01 C1C C5Q 2021:276:00000 2021:277:00000 ns 2.8672 0.0559 + DSB G063 G01 C1C C5Q 2021:277:00000 2021:278:00000 ns 2.9940 0.0506 + DSB G063 G01 C1C C5Q 2021:278:00000 2021:279:00000 ns 2.8577 0.0488 + DSB G063 G01 C1C C5Q 2021:279:00000 2021:280:00000 ns 2.8671 0.0514 + DSB G063 G01 C2W C2L 2021:274:00000 2021:275:00000 ns 1.2955 0.0325 + DSB G063 G01 C2W C2L 2021:275:00000 2021:276:00000 ns 1.2784 0.0329 + DSB G063 G01 C2W C2L 2021:276:00000 2021:277:00000 ns 1.2902 0.0326 + DSB G063 G01 C2W C2L 2021:277:00000 2021:278:00000 ns 1.2976 0.0325 + DSB G063 G01 C2W C2L 2021:278:00000 2021:279:00000 ns 1.2952 0.0323 + DSB G063 G01 C2W C2L 2021:279:00000 2021:280:00000 ns 1.2988 0.0320 + DSB G063 G01 C2W C2L 2021:280:00000 2021:281:00000 ns 1.2946 0.0322 + DSB G063 G01 C2W C2L 2021:281:00000 2021:282:00000 ns 1.2962 0.0316 + DSB G063 G01 C2W C2L 2021:282:00000 2021:283:00000 ns 1.2881 0.0321 + DSB G063 G01 C2W C2L 2021:283:00000 2021:284:00000 ns 1.2915 0.0322 + DSB G063 G01 C2W C2L 2021:284:00000 2021:285:00000 ns 1.2869 0.0319 + DSB G061 G02 C1C C1W 2021:274:00000 2021:275:00000 ns 1.7559 0.0126 + DSB G061 G02 C1C C1W 2021:275:00000 2021:276:00000 ns 1.7672 0.0128 + DSB G061 G02 C1C C1W 2021:276:00000 2021:277:00000 ns 1.7549 0.0121 + DSB G061 G02 C1C C1W 2021:277:00000 2021:278:00000 ns 1.7552 0.0121 + DSB G061 G02 C1C C1W 2021:278:00000 2021:279:00000 ns 1.7487 0.0132 + DSB G061 G02 C1C C1W 2021:279:00000 2021:280:00000 ns 1.7591 0.0128 + DSB G061 G02 C1C C1W 2021:280:00000 2021:281:00000 ns 1.7552 0.0130 + DSB G061 G02 C1C C1W 2021:281:00000 2021:282:00000 ns 1.7561 0.0132 + DSB G061 G02 C1C C1W 2021:282:00000 2021:283:00000 ns 1.7614 0.0116 + DSB G061 G02 C1C C1W 2021:283:00000 2021:284:00000 ns 1.7605 0.0133 + DSB R730 R01 C1C C1P 2021:274:00000 2021:275:00000 ns 0.4270 0.0564 + DSB R730 R01 C1C C1P 2021:275:00000 2021:276:00000 ns 0.4457 0.0584 + DSB R730 R01 C1C C1P 2021:276:00000 2021:277:00000 ns 0.4676 0.0567 + DSB R730 R01 C1C C1P 2021:277:00000 2021:278:00000 ns 0.4680 0.0560 + DSB R730 R01 C1C C1P 2021:278:00000 2021:279:00000 ns 0.4472 0.0557 + DSB R730 R01 C1C C1P 2021:279:00000 2021:280:00000 ns 0.4476 0.0568 + DSB R730 R01 C1C C1P 2021:280:00000 2021:281:00000 ns 0.4536 0.0570 + DSB R730 R01 C1C C1P 2021:281:00000 2021:282:00000 ns 0.4312 0.0560 + DSB R730 R01 C1C C1P 2021:282:00000 2021:283:00000 ns 0.4325 0.0564 + DSB R730 R01 C1C C1P 2021:283:00000 2021:284:00000 ns 0.4599 0.0576 + DSB R730 R01 C1C C1P 2021:284:00000 2021:285:00000 ns 0.4668 0.0556 + DSB E208 E08 C1C C5Q 2021:274:00000 2021:275:00000 ns 2.8843 0.0513 + DSB E208 E08 C1C C5Q 2021:275:00000 2021:276:00000 ns 2.8150 0.0590 + DSB E208 E08 C1C C5Q 2021:276:00000 2021:277:00000 ns 3.0616 0.0626 + DSB E208 E08 C1C C5Q 2021:277:00000 2021:278:00000 ns 2.8655 0.0541 + DSB E208 E08 C1C C5Q 2021:278:00000 2021:279:00000 ns 2.8371 0.0455 + DSB E208 E08 C1C C5Q 2021:279:00000 2021:280:00000 ns 2.8741 0.0571 + DSB E208 E08 C1C C5Q 2021:280:00000 2021:281:00000 ns 2.9022 0.0583 + DSB E208 E08 C1C C5Q 2021:281:00000 2021:282:00000 ns 2.7755 0.0459 + DSB E208 E08 C1C C5Q 2021:282:00000 2021:283:00000 ns 2.8650 0.0519 + DSB E208 E08 C1C C5Q 2021:283:00000 2021:284:00000 ns 2.8944 0.0548 + DSB E208 E08 C1C C5Q 2021:284:00000 2021:285:00000 ns 2.8540 0.0515 + DSB E208 E08 C1C C5Q 2021:285:00000 2021:286:00000 ns 2.6923 0.0581 + DSB E208 E08 C1C C5Q 2021:286:00000 2021:287:00000 ns 2.8889 0.0528 + DSB E208 E08 C1C C5Q 2021:287:00000 2021:288:00000 ns 2.9141 0.0453 + DSB E208 E08 C1C C5Q 2021:288:00000 2021:289:00000 ns 2.8807 0.0478 + DSB E208 E08 C1C C5Q 2021:289:00000 2021:290:00000 ns 2.8893 0.0498 + DSB E208 E08 C1C C5Q 2021:290:00000 2021:291:00000 ns 2.9170 0.0482 + DSB E208 E08 C1C C5Q 2021:291:00000 2021:292:00000 ns 2.7170 0.0483 + DSB E208 E08 C1C C5Q 2021:292:00000 2021:293:00000 ns 2.8445 0.0515 + DSB E208 E08 C1C C5Q 2021:293:00000 2021:294:00000 ns 2.9190 0.0501 + DSB E208 E08 C1C C5Q 2021:294:00000 2021:295:00000 ns 2.7940 0.0407 + DSB E208 E08 C1C C5Q 2021:295:00000 2021:296:00000 ns 2.7929 0.0475 + DSB E208 E08 C1C C5Q 2021:296:00000 2021:297:00000 ns 2.9440 0.0542 + DSB E208 E08 C1C C5Q 2021:297:00000 2021:298:00000 ns 2.8282 0.0549 + DSB E208 E08 C1C C5Q 2021:298:00000 2021:299:00000 ns 2.7993 0.0556 + DSB E208 E08 C1C C5Q 2021:299:00000 2021:300:00000 ns 2.9611 0.0590 + DSB E208 E08 C1C C5Q 2021:300:00000 2021:301:00000 ns 2.8449 0.0502 + DSB E208 E08 C1C C5Q 2021:301:00000 2021:302:00000 ns 2.6883 0.0527 + DSB E208 E08 C1C C5Q 2021:302:00000 2021:303:00000 ns 2.9535 0.0570 + DSB E208 E08 C1C C5Q 2021:303:00000 2021:304:00000 ns 2.7641 0.0668 + DSB E208 E08 C1C C5Q 2021:304:00000 2021:305:00000 ns 2.7756 0.0535 + DSB E208 E08 C1C C5Q 2021:305:00000 2021:306:00000 ns 2.8402 0.0566 + DSB C017 C13 C2I C6I 2021:274:00000 2021:275:00000 ns 3.6061 0.0942 + DSB C017 C13 C2I C6I 2021:275:00000 2021:276:00000 ns 3.6020 0.0984 + DSB C017 C13 C2I C6I 2021:276:00000 2021:277:00000 ns 3.6126 0.0974 + DSB C017 C13 C2I C6I 2021:277:00000 2021:278:00000 ns 3.6363 0.0972 + DSB C017 C13 C2I C6I 2021:278:00000 2021:279:00000 ns 3.6559 0.0914 + DSB C017 C13 C2I C6I 2021:279:00000 2021:280:00000 ns 3.6204 0.0939 + DSB C017 C13 C2I C6I 2021:280:00000 2021:281:00000 ns 3.7392 0.0954 + DSB C017 C13 C2I C6I 2021:281:00000 2021:282:00000 ns 3.6668 0.0939 + DSB C017 C13 C2I C6I 2021:282:00000 2021:283:00000 ns 3.6711 0.0955 + DSB C017 C13 C2I C6I 2021:283:00000 2021:284:00000 ns 3.6470 0.0939 + DSB C017 C13 C2I C6I 2021:284:00000 2021:285:00000 ns 3.6058 0.0966 + DSB C017 C13 C2I C6I 2021:285:00000 2021:286:00000 ns 3.6307 0.0936 + DSB C017 C13 C2I C6I 2021:286:00000 2021:287:00000 ns 3.5364 0.0940 + DSB C017 C13 C2I C6I 2021:287:00000 2021:288:00000 ns 3.5890 0.0948 + DSB C017 C13 C2I C6I 2021:288:00000 2021:289:00000 ns 3.5868 0.0945 + DSB C017 C13 C2I C6I 2021:289:00000 2021:290:00000 ns 3.4208 0.0922 + DSB G G AGGO C1C C1L 2021:275:00000 2021:276:00000 ns -0.1844 0.0294 + DSB G G AGGO C1C C1L 2021:276:00000 2021:277:00000 ns -0.1714 0.0287 + DSB G G AGGO C1C C1L 2021:277:00000 2021:278:00000 ns -0.1711 0.0300 + DSB G G AGGO C1C C1L 2021:278:00000 2021:279:00000 ns -0.1644 0.0256 + DSB G G AGGO C1C C1L 2021:279:00000 2021:280:00000 ns -0.1689 0.0265 + DSB G G AGGO C1C C1L 2021:280:00000 2021:281:00000 ns -0.1913 0.0287 + DSB G G AGGO C1C C1L 2021:281:00000 2021:282:00000 ns -0.1658 0.0273 + DSB G G AGGO C1C C1L 2021:282:00000 2021:283:00000 ns -0.1915 0.0295 + DSB G G AGGO C1C C1L 2021:283:00000 2021:284:00000 ns -0.1824 0.0244 + DSB G G AGGO C1C C1L 2021:284:00000 2021:285:00000 ns -0.1768 0.0277 + DSB G G AGGO C1C C1L 2021:285:00000 2021:286:00000 ns -0.1667 0.0499 + DSB G G AGGO C1C C1L 2021:286:00000 2021:287:00000 ns -0.1630 0.0278 + DSB G G AGGO C1C C1L 2021:287:00000 2021:288:00000 ns -0.1924 0.0441 + DSB G G AGGO C1C C1L 2021:288:00000 2021:289:00000 ns -0.2038 0.0273 + DSB G G AGGO C1C C1L 2021:289:00000 2021:290:00000 ns -0.1840 0.0266 + DSB G G AGGO C1C C1L 2021:290:00000 2021:291:00000 ns -0.1741 0.0269 + DSB G G AGGO C1C C1L 2021:291:00000 2021:292:00000 ns -0.1864 0.0273 + DSB G G AGGO C1C C1L 2021:292:00000 2021:293:00000 ns -0.1894 0.0285 + DSB G G AGGO C1C C1L 2021:293:00000 2021:294:00000 ns -0.1954 0.0275 + DSB G G AGGO C1C C1L 2021:294:00000 2021:295:00000 ns -0.1908 0.0290 + DSB G G AGGO C1C C1L 2021:295:00000 2021:296:00000 ns -0.1846 0.0265 + DSB G G AGGO C1C C1L 2021:296:00000 2021:297:00000 ns -0.1884 0.0261 + DSB G G AGGO C1C C1L 2021:297:00000 2021:298:00000 ns -0.1740 0.0240 + DSB G G AGGO C1C C1L 2021:298:00000 2021:299:00000 ns -0.1963 0.0249 + DSB G G AGGO C1C C1L 2021:299:00000 2021:300:00000 ns -0.1957 0.0277 + DSB G G AGGO C1C C1L 2021:300:00000 2021:301:00000 ns -0.1999 0.0272 + DSB G G AGGO C1C C1L 2021:301:00000 2021:302:00000 ns -0.1821 0.0289 + DSB G G AGGO C1C C1L 2021:302:00000 2021:303:00000 ns -0.1854 0.0238 + DSB G G AGGO C1C C1L 2021:303:00000 2021:304:00000 ns -0.1610 0.0240 + DSB G G AGGO C1C C1L 2021:305:00000 2021:306:00000 ns -0.1834 0.0241 + DSB G G AGGO C1C C1L 2021:306:00000 2021:307:00000 ns -0.1850 0.0286 + DSB E E AGGO C1C C5Q 2021:274:00000 2021:275:00000 ns 2.2718 0.0985 + DSB E E AGGO C1C C5Q 2021:275:00000 2021:276:00000 ns 1.8420 0.1104 + DSB E E AGGO C1C C5Q 2021:276:00000 2021:277:00000 ns 1.7551 0.1155 + DSB E E AGGO C1C C5Q 2021:277:00000 2021:278:00000 ns 1.6289 0.0967 + DSB E E AGGO C1C C5Q 2021:278:00000 2021:279:00000 ns 1.9820 0.0881 + DSB E E AGGO C1C C5Q 2021:279:00000 2021:280:00000 ns 1.8115 0.0956 + DSB E E AGGO C1C C5Q 2021:280:00000 2021:281:00000 ns 0.8327 0.1092 + DSB E E AGGO C1C C5Q 2021:281:00000 2021:282:00000 ns 0.6914 0.0845 + DSB E E AGGO C1C C5Q 2021:282:00000 2021:283:00000 ns 1.9270 0.0862 + DSB E E AGGO C1C C5Q 2021:283:00000 2021:284:00000 ns 1.5861 0.1005 + DSB E E AGGO C1C C5Q 2021:284:00000 2021:285:00000 ns 1.3094 0.1020 + DSB E E AGGO C1C C5Q 2021:285:00000 2021:286:00000 ns 2.3790 0.1384 + DSB E E AGGO C1C C5Q 2021:286:00000 2021:287:00000 ns 1.3307 0.0893 + DSB E E AGGO C1C C5Q 2021:287:00000 2021:288:00000 ns 1.6232 0.1091 + DSB E E AGGO C1C C5Q 2021:288:00000 2021:289:00000 ns 1.2009 0.0985 + DSB E E AGGO C1C C5Q 2021:289:00000 2021:290:00000 ns 1.3268 0.0863 + DSB J J AIRA C1X C5X 2021:275:00000 2021:276:00000 ns -15.8850 0.3279 + DSB J J AIRA C1X C5X 2021:276:00000 2021:277:00000 ns -15.6392 0.4773 + DSB J J AIRA C1X C5X 2021:277:00000 2021:278:00000 ns -16.4433 0.3891 + DSB J J AIRA C1X C5X 2021:278:00000 2021:279:00000 ns -16.7385 0.3055 + DSB J J AIRA C1X C5X 2021:279:00000 2021:280:00000 ns -16.2287 0.4138 + DSB J J AIRA C1X C5X 2021:280:00000 2021:281:00000 ns -16.3054 0.5310 + DSB J J AIRA C1X C5X 2021:281:00000 2021:282:00000 ns -16.8184 0.3453 + DSB J J AIRA C1X C5X 2021:282:00000 2021:283:00000 ns -16.6188 0.3794 + DSB J J AIRA C1X C5X 2021:283:00000 2021:284:00000 ns -16.4500 0.4128 + DSB J J AIRA C1X C5X 2021:284:00000 2021:285:00000 ns -16.0934 0.3652 + DSB J J AIRA C1X C5X 2021:285:00000 2021:286:00000 ns -16.6076 0.3516 + DSB J J AIRA C1X C5X 2021:286:00000 2021:287:00000 ns -15.9966 0.4721 + DSB J J AIRA C1X C5X 2021:287:00000 2021:288:00000 ns -16.7860 0.3507 + DSB J J AIRA C1X C5X 2021:288:00000 2021:289:00000 ns -16.6548 0.3471 + DSB J J AIRA C1X C5X 2021:289:00000 2021:290:00000 ns -16.7481 0.3863 + DSB J J AIRA C1X C5X 2021:290:00000 2021:291:00000 ns -17.2781 0.3491 + DSB J J AIRA C1X C5X 2021:291:00000 2021:292:00000 ns -17.2820 0.3110 + DSB J J AIRA C1X C5X 2021:292:00000 2021:293:00000 ns -17.6060 0.3463 + DSB J J AIRA C1X C5X 2021:293:00000 2021:294:00000 ns -17.3941 0.3541 + DSB J J AIRA C1X C5X 2021:294:00000 2021:295:00000 ns -16.8271 0.3548 + DSB J J AIRA C1X C5X 2021:295:00000 2021:296:00000 ns -17.1273 0.3663 + DSB J J AIRA C1X C5X 2021:296:00000 2021:297:00000 ns -17.2388 0.3719 + DSB J J AIRA C1X C5X 2021:297:00000 2021:298:00000 ns -16.7945 0.4212 + DSB J J AIRA C1X C5X 2021:298:00000 2021:299:00000 ns -17.4373 0.3043 + DSB J J AIRA C1X C5X 2021:299:00000 2021:300:00000 ns -16.5743 0.4585 + DSB J J AIRA C1X C5X 2021:300:00000 2021:301:00000 ns -16.8834 0.3852 + DSB J J AIRA C1X C5X 2021:301:00000 2021:302:00000 ns -16.7790 0.4013 + DSB J J AIRA C1X C5X 2021:302:00000 2021:303:00000 ns -16.6366 0.4063 + DSB J J AIRA C1X C5X 2021:303:00000 2021:304:00000 ns -16.4386 0.3433 + DSB J J AIRA C1X C5X 2021:304:00000 2021:305:00000 ns -16.5724 0.3771 + DSB J J AIRA C1X C5X 2021:305:00000 2021:306:00000 ns -17.5522 0.3612 + DSB J J AIRA C1X C5X 2021:306:00000 2021:307:00000 ns -16.8070 0.3965 + DSB J J AIRA C1X C5X 2021:307:00000 2021:308:00000 ns -17.1508 0.3475 + DSB J J AIRA C1X C5X 2021:308:00000 2021:309:00000 ns -17.1474 0.4229 + DSB J J AIRA C1X C5X 2021:309:00000 2021:310:00000 ns -16.9885 0.3411 + DSB J J AIRA C1X C5X 2021:310:00000 2021:311:00000 ns -16.2453 0.4287 + DSB J J AIRA C1X C5X 2021:311:00000 2021:312:00000 ns -16.5387 0.3719 + DSB J J AIRA C1X C5X 2021:312:00000 2021:313:00000 ns -16.8080 0.4619 + DSB J J AIRA C1X C5X 2021:313:00000 2021:314:00000 ns -17.1998 0.4380 + DSB J J AIRA C1X C5X 2021:314:00000 2021:315:00000 ns -17.5592 0.4007 + DSB J J AIRA C1X C5X 2021:315:00000 2021:316:00000 ns -17.5454 0.4821 + DSB J J AIRA C1X C5X 2021:316:00000 2021:317:00000 ns -17.4004 0.3711 + DSB J J AIRA C1X C5X 2021:317:00000 2021:318:00000 ns -17.4694 0.4046 + DSB J J AIRA C1X C5X 2021:318:00000 2021:319:00000 ns -17.7327 0.3525 + DSB J J AIRA C1X C5X 2021:319:00000 2021:320:00000 ns -17.5691 0.3426 + DSB J J AIRA C1X C5X 2021:320:00000 2021:321:00000 ns -18.0696 0.3271 + DSB J J AIRA C1X C5X 2021:321:00000 2021:322:00000 ns -17.2864 0.4133 + DSB J J AIRA C1X C5X 2021:322:00000 2021:323:00000 ns -17.5390 0.3374 + DSB J J AIRA C1X C5X 2021:323:00000 2021:324:00000 ns -17.5877 0.2957 + DSB J J AIRA C1X C5X 2021:324:00000 2021:325:00000 ns -16.9507 0.4055 + DSB J J AIRA C1X C5X 2021:325:00000 2021:326:00000 ns -17.7235 0.3074 + DSB J J AIRA C1X C5X 2021:326:00000 2021:327:00000 ns -17.2184 0.4217 + DSB J J AIRA C1X C5X 2021:327:00000 2021:328:00000 ns -17.7891 0.3868 + DSB J J AIRA C1X C5X 2021:328:00000 2021:329:00000 ns -17.0176 0.3892 + DSB J J AIRA C1X C5X 2021:329:00000 2021:330:00000 ns -17.6940 0.3838 + DSB J J AIRA C1X C5X 2021:330:00000 2021:331:00000 ns -17.9788 0.3060 + DSB R R ALIC C1C C1P 2021:291:00000 2021:292:00000 ns -0.6630 0.1012 + DSB R R ALIC C1C C1P 2021:292:00000 2021:293:00000 ns -0.6419 0.0981 + DSB R R ALIC C1C C1P 2021:293:00000 2021:294:00000 ns -0.6534 0.0989 + DSB R R ALIC C1C C1P 2021:294:00000 2021:295:00000 ns -0.6409 0.1019 + DSB R R ALIC C1C C1P 2021:295:00000 2021:296:00000 ns -0.6764 0.1002 + DSB R R ALIC C1C C1P 2021:296:00000 2021:297:00000 ns -0.6556 0.0962 + DSB R R ALIC C1C C1P 2021:297:00000 2021:298:00000 ns -0.6632 0.0958 + DSB R R ALIC C1C C1P 2021:298:00000 2021:299:00000 ns -0.6623 0.1016 + DSB R R ALIC C1C C1P 2021:299:00000 2021:300:00000 ns -0.6510 0.1012 + DSB R R ALIC C1C C1P 2021:300:00000 2021:301:00000 ns -0.6458 0.0967 + DSB R R ALIC C1C C1P 2021:301:00000 2021:302:00000 ns -0.6426 0.0988 + DSB R R ALIC C1C C1P 2021:302:00000 2021:303:00000 ns -0.6537 0.0988 + DSB R R ALIC C1C C1P 2021:303:00000 2021:304:00000 ns -0.6714 0.1003 + DSB R R ALIC C1C C1P 2021:304:00000 2021:305:00000 ns -0.6426 0.1035 + DSB R R ALIC C1C C1P 2021:305:00000 2021:306:00000 ns -0.6832 0.1004 + DSB R R ALIC C1C C1P 2021:306:00000 2021:307:00000 ns -0.6681 0.1045 + DSB R R ALIC C1C C1P 2021:307:00000 2021:308:00000 ns -0.6508 0.1051 + DSB R R ALIC C1C C1P 2021:308:00000 2021:309:00000 ns -0.6490 0.1039 + DSB R R ALIC C1C C1P 2021:309:00000 2021:310:00000 ns -0.6689 0.1034 + DSB R R ALIC C1C C1P 2021:310:00000 2021:311:00000 ns -0.6573 0.1063 + DSB R R ALIC C1C C1P 2021:311:00000 2021:312:00000 ns -0.6839 0.1051 + DSB R R ALIC C1C C1P 2021:312:00000 2021:313:00000 ns -0.6479 0.1054 + DSB R R ALIC C1C C1P 2021:313:00000 2021:314:00000 ns -0.6571 0.1107 + DSB R R ALIC C1C C1P 2021:314:00000 2021:315:00000 ns -0.6704 0.1080 + DSB R R ALIC C1C C1P 2021:315:00000 2021:316:00000 ns -0.6459 0.0980 + DSB R R ALIC C1C C1P 2021:316:00000 2021:317:00000 ns -0.6241 0.1016 + DSB R R ALIC C1C C1P 2021:317:00000 2021:318:00000 ns -0.6471 0.1025 + DSB R R ALIC C1C C1P 2021:318:00000 2021:319:00000 ns -0.6545 0.1028 + DSB R R ALIC C1C C1P 2021:319:00000 2021:320:00000 ns -0.6782 0.1020 + DSB R R ALIC C1C C1P 2021:320:00000 2021:321:00000 ns -0.6523 0.0996 + DSB R R ALIC C1C C1P 2021:321:00000 2021:322:00000 ns -0.6537 0.1023 + DSB R R ALIC C1C C1P 2021:322:00000 2021:323:00000 ns -0.6364 0.0985 + DSB R R ALIC C1C C1P 2021:323:00000 2021:324:00000 ns -0.6595 0.0973 + DSB R R ALIC C1C C1P 2021:324:00000 2021:325:00000 ns -0.6505 0.0936 + DSB R R ALIC C1C C1P 2021:325:00000 2021:326:00000 ns -0.6738 0.0990 + DSB R R ALIC C1C C1P 2021:326:00000 2021:327:00000 ns -0.6805 0.1007 + DSB R R ALIC C1C C1P 2021:327:00000 2021:328:00000 ns -0.6945 0.1002 + DSB R R ALIC C1C C1P 2021:328:00000 2021:329:00000 ns -0.6614 0.0984 + DSB R R ALIC C1C C1P 2021:329:00000 2021:330:00000 ns -0.6654 0.0992 + DSB R R ALIC C1C C1P 2021:330:00000 2021:331:00000 ns -0.6301 0.0985 + DSB R R ALIC C1C C1P 2021:331:00000 2021:332:00000 ns -0.6410 0.0988 + DSB R R ALIC C1C C1P 2021:332:00000 2021:333:00000 ns -0.6344 0.0995 + DSB R R ALIC C1C C1P 2021:333:00000 2021:334:00000 ns -0.6786 0.1038 + DSB R R ALIC C1C C1P 2021:334:00000 2021:335:00000 ns -0.6681 0.1061 + DSB R R ALIC C1C C1P 2021:335:00000 2021:336:00000 ns -0.6747 0.1052 + DSB R R ALIC C1C C1P 2021:336:00000 2021:337:00000 ns -0.6522 0.1042 + DSB R R ALIC C1C C1P 2021:337:00000 2021:338:00000 ns -0.6641 0.1036 + DSB R R ALIC C1C C1P 2021:338:00000 2021:339:00000 ns -0.6648 0.1016 + DSB R R ALIC C1C C1P 2021:339:00000 2021:340:00000 ns -0.6557 0.0983 + DSB R R ALIC C1C C1P 2021:340:00000 2021:341:00000 ns -0.6642 0.1070 + DSB R R ALIC C1C C1P 2021:341:00000 2021:342:00000 ns -0.6596 0.1043 + DSB R R ALIC C1C C1P 2021:342:00000 2021:343:00000 ns -0.6840 0.1026 + DSB R R ALIC C1C C1P 2021:343:00000 2021:344:00000 ns -0.6742 0.1044 + DSB R R ALIC C1C C1P 2021:344:00000 2021:345:00000 ns -0.6552 0.1041 + DSB R R ALIC C1C C1P 2021:345:00000 2021:346:00000 ns -0.6600 0.1048 + DSB R R ALIC C1C C1P 2021:346:00000 2021:347:00000 ns -0.6579 0.1014 + DSB R R ALIC C1C C1P 2021:347:00000 2021:348:00000 ns -0.6544 0.1007 + DSB R R ALIC C1C C1P 2021:348:00000 2021:349:00000 ns -0.6657 0.1106 + DSB R R ALIC C1C C1P 2021:349:00000 2021:350:00000 ns -0.6754 0.1121 +-BIAS/SOLUTION +* +*------------------------------------------------------------------------------------------------------ +%=ENDBIA diff --git a/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_trunc_sat.BSX b/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_trunc_sat.BSX new file mode 100644 index 0000000000..3e9f85a107 --- /dev/null +++ b/src/test/resources/sinex/DLR0MGXFIN_20212740000_03L_01D_DCB_trunc_sat.BSX @@ -0,0 +1,321 @@ +%=BIA 1.00 DLR 2022:011:58414 DLR 2021:274:00000 2022:001:00000 R 00246696 +*------------------------------------------------------------------------------- ++FILE/REFERENCE +*INFO_TYPE_________ INFO________________________________________________________ + DESCRIPTION German Space Operations Center (DLR/GSOC) + INPUT Daily DCB solutions in bias SINEX format + OUTPUT Daily IGS MGEX DCB solution for satelites and receivers + HARDWARE localhost.localdomain Linux x86_64 5.3.18-59.37-preempt + SOFTWARE IONDCB/BSXmerge +-FILE/REFERENCE +*------------------------------------------------------------------------------- ++FILE/COMMENT +- Multi-GNSS differential code biases (DCBs) in this product have been derived + from observations of the IGS MGEX network. Details of the DCB estimation + process are described in + Montenbruck O., Hauschild A., Steigenberger P., "Differential Code Bias + Estimation using Multi-GNSS Observations and Global Ionosphere Maps"; + Navigation - Journal of the ION 61(3):191-201 (2014). + DOI 10.1002/navi.64 +- A zero-mean constellation condition is applied to separate satellite and + receiver biases on a daily basis. +- Standard deviations reflect the uncertainty of individual satellite and + station biases adjusted from the observed set of satellite+station biases. +- This file provides the following DCBs + GPS C1C-C1W,C1C-C2W,C2W-C2S,C2W-C2L,C2W-C2X,C1C-C5Q,C1C-C5X,C1C-C1L, + C1C-C1X + GLONASS C1C-C1P,C1C-C2C,C1C-C2P + GALILEO C1C-C5Q,C1X-C5X,C1C-C7Q,C1X-C7X,C1C-C8Q,C1X-C8X,C1C-C6C + BEIDOU C2I-C7I,C2I-C6I,C2I-C1X,C2I-C2P,C2I-C5X,C2I-C5P,C2I-C7D,C2I-C7Z, + C2I-C8X + QZSS C1C-C1X,C1C-C2L,C1C-C5Q,C1X-C2X,C1X-C5X +-FILE/COMMENT +*------------------------------------------------------------------------------- ++INPUT/ACKNOWLEDGMENTS +*AGY DESCRIPTION________________________________________________________________ + DLR Deutsches Zentrum fuer Luft- und Raumfahrt, Oberpfaffenhofen, Germany + IGS International GNSS Service +-INPUT/ACKNOWLEDGMENTS +*------------------------------------------------------------------------------- ++BIAS/DESCRIPTION +*KEYWORD________________________________ VALUE (S) _____________________________ + OBSERVATION_SAMPLING 30 + PARAMETER_SPACING 86400 + DETERMINATION_METHOD INTER-FREQUENCY_BIAS_ESTIMATION + BIAS_MODE RELATIVE + TIME_SYSTEM G +-BIAS/DESCRIPTION +* +*------------------------------------------------------------------------------------------------------ ++BIAS/SOLUTION +*BIAS SVN_ PRN STATION__ OBS1 OBS2 BIAS_START____ BIAS_END______ UNIT __ESTIMATED_VALUE____ _STD_DEV___ + DSB G063 G01 C1C C1W 2021:274:00000 2021:275:00000 ns -1.0454 0.0117 + DSB G063 G01 C1C C1W 2021:275:00000 2021:276:00000 ns -1.0597 0.0122 + DSB G063 G01 C1C C1W 2021:276:00000 2021:277:00000 ns -1.0669 0.0115 + DSB G063 G01 C1C C1W 2021:277:00000 2021:278:00000 ns -1.0845 0.0119 + DSB G063 G01 C1C C1W 2021:278:00000 2021:279:00000 ns -1.0679 0.0123 + DSB G063 G01 C1C C1W 2021:279:00000 2021:280:00000 ns -1.0713 0.0112 + DSB G063 G01 C1C C1W 2021:280:00000 2021:281:00000 ns -1.0697 0.0119 + DSB G063 G01 C1C C1W 2021:281:00000 2021:282:00000 ns -1.0739 0.0116 + DSB G063 G01 C1C C1W 2021:282:00000 2021:283:00000 ns -1.0694 0.0119 + DSB G063 G01 C1C C2W 2021:274:00000 2021:275:00000 ns -7.9863 0.0457 + DSB G063 G01 C1C C2W 2021:275:00000 2021:276:00000 ns -7.9692 0.0476 + DSB G063 G01 C1C C2W 2021:276:00000 2021:277:00000 ns -8.0252 0.0451 + DSB G063 G01 C1C C2W 2021:277:00000 2021:278:00000 ns -7.9378 0.0425 + DSB G063 G01 C1C C2W 2021:278:00000 2021:279:00000 ns -8.0441 0.0411 + DSB G063 G01 C1C C2W 2021:279:00000 2021:280:00000 ns -8.0465 0.0436 + DSB G063 G01 C1C C2W 2021:280:00000 2021:281:00000 ns -8.0182 0.0442 + DSB G063 G01 C1C C2W 2021:281:00000 2021:282:00000 ns -8.0229 0.0425 + DSB G063 G01 C1C C5Q 2021:274:00000 2021:275:00000 ns 2.9398 0.0567 + DSB G063 G01 C1C C5Q 2021:275:00000 2021:276:00000 ns 2.9253 0.0589 + DSB G063 G01 C1C C5Q 2021:276:00000 2021:277:00000 ns 2.8672 0.0559 + DSB G063 G01 C1C C5Q 2021:277:00000 2021:278:00000 ns 2.9940 0.0506 + DSB G063 G01 C1C C5Q 2021:278:00000 2021:279:00000 ns 2.8577 0.0488 + DSB G063 G01 C1C C5Q 2021:279:00000 2021:280:00000 ns 2.8671 0.0514 + DSB G063 G01 C2W C2L 2021:274:00000 2021:275:00000 ns 1.2955 0.0325 + DSB G063 G01 C2W C2L 2021:275:00000 2021:276:00000 ns 1.2784 0.0329 + DSB G063 G01 C2W C2L 2021:276:00000 2021:277:00000 ns 1.2902 0.0326 + DSB G063 G01 C2W C2L 2021:277:00000 2021:278:00000 ns 1.2976 0.0325 + DSB G063 G01 C2W C2L 2021:278:00000 2021:279:00000 ns 1.2952 0.0323 + DSB G063 G01 C2W C2L 2021:279:00000 2021:280:00000 ns 1.2988 0.0320 + DSB G063 G01 C2W C2L 2021:280:00000 2021:281:00000 ns 1.2946 0.0322 + DSB G063 G01 C2W C2L 2021:281:00000 2021:282:00000 ns 1.2962 0.0316 + DSB G063 G01 C2W C2L 2021:282:00000 2021:283:00000 ns 1.2881 0.0321 + DSB G063 G01 C2W C2L 2021:283:00000 2021:284:00000 ns 1.2915 0.0322 + DSB G063 G01 C2W C2L 2021:284:00000 2021:285:00000 ns 1.2869 0.0319 + DSB G061 G02 C1C C1W 2021:274:00000 2021:275:00000 ns 1.7559 0.0126 + DSB G061 G02 C1C C1W 2021:275:00000 2021:276:00000 ns 1.7672 0.0128 + DSB G061 G02 C1C C1W 2021:276:00000 2021:277:00000 ns 1.7549 0.0121 + DSB G061 G02 C1C C1W 2021:277:00000 2021:278:00000 ns 1.7552 0.0121 + DSB G061 G02 C1C C1W 2021:278:00000 2021:279:00000 ns 1.7487 0.0132 + DSB G061 G02 C1C C1W 2021:279:00000 2021:280:00000 ns 1.7591 0.0128 + DSB G061 G02 C1C C1W 2021:280:00000 2021:281:00000 ns 1.7552 0.0130 + DSB G061 G02 C1C C1W 2021:281:00000 2021:282:00000 ns 1.7561 0.0132 + DSB G061 G02 C1C C1W 2021:282:00000 2021:283:00000 ns 1.7614 0.0116 + DSB G061 G02 C1C C1W 2021:283:00000 2021:284:00000 ns 1.7605 0.0133 + DSB R730 R01 C1C C1P 2021:274:00000 2021:275:00000 ns 0.4270 0.0564 + DSB R730 R01 C1C C1P 2021:275:00000 2021:276:00000 ns 0.4457 0.0584 + DSB R730 R01 C1C C1P 2021:276:00000 2021:277:00000 ns 0.4676 0.0567 + DSB R730 R01 C1C C1P 2021:277:00000 2021:278:00000 ns 0.4680 0.0560 + DSB R730 R01 C1C C1P 2021:278:00000 2021:279:00000 ns 0.4472 0.0557 + DSB R730 R01 C1C C1P 2021:279:00000 2021:280:00000 ns 0.4476 0.0568 + DSB R730 R01 C1C C1P 2021:280:00000 2021:281:00000 ns 0.4536 0.0570 + DSB R730 R01 C1C C1P 2021:281:00000 2021:282:00000 ns 0.4312 0.0560 + DSB R730 R01 C1C C1P 2021:282:00000 2021:283:00000 ns 0.4325 0.0564 + DSB R730 R01 C1C C1P 2021:283:00000 2021:284:00000 ns 0.4599 0.0576 + DSB R730 R01 C1C C1P 2021:284:00000 2021:285:00000 ns 0.4668 0.0556 + DSB E208 E08 C1C C5Q 2021:274:00000 2021:275:00000 ns 2.8843 0.0513 + DSB E208 E08 C1C C5Q 2021:275:00000 2021:276:00000 ns 2.8150 0.0590 + DSB E208 E08 C1C C5Q 2021:276:00000 2021:277:00000 ns 3.0616 0.0626 + DSB E208 E08 C1C C5Q 2021:277:00000 2021:278:00000 ns 2.8655 0.0541 + DSB E208 E08 C1C C5Q 2021:278:00000 2021:279:00000 ns 2.8371 0.0455 + DSB E208 E08 C1C C5Q 2021:279:00000 2021:280:00000 ns 2.8741 0.0571 + DSB E208 E08 C1C C5Q 2021:280:00000 2021:281:00000 ns 2.9022 0.0583 + DSB E208 E08 C1C C5Q 2021:281:00000 2021:282:00000 ns 2.7755 0.0459 + DSB E208 E08 C1C C5Q 2021:282:00000 2021:283:00000 ns 2.8650 0.0519 + DSB E208 E08 C1C C5Q 2021:283:00000 2021:284:00000 ns 2.8944 0.0548 + DSB E208 E08 C1C C5Q 2021:284:00000 2021:285:00000 ns 2.8540 0.0515 + DSB E208 E08 C1C C5Q 2021:285:00000 2021:286:00000 ns 2.6923 0.0581 + DSB E208 E08 C1C C5Q 2021:286:00000 2021:287:00000 ns 2.8889 0.0528 + DSB E208 E08 C1C C5Q 2021:287:00000 2021:288:00000 ns 2.9141 0.0453 + DSB E208 E08 C1C C5Q 2021:288:00000 2021:289:00000 ns 2.8807 0.0478 + DSB E208 E08 C1C C5Q 2021:289:00000 2021:290:00000 ns 2.8893 0.0498 + DSB E208 E08 C1C C5Q 2021:290:00000 2021:291:00000 ns 2.9170 0.0482 + DSB E208 E08 C1C C5Q 2021:291:00000 2021:292:00000 ns 2.7170 0.0483 + DSB E208 E08 C1C C5Q 2021:292:00000 2021:293:00000 ns 2.8445 0.0515 + DSB E208 E08 C1C C5Q 2021:293:00000 2021:294:00000 ns 2.9190 0.0501 + DSB E208 E08 C1C C5Q 2021:294:00000 2021:295:00000 ns 2.7940 0.0407 + DSB E208 E08 C1C C5Q 2021:295:00000 2021:296:00000 ns 2.7929 0.0475 + DSB E208 E08 C1C C5Q 2021:296:00000 2021:297:00000 ns 2.9440 0.0542 + DSB E208 E08 C1C C5Q 2021:297:00000 2021:298:00000 ns 2.8282 0.0549 + DSB E208 E08 C1C C5Q 2021:298:00000 2021:299:00000 ns 2.7993 0.0556 + DSB E208 E08 C1C C5Q 2021:299:00000 2021:300:00000 ns 2.9611 0.0590 + DSB E208 E08 C1C C5Q 2021:300:00000 2021:301:00000 ns 2.8449 0.0502 + DSB E208 E08 C1C C5Q 2021:301:00000 2021:302:00000 ns 2.6883 0.0527 + DSB E208 E08 C1C C5Q 2021:302:00000 2021:303:00000 ns 2.9535 0.0570 + DSB E208 E08 C1C C5Q 2021:303:00000 2021:304:00000 ns 2.7641 0.0668 + DSB E208 E08 C1C C5Q 2021:304:00000 2021:305:00000 ns 2.7756 0.0535 + DSB E208 E08 C1C C5Q 2021:305:00000 2021:306:00000 ns 2.8402 0.0566 + DSB C017 C13 C2I C6I 2021:274:00000 2021:275:00000 ns 3.6061 0.0942 + DSB C017 C13 C2I C6I 2021:275:00000 2021:276:00000 ns 3.6020 0.0984 + DSB C017 C13 C2I C6I 2021:276:00000 2021:277:00000 ns 3.6126 0.0974 + DSB C017 C13 C2I C6I 2021:277:00000 2021:278:00000 ns 3.6363 0.0972 + DSB C017 C13 C2I C6I 2021:278:00000 2021:279:00000 ns 3.6559 0.0914 + DSB C017 C13 C2I C6I 2021:279:00000 2021:280:00000 ns 3.6204 0.0939 + DSB C017 C13 C2I C6I 2021:280:00000 2021:281:00000 ns 3.7392 0.0954 + DSB C017 C13 C2I C6I 2021:281:00000 2021:282:00000 ns 3.6668 0.0939 + DSB C017 C13 C2I C6I 2021:282:00000 2021:283:00000 ns 3.6711 0.0955 + DSB C017 C13 C2I C6I 2021:283:00000 2021:284:00000 ns 3.6470 0.0939 + DSB C017 C13 C2I C6I 2021:284:00000 2021:285:00000 ns 3.6058 0.0966 + DSB C017 C13 C2I C6I 2021:285:00000 2021:286:00000 ns 3.6307 0.0936 + DSB C017 C13 C2I C6I 2021:286:00000 2021:287:00000 ns 3.5364 0.0940 + DSB C017 C13 C2I C6I 2021:287:00000 2021:288:00000 ns 3.5890 0.0948 + DSB C017 C13 C2I C6I 2021:288:00000 2021:289:00000 ns 3.5868 0.0945 + DSB C017 C13 C2I C6I 2021:289:00000 2021:290:00000 ns 3.4208 0.0922 + DSB G078 G11 C1C C2W 2021:283:00000 2021:290:00000 ns 1.3135 0.0442 + DSB G G AGGO C1C C1L 2021:275:00000 2021:276:00000 ns -0.1844 0.0294 + DSB G G AGGO C1C C1L 2021:276:00000 2021:277:00000 ns -0.1714 0.0287 + DSB G G AGGO C1C C1L 2021:277:00000 2021:278:00000 ns -0.1711 0.0300 + DSB G G AGGO C1C C1L 2021:278:00000 2021:279:00000 ns -0.1644 0.0256 + DSB G G AGGO C1C C1L 2021:279:00000 2021:280:00000 ns -0.1689 0.0265 + DSB G G AGGO C1C C1L 2021:280:00000 2021:281:00000 ns -0.1913 0.0287 + DSB G G AGGO C1C C1L 2021:281:00000 2021:282:00000 ns -0.1658 0.0273 + DSB G G AGGO C1C C1L 2021:282:00000 2021:283:00000 ns -0.1915 0.0295 + DSB G G AGGO C1C C1L 2021:283:00000 2021:284:00000 ns -0.1824 0.0244 + DSB G G AGGO C1C C1L 2021:284:00000 2021:285:00000 ns -0.1768 0.0277 + DSB G G AGGO C1C C1L 2021:285:00000 2021:286:00000 ns -0.1667 0.0499 + DSB G G AGGO C1C C1L 2021:286:00000 2021:287:00000 ns -0.1630 0.0278 + DSB G G AGGO C1C C1L 2021:287:00000 2021:288:00000 ns -0.1924 0.0441 + DSB G G AGGO C1C C1L 2021:288:00000 2021:289:00000 ns -0.2038 0.0273 + DSB G G AGGO C1C C1L 2021:289:00000 2021:290:00000 ns -0.1840 0.0266 + DSB G G AGGO C1C C1L 2021:290:00000 2021:291:00000 ns -0.1741 0.0269 + DSB G G AGGO C1C C1L 2021:291:00000 2021:292:00000 ns -0.1864 0.0273 + DSB G G AGGO C1C C1L 2021:292:00000 2021:293:00000 ns -0.1894 0.0285 + DSB G G AGGO C1C C1L 2021:293:00000 2021:294:00000 ns -0.1954 0.0275 + DSB G G AGGO C1C C1L 2021:294:00000 2021:295:00000 ns -0.1908 0.0290 + DSB G G AGGO C1C C1L 2021:295:00000 2021:296:00000 ns -0.1846 0.0265 + DSB G G AGGO C1C C1L 2021:296:00000 2021:297:00000 ns -0.1884 0.0261 + DSB G G AGGO C1C C1L 2021:297:00000 2021:298:00000 ns -0.1740 0.0240 + DSB G G AGGO C1C C1L 2021:298:00000 2021:299:00000 ns -0.1963 0.0249 + DSB G G AGGO C1C C1L 2021:299:00000 2021:300:00000 ns -0.1957 0.0277 + DSB G G AGGO C1C C1L 2021:300:00000 2021:301:00000 ns -0.1999 0.0272 + DSB G G AGGO C1C C1L 2021:301:00000 2021:302:00000 ns -0.1821 0.0289 + DSB G G AGGO C1C C1L 2021:302:00000 2021:303:00000 ns -0.1854 0.0238 + DSB G G AGGO C1C C1L 2021:303:00000 2021:304:00000 ns -0.1610 0.0240 + DSB G G AGGO C1C C1L 2021:305:00000 2021:306:00000 ns -0.1834 0.0241 + DSB G G AGGO C1C C1L 2021:306:00000 2021:307:00000 ns -0.1850 0.0286 + DSB E E AGGO C1C C5Q 2021:274:00000 2021:275:00000 ns 2.2718 0.0985 + DSB E E AGGO C1C C5Q 2021:275:00000 2021:276:00000 ns 1.8420 0.1104 + DSB E E AGGO C1C C5Q 2021:276:00000 2021:277:00000 ns 1.7551 0.1155 + DSB E E AGGO C1C C5Q 2021:277:00000 2021:278:00000 ns 1.6289 0.0967 + DSB E E AGGO C1C C5Q 2021:278:00000 2021:279:00000 ns 1.9820 0.0881 + DSB E E AGGO C1C C5Q 2021:279:00000 2021:280:00000 ns 1.8115 0.0956 + DSB E E AGGO C1C C5Q 2021:280:00000 2021:281:00000 ns 0.8327 0.1092 + DSB E E AGGO C1C C5Q 2021:281:00000 2021:282:00000 ns 0.6914 0.0845 + DSB E E AGGO C1C C5Q 2021:282:00000 2021:283:00000 ns 1.9270 0.0862 + DSB E E AGGO C1C C5Q 2021:283:00000 2021:284:00000 ns 1.5861 0.1005 + DSB E E AGGO C1C C5Q 2021:284:00000 2021:285:00000 ns 1.3094 0.1020 + DSB E E AGGO C1C C5Q 2021:285:00000 2021:286:00000 ns 2.3790 0.1384 + DSB E E AGGO C1C C5Q 2021:286:00000 2021:287:00000 ns 1.3307 0.0893 + DSB E E AGGO C1C C5Q 2021:287:00000 2021:288:00000 ns 1.6232 0.1091 + DSB E E AGGO C1C C5Q 2021:288:00000 2021:289:00000 ns 1.2009 0.0985 + DSB E E AGGO C1C C5Q 2021:289:00000 2021:290:00000 ns 1.3268 0.0863 + DSB J J AIRA C1X C5X 2021:275:00000 2021:276:00000 ns -15.8850 0.3279 + DSB J J AIRA C1X C5X 2021:276:00000 2021:277:00000 ns -15.6392 0.4773 + DSB J J AIRA C1X C5X 2021:277:00000 2021:278:00000 ns -16.4433 0.3891 + DSB J J AIRA C1X C5X 2021:278:00000 2021:279:00000 ns -16.7385 0.3055 + DSB J J AIRA C1X C5X 2021:279:00000 2021:280:00000 ns -16.2287 0.4138 + DSB J J AIRA C1X C5X 2021:280:00000 2021:281:00000 ns -16.3054 0.5310 + DSB J J AIRA C1X C5X 2021:281:00000 2021:282:00000 ns -16.8184 0.3453 + DSB J J AIRA C1X C5X 2021:282:00000 2021:283:00000 ns -16.6188 0.3794 + DSB J J AIRA C1X C5X 2021:283:00000 2021:284:00000 ns -16.4500 0.4128 + DSB J J AIRA C1X C5X 2021:284:00000 2021:285:00000 ns -16.0934 0.3652 + DSB J J AIRA C1X C5X 2021:285:00000 2021:286:00000 ns -16.6076 0.3516 + DSB J J AIRA C1X C5X 2021:286:00000 2021:287:00000 ns -15.9966 0.4721 + DSB J J AIRA C1X C5X 2021:287:00000 2021:288:00000 ns -16.7860 0.3507 + DSB J J AIRA C1X C5X 2021:288:00000 2021:289:00000 ns -16.6548 0.3471 + DSB J J AIRA C1X C5X 2021:289:00000 2021:290:00000 ns -16.7481 0.3863 + DSB J J AIRA C1X C5X 2021:290:00000 2021:291:00000 ns -17.2781 0.3491 + DSB J J AIRA C1X C5X 2021:291:00000 2021:292:00000 ns -17.2820 0.3110 + DSB J J AIRA C1X C5X 2021:292:00000 2021:293:00000 ns -17.6060 0.3463 + DSB J J AIRA C1X C5X 2021:293:00000 2021:294:00000 ns -17.3941 0.3541 + DSB J J AIRA C1X C5X 2021:294:00000 2021:295:00000 ns -16.8271 0.3548 + DSB J J AIRA C1X C5X 2021:295:00000 2021:296:00000 ns -17.1273 0.3663 + DSB J J AIRA C1X C5X 2021:296:00000 2021:297:00000 ns -17.2388 0.3719 + DSB J J AIRA C1X C5X 2021:297:00000 2021:298:00000 ns -16.7945 0.4212 + DSB J J AIRA C1X C5X 2021:298:00000 2021:299:00000 ns -17.4373 0.3043 + DSB J J AIRA C1X C5X 2021:299:00000 2021:300:00000 ns -16.5743 0.4585 + DSB J J AIRA C1X C5X 2021:300:00000 2021:301:00000 ns -16.8834 0.3852 + DSB J J AIRA C1X C5X 2021:301:00000 2021:302:00000 ns -16.7790 0.4013 + DSB J J AIRA C1X C5X 2021:302:00000 2021:303:00000 ns -16.6366 0.4063 + DSB J J AIRA C1X C5X 2021:303:00000 2021:304:00000 ns -16.4386 0.3433 + DSB J J AIRA C1X C5X 2021:304:00000 2021:305:00000 ns -16.5724 0.3771 + DSB J J AIRA C1X C5X 2021:305:00000 2021:306:00000 ns -17.5522 0.3612 + DSB J J AIRA C1X C5X 2021:306:00000 2021:307:00000 ns -16.8070 0.3965 + DSB J J AIRA C1X C5X 2021:307:00000 2021:308:00000 ns -17.1508 0.3475 + DSB J J AIRA C1X C5X 2021:308:00000 2021:309:00000 ns -17.1474 0.4229 + DSB J J AIRA C1X C5X 2021:309:00000 2021:310:00000 ns -16.9885 0.3411 + DSB J J AIRA C1X C5X 2021:310:00000 2021:311:00000 ns -16.2453 0.4287 + DSB J J AIRA C1X C5X 2021:311:00000 2021:312:00000 ns -16.5387 0.3719 + DSB J J AIRA C1X C5X 2021:312:00000 2021:313:00000 ns -16.8080 0.4619 + DSB J J AIRA C1X C5X 2021:313:00000 2021:314:00000 ns -17.1998 0.4380 + DSB J J AIRA C1X C5X 2021:314:00000 2021:315:00000 ns -17.5592 0.4007 + DSB J J AIRA C1X C5X 2021:315:00000 2021:316:00000 ns -17.5454 0.4821 + DSB J J AIRA C1X C5X 2021:316:00000 2021:317:00000 ns -17.4004 0.3711 + DSB J J AIRA C1X C5X 2021:317:00000 2021:318:00000 ns -17.4694 0.4046 + DSB J J AIRA C1X C5X 2021:318:00000 2021:319:00000 ns -17.7327 0.3525 + DSB J J AIRA C1X C5X 2021:319:00000 2021:320:00000 ns -17.5691 0.3426 + DSB J J AIRA C1X C5X 2021:320:00000 2021:321:00000 ns -18.0696 0.3271 + DSB J J AIRA C1X C5X 2021:321:00000 2021:322:00000 ns -17.2864 0.4133 + DSB J J AIRA C1X C5X 2021:322:00000 2021:323:00000 ns -17.5390 0.3374 + DSB J J AIRA C1X C5X 2021:323:00000 2021:324:00000 ns -17.5877 0.2957 + DSB J J AIRA C1X C5X 2021:324:00000 2021:325:00000 ns -16.9507 0.4055 + DSB J J AIRA C1X C5X 2021:325:00000 2021:326:00000 ns -17.7235 0.3074 + DSB J J AIRA C1X C5X 2021:326:00000 2021:327:00000 ns -17.2184 0.4217 + DSB J J AIRA C1X C5X 2021:327:00000 2021:328:00000 ns -17.7891 0.3868 + DSB J J AIRA C1X C5X 2021:328:00000 2021:329:00000 ns -17.0176 0.3892 + DSB J J AIRA C1X C5X 2021:329:00000 2021:330:00000 ns -17.6940 0.3838 + DSB J J AIRA C1X C5X 2021:330:00000 2021:331:00000 ns -17.9788 0.3060 + DSB R R ALIC C1C C1P 2021:291:00000 2021:292:00000 ns -0.6630 0.1012 + DSB R R ALIC C1C C1P 2021:292:00000 2021:293:00000 ns -0.6419 0.0981 + DSB R R ALIC C1C C1P 2021:293:00000 2021:294:00000 ns -0.6534 0.0989 + DSB R R ALIC C1C C1P 2021:294:00000 2021:295:00000 ns -0.6409 0.1019 + DSB R R ALIC C1C C1P 2021:295:00000 2021:296:00000 ns -0.6764 0.1002 + DSB R R ALIC C1C C1P 2021:296:00000 2021:297:00000 ns -0.6556 0.0962 + DSB R R ALIC C1C C1P 2021:297:00000 2021:298:00000 ns -0.6632 0.0958 + DSB R R ALIC C1C C1P 2021:298:00000 2021:299:00000 ns -0.6623 0.1016 + DSB R R ALIC C1C C1P 2021:299:00000 2021:300:00000 ns -0.6510 0.1012 + DSB R R ALIC C1C C1P 2021:300:00000 2021:301:00000 ns -0.6458 0.0967 + DSB R R ALIC C1C C1P 2021:301:00000 2021:302:00000 ns -0.6426 0.0988 + DSB R R ALIC C1C C1P 2021:302:00000 2021:303:00000 ns -0.6537 0.0988 + DSB R R ALIC C1C C1P 2021:303:00000 2021:304:00000 ns -0.6714 0.1003 + DSB R R ALIC C1C C1P 2021:304:00000 2021:305:00000 ns -0.6426 0.1035 + DSB R R ALIC C1C C1P 2021:305:00000 2021:306:00000 ns -0.6832 0.1004 + DSB R R ALIC C1C C1P 2021:306:00000 2021:307:00000 ns -0.6681 0.1045 + DSB R R ALIC C1C C1P 2021:307:00000 2021:308:00000 ns -0.6508 0.1051 + DSB R R ALIC C1C C1P 2021:308:00000 2021:309:00000 ns -0.6490 0.1039 + DSB R R ALIC C1C C1P 2021:309:00000 2021:310:00000 ns -0.6689 0.1034 + DSB R R ALIC C1C C1P 2021:310:00000 2021:311:00000 ns -0.6573 0.1063 + DSB R R ALIC C1C C1P 2021:311:00000 2021:312:00000 ns -0.6839 0.1051 + DSB R R ALIC C1C C1P 2021:312:00000 2021:313:00000 ns -0.6479 0.1054 + DSB R R ALIC C1C C1P 2021:313:00000 2021:314:00000 ns -0.6571 0.1107 + DSB R R ALIC C1C C1P 2021:314:00000 2021:315:00000 ns -0.6704 0.1080 + DSB R R ALIC C1C C1P 2021:315:00000 2021:316:00000 ns -0.6459 0.0980 + DSB R R ALIC C1C C1P 2021:316:00000 2021:317:00000 ns -0.6241 0.1016 + DSB R R ALIC C1C C1P 2021:317:00000 2021:318:00000 ns -0.6471 0.1025 + DSB R R ALIC C1C C1P 2021:318:00000 2021:319:00000 ns -0.6545 0.1028 + DSB R R ALIC C1C C1P 2021:319:00000 2021:320:00000 ns -0.6782 0.1020 + DSB R R ALIC C1C C1P 2021:320:00000 2021:321:00000 ns -0.6523 0.0996 + DSB R R ALIC C1C C1P 2021:321:00000 2021:322:00000 ns -0.6537 0.1023 + DSB R R ALIC C1C C1P 2021:322:00000 2021:323:00000 ns -0.6364 0.0985 + DSB R R ALIC C1C C1P 2021:323:00000 2021:324:00000 ns -0.6595 0.0973 + DSB R R ALIC C1C C1P 2021:324:00000 2021:325:00000 ns -0.6505 0.0936 + DSB R R ALIC C1C C1P 2021:325:00000 2021:326:00000 ns -0.6738 0.0990 + DSB R R ALIC C1C C1P 2021:326:00000 2021:327:00000 ns -0.6805 0.1007 + DSB R R ALIC C1C C1P 2021:327:00000 2021:328:00000 ns -0.6945 0.1002 + DSB R R ALIC C1C C1P 2021:328:00000 2021:329:00000 ns -0.6614 0.0984 + DSB R R ALIC C1C C1P 2021:329:00000 2021:330:00000 ns -0.6654 0.0992 + DSB R R ALIC C1C C1P 2021:330:00000 2021:331:00000 ns -0.6301 0.0985 + DSB R R ALIC C1C C1P 2021:331:00000 2021:332:00000 ns -0.6410 0.0988 + DSB R R ALIC C1C C1P 2021:332:00000 2021:333:00000 ns -0.6344 0.0995 + DSB R R ALIC C1C C1P 2021:333:00000 2021:334:00000 ns -0.6786 0.1038 + DSB R R ALIC C1C C1P 2021:334:00000 2021:335:00000 ns -0.6681 0.1061 + DSB R R ALIC C1C C1P 2021:335:00000 2021:336:00000 ns -0.6747 0.1052 + DSB R R ALIC C1C C1P 2021:336:00000 2021:337:00000 ns -0.6522 0.1042 + DSB R R ALIC C1C C1P 2021:337:00000 2021:338:00000 ns -0.6641 0.1036 + DSB R R ALIC C1C C1P 2021:338:00000 2021:339:00000 ns -0.6648 0.1016 + DSB R R ALIC C1C C1P 2021:339:00000 2021:340:00000 ns -0.6557 0.0983 + DSB R R ALIC C1C C1P 2021:340:00000 2021:341:00000 ns -0.6642 0.1070 + DSB R R ALIC C1C C1P 2021:341:00000 2021:342:00000 ns -0.6596 0.1043 + DSB R R ALIC C1C C1P 2021:342:00000 2021:343:00000 ns -0.6840 0.1026 + DSB R R ALIC C1C C1P 2021:343:00000 2021:344:00000 ns -0.6742 0.1044 + DSB R R ALIC C1C C1P 2021:344:00000 2021:345:00000 ns -0.6552 0.1041 + DSB R R ALIC C1C C1P 2021:345:00000 2021:346:00000 ns -0.6600 0.1048 + DSB R R ALIC C1C C1P 2021:346:00000 2021:347:00000 ns -0.6579 0.1014 + DSB R R ALIC C1C C1P 2021:347:00000 2021:348:00000 ns -0.6544 0.1007 + DSB R R ALIC C1C C1P 2021:348:00000 2021:349:00000 ns -0.6657 0.1106 + DSB R R ALIC C1C C1P 2021:349:00000 2021:350:00000 ns -0.6754 0.1121 +-BIAS/SOLUTION +* +*------------------------------------------------------------------------------------------------------ +%=ENDBIA diff --git a/src/test/resources/sinex/JAX0MGXFIN_20202440000_01D_000_SOL.SNX b/src/test/resources/sinex/JAX0MGXFIN_20202440000_01D_000_SOL.SNX new file mode 100644 index 0000000000..2f14cd8644 --- /dev/null +++ b/src/test/resources/sinex/JAX0MGXFIN_20202440000_01D_000_SOL.SNX @@ -0,0 +1,1215 @@ +%=SNX 2.02 20:246:43630 20:244:00000 20:244:86100 P 405 2 S E +*------------------------------------------------------------------------------- ++FILE/REFERENCE + DESCRIPTION POSITION SOLUTION + OUTPUT + CONTACT + SOFTWARE MADOCA v.0.9.3 + INPUT STATION RINEX DATA +-FILE/REFERENCE +*------------------------------------------------------------------------------- ++FILE/COMMENT + MADOCA FINAL +-FILE/COMMENT +*------------------------------------------------------------------------------- ++SITE/ID + ABPO A 33302M001 P 47 13 45.2 -19 1 5.9 1553.0 + AGGO A 41596M001 P 301 51 36.5 -34 52 25.3 39.7 + ALGO A 40104M002 P 281 55 43.1 45 57 20.9 201.0 + ALIC A 50137M001 P 133 53 7.9 -23 40 12.4 603.2 + AMU2 A 66040M002 P 285 2 45.7 -89 59 53.4 2816.9 + AREG A 42202M008 P 288 30 25.5 -16 27 55.5 2489.4 + AREQ A 42202M005 P 288 30 25.9 -16 27 55.9 2488.9 + ARTU A 12362M001 P 58 33 37.7 56 25 47.4 247.6 + ASCG A 30602M004 P 345 40 2.4 -7 54 58.6 38.0 + BAKE A 40152M001 P 263 59 51.5 64 19 4.1 4.6 + BHR3 A 24901S002 P 50 36 29.3 26 12 32.9 -13.9 + BILB A 12363M002 P 166 27 12.2 68 3 55.1 315.8 + BJFS A 21601M001 P 115 53 33.0 39 36 31.0 87.5 + BRMU A 42501S004 P 295 18 13.4 32 22 13.4 -11.6 + BRST A 10004M004 P 355 30 12.3 48 22 49.8 65.8 + BRUX A 13101M010 P 4 21 30.8 50 47 53.0 158.1 + BUCU A 11401M001 P 26 7 32.7 44 27 50.2 143.2 + CAS1 A 66011M001 P 110 31 10.9 -66 17 0.1 22.5 + CCJ2 A 21732M002 P 142 11 42.1 27 4 3.1 104.2 + CEBR A 13408M001 P 355 37 55.7 40 27 12.4 775.8 + CEDU A 50138M001 P 133 48 35.4 -31 51 60.0 144.7 + CHAN A 21611M002 P 125 26 39.1 43 47 26.5 273.3 + CHPI A 41609M003 P 315 0 53.4 -22 41 13.7 617.4 + CHUR A 40128M002 P 265 54 40.6 58 45 32.7 -19.2 + CPNM A 21907M001 P 99 22 27.8 10 43 28.7 9.1 + CPVG A 39601M001 P 337 3 54.2 16 43 55.4 94.1 + CRO1 A 43201M001 P 295 24 56.5 17 45 24.8 -32.0 + CUSV A 21904S001 P 100 32 2.1 13 44 9.3 74.3 + CZTG A 91301M002 P 51 51 19.7 -46 25 54.8 202.8 + DAEJ A 23902M002 P 127 22 28.1 36 23 57.9 116.9 + DAKR A 34108M001 P 342 33 37.9 14 43 16.5 51.8 + DAV1 A 66010M001 P 77 58 21.4 -68 34 38.4 44.4 + DEAR A 30316M001 P 23 59 33.5 -30 39 54.7 1321.7 + DGAR A 30802M001 P 72 22 12.9 -7 16 10.9 -64.9 + DRAG A 20710S001 P 35 23 31.5 31 35 35.5 31.9 + DUBO A 40137M001 P 264 8 1.7 50 15 31.7 245.3 + DUND A 50212M003 P 170 35 49.8 -45 53 1.2 386.9 + DYNG A 12602M006 P 23 55 56.8 38 4 42.8 510.6 + EUR2 A 40153M002 P 274 3 44.6 79 59 20.2 29.1 + FALK A 80602M001 P 302 7 33.4 -51 41 37.1 50.8 + GLPS A 42005M002 P 269 41 46.8 -0 44 34.8 1.8 + GUUG A 82301M001 P 144 48 9.8 13 25 59.6 134.7 + HKSL A 23015M001 P 113 55 40.8 22 22 19.2 95.3 + HOB2 A 50116M004 P 147 26 19.5 -42 48 16.9 41.0 + HOFN A 10204M002 P 344 48 7.5 64 16 2.3 82.9 + HRAG A 30302M010 P 27 41 6.7 -25 53 25.4 1407.2 + IISC A 22306M002 P 77 34 13.4 13 1 16.2 843.7 + IQAL A 40194M001 P 291 29 22.2 63 45 21.5 91.8 + IRKJ A 12313M002 P 104 18 58.3 52 13 8.5 502.0 + JCTW A 30305M001 P 18 28 6.8 -33 57 5.2 83.6 + JPLM A 40400M007 P 241 49 36.4 34 12 17.4 424.0 + KARR A 50139M001 P 117 5 49.9 -20 58 53.1 109.1 + KAT1 A 59968M001 P 132 9 11.8 -14 22 33.6 184.3 + KIRI A 50305M001 P 172 55 22.4 1 21 16.5 36.2 + KIRU A 10403M002 P 20 58 6.4 67 51 26.5 391.0 + KITG A 12334M004 P 66 53 12.3 39 8 0.2 620.7 + KOKB A 40424M004 P 200 20 6.2 22 7 34.6 1167.4 + KOKV A 40424M004 P 200 20 6.2 22 7 34.6 1167.4 + KOUR A 97301M210 P 307 11 38.5 5 15 7.9 -25.8 + LAUT A 50804M002 P 177 26 47.7 -17 36 31.7 89.7 + LHAZ A 21613M002 P 91 6 14.5 29 39 26.4 3624.6 + LOVJ A P 34 36 57.1 67 53 27.5 463.9 + MAC1 A 50135M001 P 158 56 9.0 -54 29 58.3 -6.8 + MAJU A 51501M001 P 171 21 52.2 7 7 8.9 33.7 + MARS A 10073M008 P 5 21 13.6 43 16 43.6 61.8 + MAS1 A 31303M002 P 344 22 0.2 27 45 49.5 197.1 + MATE A 12734M008 P 16 42 16.1 40 38 56.9 535.7 + MAW1 A 66004M001 P 62 52 14.6 -67 36 17.2 59.1 + MBAR A 33901M001 P 30 44 16.4 -0 36 5.3 1337.5 + MCM4 A 66001M003 P 166 40 9.6 -77 50 18.1 98.0 + MELI A 19379M001 P 357 2 54.1 35 16 52.3 88.8 + MGUE A 41558M001 P 290 36 7.5 -35 46 38.5 1553.7 + MIKL A 12335M001 P 31 58 22.2 46 58 22.0 93.9 + MKEA A 40477M001 P 204 32 37.1 19 48 4.9 3754.7 + MOBK A 12365M002 P 36 34 11.0 55 6 53.6 182.7 + MORP A 13299S001 P 358 18 52.2 55 12 46.1 144.4 + NICO A 14302M001 P 33 23 47.2 35 8 27.6 190.0 + NIUM A 50210M001 P 190 4 22.5 -19 4 35.5 89.7 + NKLG A 32809M002 P 9 40 19.7 0 21 14.1 31.5 + NNOR A 50181M001 P 116 11 33.8 -31 2 55.4 234.8 + NOVM A 12367M002 P 82 54 34.2 55 1 49.8 150.1 + NTKA A 40143M001 P 233 23 0.2 49 35 32.5 -1.5 + NTUS A 22601M001 P 103 40 47.9 1 20 44.9 75.4 + NYA1 A 10317M003 P 11 51 55.1 78 55 46.4 84.4 + OWMG A 50253M004 P 183 37 52.3 -44 1 27.5 21.6 + PALM A 66005M002 P 295 56 56.0 -64 46 30.3 31.1 + PETS A 12355M003 P 158 39 0.5 53 1 23.9 102.0 + PFRR A 40495M001 P 212 34 1.0 65 7 2.8 512.4 + PICL A 40155M001 P 269 50 16.9 51 28 47.3 315.2 + PIE1 A 40456M001 P 251 52 51.9 34 18 5.4 2347.7 + PNGM A 51006M001 P 147 21 57.6 -2 2 35.6 116.3 + POL2 A 12348M001 P 74 41 39.4 42 40 47.2 1714.2 + PRDS A 40124M001 P 245 42 23.4 50 52 16.9 1248.0 + QUIN A 40433M004 P 239 3 20.0 39 58 28.4 1106.3 + REUN A 97401M003 P 55 34 18.2 -21 12 29.6 1558.4 + REYK A 10202M001 P 338 2 40.2 64 8 19.6 93.0 + RIGA A 12302M002 P 24 3 31.6 56 56 55.0 34.7 + RIOP A 42006M001 P 281 20 56.0 -1 39 2.1 2817.2 + SAVO A 41643M001 P 321 34 3.9 -12 56 21.3 76.3 + SBOK A 30320M001 P 17 52 45.2 -29 40 9.6 1043.1 + SCH2 A 40133M002 P 293 10 2.6 54 49 55.5 498.4 + SCRZ A 41801M001 P 296 50 25.2 -17 47 48.4 442.1 + SEY2 A 39801M006 P 55 28 46.0 -4 40 25.1 538.8 + SFER A 13402M004 P 353 47 39.7 36 27 51.7 84.2 + SGOC A 23501M003 P 79 52 27.0 6 53 31.5 -78.5 + SOLO A 51202M001 P 159 57 15.6 -9 26 5.7 122.9 + SSIA A 41401S001 P 270 53 0.3 13 41 49.5 626.6 + STFU A 49430S001 P 237 49 36.1 37 25 36.9 20.4 + STHL A 30606M003 P 354 19 57.6 -15 56 33.1 453.2 + STJ2 A 40101M004 P 307 19 18.1 47 35 42.8 152.6 + STK2 A 21731S004 P 141 50 41.4 43 31 43.1 118.6 + STR1 A 50119M002 P 149 0 36.2 -35 18 55.9 799.9 + THTG A 92201M016 P 210 23 36.8 -17 34 37.4 98.0 + THU2 A 43001M002 P 291 10 29.8 76 32 13.4 36.2 + TIXG A 12360M002 P 128 51 59.1 71 38 4.1 47.1 + TKSC A P 140 7 38.6 36 4 14.1 66.9 + TONG A 50902M001 P 184 49 14.9 -21 8 41.0 56.3 + TOW2 A 50140M001 P 147 3 20.5 -19 16 9.4 88.1 + TUKT A 40165M001 P 227 0 20.3 69 26 17.6 -1.5 + TWTF A TWTF01 P 121 9 52.2 24 57 12.8 201.6 + ULDI A 30318M001 P 31 25 15.3 -28 17 35.2 608.0 + USN8 A 40451S009 P 282 56 1.4 38 55 14.0 58.9 + VACS A 39003M001 P 57 29 49.3 -20 17 49.5 421.2 + WARK A 50243M001 P 174 39 46.0 -36 26 3.9 111.3 + WHIT A 40136M001 P 224 46 40.4 60 45 1.8 1427.4 + WILL A 40134M001 P 237 49 55.9 52 14 12.7 1095.7 + WROC A 12217M001 P 17 3 43.4 51 6 47.7 180.8 + XMIS A 50183M001 P 105 41 18.6 -10 26 59.9 261.5 + YAKT A 12353M002 P 129 40 49.1 62 1 51.4 103.4 + YEL3 A 40127M007 P 245 31 7.5 62 28 49.7 180.5 + YSSK A 12329M003 P 142 43 0.2 47 1 47.0 91.3 + ZAMB A 34601M001 P 28 18 39.7 -15 25 31.9 1324.9 + ZECK A 12351M001 P 41 33 54.3 43 47 18.2 1166.3 +-SITE/ID +*------------------------------------------------------------------------------- ++SITE/RECEIVER + ABPO A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30133 5.3.2 + AGGO A ---- P 00:000:00000 00:000:00000 LEICA GRX1200+GNSS 49588 9.20/6.407 + ALGO A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30159 5.3.2 + ALIC A ---- P 00:000:00000 00:000:00000 LEICA GR25 18304 4.31.101 + AMU2 A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR8 5010K 48.01 + AREG A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 17323 5.3.2 + AREQ A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 01712 3.7.9 + ARTU A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 02668 3.6.9 Nov,2 + ASCG A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5340K 5.45 + BAKE A ---- P 00:000:00000 00:000:00000 TPS NET-G3A 618-0 4.7 Nov,23, + BHR3 A ---- P 00:000:00000 00:000:00000 ITT 3750300 102 V3.2.14 + BILB A ---- P 00:000:00000 00:000:00000 ASHTECH Z-XII3 LP017 CC00 + BJFS A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5310K 4.85 + BRMU A ---- P 00:000:00000 00:000:00000 LEICA GRX1200GGPRO 35124 9.20/3.823 + BRST A ---- P 00:000:00000 00:000:00000 TRIMBLE ALLOY 5818R 5.45 + BRUX A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 30576 5.3.2 + BUCU A ---- P 00:000:00000 00:000:00000 LEICA GRX1200GGPRO 35536 8.71/3.822 + CAS1 A ---- P 00:000:00000 00:000:00000 SEPT POLARXS 30003 2.9.6 + CCJ2 A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5035K 5.45 + CEBR A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 47013 5.3.2 + CEDU A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30122 5.3.2 + CHAN A ---- P 00:000:00000 00:000:00000 ASHTECH UZ-12 UC220 CQ00 + CHPI A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30135 5.3.2 + CHUR A ---- P 00:000:00000 00:000:00000 TPS NET-G3A 618-0 4.7 Nov,23, + CPNM A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5426R 5.37 + CPVG A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5224K 5.45 + CRO1 A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02460 3.7.9 + CUSV A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 01926 3.7.9 + CZTG A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5439R 5.44 + DAEJ A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5412K 5.44 + DAKR A ---- P 00:000:00000 00:000:00000 TPS NET-G3A 618-0 4.7 Nov,23, + DAV1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30122 5.3.2 + DEAR A ---- P 00:000:00000 00:000:00000 TRIMBLE NETRS 46052 Nav 1.3-2 / + DGAR A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30185 5.3.2 + DRAG A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02631 3.6.14 + DUBO A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30159 5.2.0 + DUND A ---- P 00:000:00000 00:000:00000 TRIMBLE ALLOY 5920R 5.43 + DYNG A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5037K 5.45 + EUR2 A ---- P 00:000:00000 00:000:00000 TPS NET-G3A 618-0 4.7 Nov,23, + FALK A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30135 5.3.2 + GLPS A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 00584 3.7.9 + GUUG A ---- P 00:000:00000 00:000:00000 LEICA GR50 18704 4.30/7.402 + HKSL A ---- P 00:000:00000 00:000:00000 LEICA GR50 18702 4.31/7.403 + HOB2 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30122 5.3.2 + HOFN A ---- P 00:000:00000 00:000:00000 LEICA GR50 18706 4.31/7.403 + HRAG A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 00394 3.7.9 + IISC A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30135 5.3.2 + IQAL A ---- P 00:000:00000 00:000:00000 TPS NET-G3A 618-0 4.7 Nov,23, + IRKJ A ---- P 00:000:00000 00:000:00000 JPS LEGACY 00517 2.6.0 OCT,2 + JCTW A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5427R 5.01 + JPLM A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30256 5.3.2 + KARR A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5207K 5.44 + KAT1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30122 5.3.2 + KIRI A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5038K 5.44 + KIRU A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 45030 5.3.2 + KITG A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 17323 5.3.2 + KOKB A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 30139 5.3.2 + KOKV A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 01517 3.7.9 + KOUR A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 45030 5.3.2 + LAUT A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30251 5.3.2 + LHAZ A ---- P 00:000:00000 00:000:00000 LEICA GR25 18310 4.31/6.712 + LOVJ A ---- P 00:000:00000 00:000:00000 TPS LEGACY 8RHIX 2.6.0 Apr,1 + MAC1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30122 5.3.2 + MAJU A ---- P 00:000:00000 00:000:00000 SEPT POLARX4TR 30076 2.9.6 + MARS A ---- P 00:000:00000 00:000:00000 LEICA GR25 18313 4.31 + MAS1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 45030 5.3.2 + MATE A ---- P 00:000:00000 00:000:00000 LEICA GR30 17054 4.35/7.504 + MAW1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30159 5.3.2 + MBAR A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02557 3.7.6 + MCM4 A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 01705 3.7.9 + MELI A ---- P 00:000:00000 00:000:00000 LEICA GR10 17005 4.00/6.524 + MGUE A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 30254 5.3.2 + MIKL A ---- P 00:000:00000 00:000:00000 LEICA GR10 17006 4.30/6.525 + MKEA A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30135 5.3.2 + MOBK A ---- P 00:000:00000 00:000:00000 JPS EGGDT 8QIGP 2.7.0 Mar,3 + MORP A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR8 4843K 4.70 + NICO A ---- P 00:000:00000 00:000:00000 LEICA GR50 18302 4.31/7.300 + NIUM A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5338K 5.44 + NKLG A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 17323 5.3.2 + NNOR A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 47012 5.3.2 + NOVM A ---- P 00:000:00000 00:000:00000 JPS LEGACY 00202 2.6.0 OCT,2 + NTKA A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30159 5.2.0-patch + NTUS A ---- P 00:000:00000 00:000:00000 LEICA GR50 18324 4.20/7.300 + NYA1 A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR8 4843K 48.01 + OWMG A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5340K 5.45 + PALM A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 01708 3.7.9 + PETS A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 01336 3.6.16 Jan, + PFRR A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5423R 5.37 + PICL A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30465 5.2.0 + PIE1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30255 5.3.2 + PNGM A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5041K 5.44 + POL2 A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02563 3.7.9 + PRDS A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3N DELTA 02784 3.7.5p1 Jan + QUIN A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02567 3.7.9 + REUN A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30159 5.3.2 + REYK A ---- P 00:000:00000 00:000:00000 LEICA GR50 18301 4.31/7.403 + RIGA A ---- P 00:000:00000 00:000:00000 LEICA GR25 18304 4.30/6.713 + RIOP A ---- P 00:000:00000 00:000:00000 TRIMBLE NETRS 49271 1.3-2 + SAVO A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5750R 5.45 + SBOK A ---- P 00:000:00000 00:000:00000 TRIMBLE NETRS 46032 Nav 1.3-2 / + SCH2 A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3N DELTA 02686 3.7.5p1 Jan + SCRZ A ---- P 00:000:00000 00:000:00000 LEICA GR10 17008 4.12/6.712 + SEY2 A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 01321 3.7.9 + SFER A ---- P 00:000:00000 00:000:00000 LEICA GR25 18305 4.31/6.713 + SGOC A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 41716 3.7.9 + SOLO A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5041K 5.44 + SSIA A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5041K 5.42 + STFU A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 00184 3.7.9 + STHL A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02219 3.7.9 + STJ2 A ---- P 00:000:00000 00:000:00000 TPS NET-G5 1294- 5.2.2 Dec,0 + STK2 A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5035K 5.45 + STR1 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30122 5.3.2 + THTG A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 16283 5.3.2 + THU2 A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3T SIGMA 00812 3.7.9 Dec,3 + TIXG A ---- P 00:000:00000 00:000:00000 TPS ODYSSEY_E 8QURZ 2.7.0 Mar,3 + TKSC A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 ----- 5.37 + TONG A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30132 5.2.0 + TOW2 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30483 5.3.2 + TUKT A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5311K 5.03 + TWTF A ---- P 00:000:00000 00:000:00000 SEPT POLARX4 30080 2.9.0 + ULDI A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5739R Nav 5.30 / + USN8 A ---- P 00:000:00000 00:000:00000 SEPT POLARX5TR 47011 5.2.0 + VACS A ---- P 00:000:00000 00:000:00000 SEPT POLARX5 30134 5.3.2 + WARK A ---- P 00:000:00000 00:000:00000 TRIMBLE ALLOY 5921R 5.43 + WHIT A ---- P 00:000:00000 00:000:00000 TPS NET-G3A 618-0 4.7 Nov,23, + WILL A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5107K 4.85 + WROC A ---- P 00:000:00000 00:000:00000 LEICA GR50 18302 4.31/7.403 + XMIS A ---- P 00:000:00000 00:000:00000 TRIMBLE NETR9 5211K 5.44 + YAKT A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3N DELTA 02641 3.6.12 Oct, + YEL3 A ---- P 00:000:00000 00:000:00000 TPS NET-G5 1294- 5.2.2 Dec,0 + YSSK A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3N DELTA 02640 3.6.12 Oct, + ZAMB A ---- P 00:000:00000 00:000:00000 JAVAD TRE_3 DELTA 02462 3.7.9 + ZECK A ---- P 00:000:00000 00:000:00000 JAVAD TRE_G3TH DELTA 00180 3.2.7 May,1 +-SITE/RECEIVER +*------------------------------------------------------------------------------- ++SITE/ANTENNA + ABPO A ---- P 00:000:00000 00:000:00000 ASH701945G_M SCIT CR620 + AGGO A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72672 + ALGO A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 303 + ALIC A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 NONE 09370 + AMU2 A ---- P 00:000:00000 00:000:00000 ASH700936D_M SCIS 13569 + AREG A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 51293 + AREQ A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 01548 + ARTU A ---- P 00:000:00000 00:000:00000 ASH700936D_M DOME CR130 + ASCG A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 54063 + BAKE A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 383-0 + BHR3 A ---- P 00:000:00000 00:000:00000 TPSCR.G5 TPSH 762-1 + BILB A ---- P 00:000:00000 00:000:00000 ASH701945E_M SCIS CR620 + BJFS A ---- P 00:000:00000 00:000:00000 TRM59900.00 SCIS 53173 + BRMU A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 00480 + BRST A ---- P 00:000:00000 00:000:00000 TRM57971.00 NONE 14410 + BRUX A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 00464 + BUCU A ---- P 00:000:00000 00:000:00000 LEIAT504GG LEIS 20045 + CAS1 A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 LEIT 93100 + CCJ2 A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 50153 + CEBR A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 NONE 5661 + CEDU A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 194 + CHAN A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR620 + CHPI A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 38301 + CHUR A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR620 + CPNM A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM SCIS 00469 + CPVG A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 51383 + CRO1 A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM SCIS 00843 + CUSV A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 00918 + CZTG A ---- P 00:000:00000 00:000:00000 LEIAR20 LEIM 16247 + DAEJ A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 49233 + DAKR A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 383-2 + DAV1 A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 LEIT 09310 + DEAR A ---- P 00:000:00000 00:000:00000 ASH701941.B SCIS CRL19 + DGAR A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR620 + DRAG A ---- P 00:000:00000 00:000:00000 ASH700936D_M SNOW CR143 + DUBO A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 464 + DUND A ---- P 00:000:00000 00:000:00000 TRM115000.00 NONE 15510 + DYNG A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 54063 + EUR2 A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 383-0 + FALK A ---- P 00:000:00000 00:000:00000 ASH701945E_M SCIT CR620 + GLPS A ---- P 00:000:00000 00:000:00000 ASH701945B_M SCIT CR519 + GUUG A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 SPKE 5451 + HKSL A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72519 + HOB2 A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 203 + HOFN A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72528 + HRAG A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 LEIT 09310 + IISC A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR620 + IQAL A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 383-0 + IRKJ A ---- P 00:000:00000 00:000:00000 JPSREGANT_SD_E1 NONE RA022 + JCTW A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 31053 + JPLM A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 422 + KARR A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 50383 + KAT1 A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 LEIT 09310 + KIRI A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 54213 + KIRU A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 SPKE 5651 + KITG A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 52253 + KOKB A ---- P 00:000:00000 00:000:00000 ASH701945G_M NONE CR620 + KOKV A ---- P 00:000:00000 00:000:00000 ASH701945G_M NONE CR620 + KOUR A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 NONE 5653 + LAUT A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 00682 + LHAZ A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 08430 + LOVJ A ---- P 00:000:00000 00:000:00000 TPSPG_A1+GP MA+ 4 + MAC1 A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM SCIS 01514 + MAJU A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 01513 + MARS A ---- P 00:000:00000 00:000:00000 TRM57971.00 NONE 53111 + MAS1 A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 NONE 72505 + MATE A ---- P 00:000:00000 00:000:00000 LEIAR20 NONE 19203 + MAW1 A ---- P 00:000:00000 00:000:00000 AOAD/M_T AUST 275 + MBAR A ---- P 00:000:00000 00:000:00000 ASH701945B_M SCIS CR519 + MCM4 A ---- P 00:000:00000 00:000:00000 AOAD/M_T JPLA 363 + MELI A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72514 + MGUE A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 NONE 72512 + MIKL A ---- P 00:000:00000 00:000:00000 LEIAR10 NONE 16150 + MKEA A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM SCIS 00983 + MOBK A ---- P 00:000:00000 00:000:00000 JPSREGANT_SD_E1 NONE RA003 + MORP A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 221 + NICO A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72528 + NIUM A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 53475 + NKLG A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 4811A + NNOR A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 NONE 5657 + NOVM A ---- P 00:000:00000 00:000:00000 JPSREGANT_SD_E1 NONE RA003 + NTKA A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 SPKE 5140 + NTUS A ---- P 00:000:00000 00:000:00000 LEIAR20 LEIM 20288 + NYA1 A ---- P 00:000:00000 00:000:00000 ASH701073.1 SNOW CRG01 + OWMG A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 54063 + PALM A ---- P 00:000:00000 00:000:00000 ASH700936D_M SCIS CR141 + PETS A ---- P 00:000:00000 00:000:00000 ASH701933B_M SCIS 19990 + PFRR A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM SCIT 00531 + PICL A ---- P 00:000:00000 00:000:00000 TWIVC6150 NONE 19031 + PIE1 A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR520 + PNGM A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 50383 + POL2 A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 383-1 + PRDS A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE KW-94 + QUIN A ---- P 00:000:00000 00:000:00000 ASH701945E_M SNOW CR620 + REUN A ---- P 00:000:00000 00:000:00000 TRM55971.00 NONE 30976 + REYK A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72528 + RIGA A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72667 + RIOP A ---- P 00:000:00000 00:000:00000 TRM41249.00 NONE 60261 + SAVO A ---- P 00:000:00000 00:000:00000 TRM115000.00 NONE 15511 + SBOK A ---- P 00:000:00000 00:000:00000 ASH701941.B SCIS CRL19 + SCH2 A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR620 + SCRZ A ---- P 00:000:00000 00:000:00000 LEIAR10 NONE 14206 + SEY2 A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM SCIS 00805 + SFER A ---- P 00:000:00000 00:000:00000 LEIAR25 NONE 72638 + SGOC A ---- P 00:000:00000 00:000:00000 JAVRINGANT_G5T NONE 6076C + SOLO A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 954 + SSIA A ---- P 00:000:00000 00:000:00000 TRM59800.80 SCIS 4623A + STFU A ---- P 00:000:00000 00:000:00000 TRM57971.00 NONE 30123 + STHL A ---- P 00:000:00000 00:000:00000 TPSCR.G3 SCIS 383-0 + STJ2 A ---- P 00:000:00000 00:000:00000 TPSCR.G3 NONE 383-0 + STK2 A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 50153 + STR1 A ---- P 00:000:00000 00:000:00000 ASH701945C_M NONE CR620 + THTG A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 LEIT 10210 + THU2 A ---- P 00:000:00000 00:000:00000 ASH701073.1 SCIS G0111 + TIXG A ---- P 00:000:00000 00:000:00000 TPSCR3_GGD NONE 00263 + TKSC A ---- P 00:000:00000 00:000:00000 SEPCHOKE_B3E6 DOME 5171 + TONG A ---- P 00:000:00000 00:000:00000 TRM59800.00 NONE 4844A + TOW2 A ---- P 00:000:00000 00:000:00000 LEIAR25.R3 NONE 09310 + TUKT A ---- P 00:000:00000 00:000:00000 ASH701945E_M NONE CR620 + TWTF A ---- P 00:000:00000 00:000:00000 ASH701945C_M SCIS CR620 + ULDI A ---- P 00:000:00000 00:000:00000 ASH701941.B SCIS CRL19 + USN8 A ---- P 00:000:00000 00:000:00000 TPSCR.G5 TPSH 762-1 + VACS A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 00850 + WARK A ---- P 00:000:00000 00:000:00000 TRM115000.00 NONE 15510 + WHIT A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 289 + WILL A ---- P 00:000:00000 00:000:00000 TRM59800.00 SCIS 49313 + WROC A ---- P 00:000:00000 00:000:00000 LEIAR25.R4 LEIT 72527 + XMIS A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM NONE 00955 + YAKT A ---- P 00:000:00000 00:000:00000 ASH701933B_M SCIS CRN19 + YEL3 A ---- P 00:000:00000 00:000:00000 TWIVP6050_CONE NONE 60500 + YSSK A ---- P 00:000:00000 00:000:00000 ASH701933B_M DOME CRN19 + ZAMB A ---- P 00:000:00000 00:000:00000 AOAD/M_T NONE 372 + ZECK A ---- P 00:000:00000 00:000:00000 JAVRINGANT_DM JVDM 00314 +-SITE/ANTENNA +*------------------------------------------------------------------------------- ++SITE/GPS_PHASE_CENTER + AOAD/M_T NONE ----- .0918 .0007 -.0005 .1204 -.0003 -.0007 igs14_%Y%m + ASH700936D_M NONE ----- .0910 .0004 -.0003 .1204 -.0001 -.0001 igs14_%Y%m + ASH700936D_M SCIS ----- .0879 .0005 -.0001 .1192 .0001 -.0001 igs14_%Y%m + ASH700936D_M SNOW ----- .0909 .0003 -.0002 .1192 .0001 .0001 igs14_%Y%m + ASH701073.1 SCIS ----- .0861 .0005 .0009 .1152 -.0005 .0014 igs14_%Y%m + ASH701073.1 SNOW ----- .0890 .0005 .0010 .1163 -.0004 .0015 igs14_%Y%m + ASH701933B_M NONE ----- .0893 .0004 -.0008 .1188 -.0004 .0004 igs14_%Y%m + ASH701941.B SCIS ----- .0851 -.0006 .0002 .1197 -.0007 .0009 igs14_%Y%m + ASH701945B_M SCIS ----- .0876 .0011 -.0007 .1192 .0001 .0004 igs14_%Y%m + ASH701945B_M SCIT ----- .0892 .0008 -.0004 .1178 -.0004 -.0005 igs14_%Y%m + ASH701945C_M NONE ----- .0899 .0001 -.0007 .1192 .0003 .0004 igs14_%Y%m + ASH701945C_M SCIS ----- .0887 .0000 -.0002 .1156 .0003 -.0005 igs14_%Y%m + ASH701945E_M NONE ----- .0905 .0011 -.0001 .1190 .0000 .0006 igs14_%Y%m + ASH701945E_M SCIS ----- .0876 .0011 -.0007 .1192 .0001 .0004 igs14_%Y%m + ASH701945E_M SCIT ----- .0892 .0008 -.0004 .1178 -.0004 -.0005 igs14_%Y%m + ASH701945E_M SNOW ----- .0902 .0010 -.0007 .1159 .0004 .0004 igs14_%Y%m + ASH701945G_M NONE ----- .0895 .0001 -.0001 .1162 -.0007 -.0001 igs14_%Y%m + ASH701945G_M SCIT ----- .0892 .0008 -.0004 .1178 -.0004 -.0005 igs14_%Y%m + JAVRINGANT_DM NONE ----- .0897 .0006 .0014 .1162 .0004 -.0003 igs14_%Y%m + JAVRINGANT_DM JVDM ----- .0888 .0009 .0007 .1185 -.0000 -.0002 igs14_%Y%m + JAVRINGANT_DM SCIS ----- .0854 .0005 .0008 .1150 .0002 .0001 igs14_%Y%m + JAVRINGANT_G5T NONE ----- .0483 .0008 .0020 .0551 .0055 -.0026 igs14_%Y%m + JPSREGANT_SD_E1 NONE ----- .0912 -.0007 -.0002 .1154 .0000 -.0000 igs14_%Y%m + LEIAR10 NONE ----- .0880 .0014 -.0002 .0812 .0006 .0002 igs14_%Y%m + LEIAR20 NONE ----- .1242 .0001 -.0008 .1337 -.0000 -.0008 igs14_%Y%m + LEIAR20 LEIM ----- .1246 .0007 .0001 .1314 .0004 -.0001 igs14_%Y%m + LEIAR25 NONE ----- .1553 .0014 .0010 .1640 -.0001 .0003 igs14_%Y%m + LEIAR25.R3 NONE ----- .1614 .0012 .0003 .1590 .0003 -.0005 igs14_%Y%m + LEIAR25.R3 LEIT ----- .1617 .0004 .0001 .1579 .0001 -.0007 igs14_%Y%m + LEIAR25.R4 NONE ----- .1588 .0009 .0007 .1546 .0004 -.0001 igs14_%Y%m + LEIAR25.R4 LEIT ----- .1591 .0008 .0007 .1548 .0002 -.0001 igs14_%Y%m + LEIAT504GG LEIS ----- .0871 .0004 .0012 .1175 .0001 -.0003 igs14_%Y%m + SEPCHOKE_B3E6 NONE ----- .1251 .0001 -.0003 .1381 -.0002 .0002 igs14_%Y%m + SEPCHOKE_B3E6 SPKE ----- .1274 -.0000 -.0004 .1412 -.0001 .0002 igs14_%Y%m + TPSCR.G3 NONE ----- .0884 -.0002 .0003 .1194 .0003 -.0000 igs14_%Y%m + TPSCR.G3 SCIS ----- .0842 -.0002 .0003 .1203 .0004 -.0001 igs14_%Y%m + TPSCR.G5 TPSH ----- .0920 .0002 .0002 .1178 .0004 .0006 igs14_%Y%m + TPSCR3_GGD NONE ----- .0628 -.0000 -.0001 .0982 .0008 .0002 igs14_%Y%m + TPSPG_A1+GP NONE ----- .0348 -.0001 .0000 .0400 -.0000 -.0003 igs14_%Y%m + TRM115000.00 NONE ----- .0652 .0007 -.0001 .0577 .0008 .0002 igs14_%Y%m + TRM41249.00 NONE ----- .0559 .0003 .0005 .0580 .0001 .0005 igs14_%Y%m + TRM55971.00 NONE ----- .0667 .0013 -.0002 .0577 .0004 .0006 igs14_%Y%m + TRM57971.00 NONE ----- .0649 .0011 -.0000 .0580 .0001 .0007 igs14_%Y%m + TRM59800.00 NONE ----- .0895 .0010 .0007 .1171 .0001 .0000 igs14_%Y%m + TRM59800.00 SCIS ----- .0865 .0007 .0002 .1159 .0001 -.0001 igs14_%Y%m + TRM59800.80 SCIS ----- .0865 .0007 .0002 .1159 .0001 -.0001 igs14_%Y%m + TRM59900.00 SCIS ----- .1112 .0008 -.0004 .1248 .0004 .0005 igs14_%Y%m + TWIVC6150 NONE ----- .1270 .0006 .0010 .1374 -.0001 .0003 igs14_%Y%m + TWIVP6050_CONE NONE ----- .1017 -.0002 .0014 .1001 .0003 .0004 igs14_%Y%m +-SITE/GPS_PHASE_CENTER +*------------------------------------------------------------------------------- ++SITE/ECCENTRICITY + ABPO A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + AGGO A ---- P 00:000:00000 00:000:00000 UNE 0.1550 0.0000 0.0000 + ALGO A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + ALIC A ---- P 00:000:00000 00:000:00000 UNE 0.0015 0.0000 0.0000 + AMU2 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + AREG A ---- P 00:000:00000 00:000:00000 UNE 0.4291 0.0000 -0.0006 + AREQ A ---- P 00:000:00000 00:000:00000 UNE 0.0610 0.0000 0.0000 + ARTU A ---- P 00:000:00000 00:000:00000 UNE 0.0796 0.0000 0.0000 + ASCG A ---- P 00:000:00000 00:000:00000 UNE 0.4080 -0.0007 0.0001 + BAKE A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + BHR3 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + BILB A ---- P 00:000:00000 00:000:00000 UNE 0.0329 0.0000 0.0000 + BJFS A ---- P 00:000:00000 00:000:00000 UNE 0.0465 0.0000 0.0000 + BRMU A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + BRST A ---- P 00:000:00000 00:000:00000 UNE 2.0431 0.0000 0.0000 + BRUX A ---- P 00:000:00000 00:000:00000 UNE 0.4689 0.0010 0.0000 + BUCU A ---- P 00:000:00000 00:000:00000 UNE 0.0970 0.0000 0.0000 + CAS1 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + CCJ2 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + CEBR A ---- P 00:000:00000 00:000:00000 UNE 0.1527 0.0000 0.0000 + CEDU A ---- P 00:000:00000 00:000:00000 UNE 0.0060 0.0000 0.0000 + CHAN A ---- P 00:000:00000 00:000:00000 UNE 0.2500 0.0000 0.0000 + CHPI A ---- P 00:000:00000 00:000:00000 UNE 0.0792 0.0000 0.0000 + CHUR A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + CPNM A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + CPVG A ---- P 00:000:00000 00:000:00000 UNE 0.4300 0.0000 0.0000 + CRO1 A ---- P 00:000:00000 00:000:00000 UNE 0.6435 0.0000 0.0000 + CUSV A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + CZTG A ---- P 00:000:00000 00:000:00000 UNE 0.4400 0.0000 0.0000 + DAEJ A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + DAKR A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + DAV1 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + DEAR A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + DGAR A ---- P 00:000:00000 00:000:00000 UNE 0.0814 0.0000 0.0000 + DRAG A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + DUBO A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + DUND A ---- P 00:000:00000 00:000:00000 UNE 0.0020 0.0000 0.0000 + DYNG A ---- P 00:000:00000 00:000:00000 UNE 2.0180 0.0000 0.0000 + EUR2 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + FALK A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + GLPS A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + GUUG A ---- P 00:000:00000 00:000:00000 UNE 0.0032 0.0000 0.0000 + HKSL A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + HOB2 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + HOFN A ---- P 00:000:00000 00:000:00000 UNE 0.0319 0.0000 0.0000 + HRAG A ---- P 00:000:00000 00:000:00000 UNE 0.1280 0.0000 0.0000 + IISC A ---- P 00:000:00000 00:000:00000 UNE 0.0780 0.0000 0.0000 + IQAL A ---- P 00:000:00000 00:000:00000 UNE 0.0500 0.0000 0.0000 + IRKJ A ---- P 00:000:00000 00:000:00000 UNE 0.1280 0.0000 0.0000 + JCTW A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + JPLM A ---- P 00:000:00000 00:000:00000 UNE 0.0610 0.0000 0.0000 + KARR A ---- P 00:000:00000 00:000:00000 UNE 0.0005 0.0000 0.0000 + KAT1 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + KIRI A ---- P 00:000:00000 00:000:00000 UNE 0.0038 0.0000 0.0000 + KIRU A ---- P 00:000:00000 00:000:00000 UNE 0.1120 0.0000 0.0000 + KITG A ---- P 00:000:00000 00:000:00000 UNE 2.0374 0.0003 0.0000 + KOKB A ---- P 00:000:00000 00:000:00000 UNE 0.0614 0.0000 0.0000 + KOKV A ---- P 00:000:00000 00:000:00000 UNE 0.0614 0.0000 0.0000 + KOUR A ---- P 00:000:00000 00:000:00000 UNE 0.0950 0.0000 0.0000 + LAUT A ---- P 00:000:00000 00:000:00000 UNE 0.0056 0.0000 0.0000 + LHAZ A ---- P 00:000:00000 00:000:00000 UNE 0.1330 0.0000 0.0000 + LOVJ A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + MAC1 A ---- P 00:000:00000 00:000:00000 UNE 0.0280 0.0000 0.0000 + MAJU A ---- P 00:000:00000 00:000:00000 UNE 0.0016 0.0000 0.0000 + MARS A ---- P 00:000:00000 00:000:00000 UNE 0.0700 0.0000 0.0000 + MAS1 A ---- P 00:000:00000 00:000:00000 UNE 0.0330 0.0000 0.0000 + MATE A ---- P 00:000:00000 00:000:00000 UNE 0.1010 0.0000 0.0000 + MAW1 A ---- P 00:000:00000 00:000:00000 UNE 0.0035 0.0000 0.0000 + MBAR A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + MCM4 A ---- P 00:000:00000 00:000:00000 UNE 0.0814 0.0000 0.0000 + MELI A ---- P 00:000:00000 00:000:00000 UNE 0.0606 0.0000 0.0000 + MGUE A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + MIKL A ---- P 00:000:00000 00:000:00000 UNE 0.0237 0.0000 0.0000 + MKEA A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + MOBK A ---- P 00:000:00000 00:000:00000 UNE 0.1312 0.0000 0.0000 + MORP A ---- P 00:000:00000 00:000:00000 UNE 0.0330 0.0000 0.0000 + NICO A ---- P 00:000:00000 00:000:00000 UNE 0.0650 0.0000 0.0000 + NIUM A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + NKLG A ---- P 00:000:00000 00:000:00000 UNE 3.0430 -0.0015 -0.0025 + NNOR A ---- P 00:000:00000 00:000:00000 UNE 0.1121 0.0000 0.0000 + NOVM A ---- P 00:000:00000 00:000:00000 UNE 0.0800 0.0000 0.0000 + NTKA A ---- P 00:000:00000 00:000:00000 UNE 0.0771 0.0000 0.0000 + NTUS A ---- P 00:000:00000 00:000:00000 UNE 0.0776 0.0000 0.0000 + NYA1 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + OWMG A ---- P 00:000:00000 00:000:00000 UNE 2.0352 0.0004 0.0003 + PALM A ---- P 00:000:00000 00:000:00000 UNE 0.0794 0.0000 0.0000 + PETS A ---- P 00:000:00000 00:000:00000 UNE 0.0885 0.0000 0.0000 + PFRR A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + PICL A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + PIE1 A ---- P 00:000:00000 00:000:00000 UNE 0.0610 0.0000 0.0000 + PNGM A ---- P 00:000:00000 00:000:00000 UNE 0.0011 0.0000 0.0000 + POL2 A ---- P 00:000:00000 00:000:00000 UNE 0.0780 0.0000 0.0000 + PRDS A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + QUIN A ---- P 00:000:00000 00:000:00000 UNE 0.0614 0.0000 0.0000 + REUN A ---- P 00:000:00000 00:000:00000 UNE 0.0610 0.0000 0.0000 + REYK A ---- P 00:000:00000 00:000:00000 UNE 0.0635 0.0000 0.0000 + RIGA A ---- P 00:000:00000 00:000:00000 UNE 0.0850 0.0000 0.0000 + RIOP A ---- P 00:000:00000 00:000:00000 UNE 0.0729 0.0000 0.0000 + SAVO A ---- P 00:000:00000 00:000:00000 UNE 0.0010 0.0000 0.0000 + SBOK A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + SCH2 A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + SCRZ A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + SEY2 A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + SFER A ---- P 00:000:00000 00:000:00000 UNE 1.6260 0.0000 0.0000 + SGOC A ---- P 00:000:00000 00:000:00000 UNE 0.1300 0.0000 0.0000 + SOLO A ---- P 00:000:00000 00:000:00000 UNE 0.0010 0.0000 0.0000 + SSIA A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + STFU A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + STHL A ---- P 00:000:00000 00:000:00000 UNE 0.0083 0.0000 0.0000 + STJ2 A ---- P 00:000:00000 00:000:00000 UNE 0.0500 0.0000 0.0000 + STK2 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + STR1 A ---- P 00:000:00000 00:000:00000 UNE 0.0040 0.0000 0.0000 + THTG A ---- P 00:000:00000 00:000:00000 UNE 1.0500 0.0000 0.0000 + THU2 A ---- P 00:000:00000 00:000:00000 UNE 0.1002 0.0000 0.0000 + TIXG A ---- P 00:000:00000 00:000:00000 UNE 0.0792 0.0000 0.0000 + TKSC A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + TONG A ---- P 00:000:00000 00:000:00000 UNE 0.0030 0.0000 0.0000 + TOW2 A ---- P 00:000:00000 00:000:00000 UNE 0.0033 0.0000 0.0000 + TUKT A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + TWTF A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + ULDI A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + USN8 A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + VACS A ---- P 00:000:00000 00:000:00000 UNE 0.2500 0.0000 0.0000 + WARK A ---- P 00:000:00000 00:000:00000 UNE 0.0020 0.0000 0.0000 + WHIT A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + WILL A ---- P 00:000:00000 00:000:00000 UNE 0.1000 0.0000 0.0000 + WROC A ---- P 00:000:00000 00:000:00000 UNE 0.0000 0.0000 0.0000 + XMIS A ---- P 00:000:00000 00:000:00000 UNE 0.0015 0.0000 0.0000 + YAKT A ---- P 00:000:00000 00:000:00000 UNE 0.0810 0.0000 0.0000 + YEL3 A ---- P 00:000:00000 00:000:00000 UNE 0.1377 0.0000 0.0000 + YSSK A ---- P 00:000:00000 00:000:00000 UNE 0.0798 0.0000 0.0000 + ZAMB A ---- P 00:000:00000 00:000:00000 UNE 0.0540 0.0000 0.0000 + ZECK A ---- P 00:000:00000 00:000:00000 UNE 0.0450 0.0000 0.0000 +-SITE/ECCENTRICITY +*------------------------------------------------------------------------------- ++SATELLITE/PHASE_CENTER + G01 1 1.5018 .3940 .0000 2 1.5018 .3940 .0000 igs14_%Y%m A E + G02 1 .7288 .0013 -.0011 2 .7288 .0013 -.0011 igs14_%Y%m A E + G03 1 1.5506 .3940 .0000 2 1.5506 .3940 .0000 igs14_%Y%m A E + G05 1 .7780 -.0033 -.0003 2 .7780 -.0033 -.0003 igs14_%Y%m A E + G06 1 1.4670 .3940 .0000 2 1.4670 .3940 .0000 igs14_%Y%m A E + G07 1 .8224 -.0004 .0050 2 .8224 -.0004 .0050 igs14_%Y%m A E + G08 1 1.5014 .3940 .0000 2 1.5014 .3940 .0000 igs14_%Y%m A E + G09 1 1.5226 .3940 .0000 2 1.5226 .3940 .0000 igs14_%Y%m A E + G10 1 1.5151 .3940 .0000 2 1.5151 .3940 .0000 igs14_%Y%m A E + G11 1 1.1178 -.0007 -.0012 2 1.1178 -.0007 -.0012 igs14_%Y%m A E + G12 1 .7678 .0102 -.0056 2 .7678 .0102 -.0056 igs14_%Y%m A E + G13 1 1.3483 -.0024 -.0016 2 1.3483 -.0024 -.0016 igs14_%Y%m A E + G14 1 1.3045 -.0025 -.0017 2 1.3045 -.0025 -.0017 igs14_%Y%m A E + G15 1 .6228 .0045 .0019 2 .6228 .0045 .0019 igs14_%Y%m A E + G16 1 1.4687 .0126 -.0069 2 1.4687 .0126 -.0069 igs14_%Y%m A E + G17 1 .7709 .0030 .0010 2 .7709 .0030 .0010 igs14_%Y%m A E + G19 1 .8082 .0086 -.0006 2 .8082 .0086 -.0006 igs14_%Y%m A E + G20 1 1.3135 .0010 -.0032 2 1.3135 .0010 -.0032 igs14_%Y%m A E + G21 1 1.3591 -.0034 .0029 2 1.3591 -.0034 .0029 igs14_%Y%m A E + G22 1 .8506 -.0022 .0022 2 .8506 -.0022 .0022 igs14_%Y%m A E + G24 1 1.4071 .3940 .0000 2 1.4071 .3940 .0000 igs14_%Y%m A E + G25 1 1.5174 .3940 .0000 2 1.5174 .3940 .0000 igs14_%Y%m A E + G26 1 1.5035 .3940 .0000 2 1.5035 .3940 .0000 igs14_%Y%m A E + G27 1 1.5223 .3940 .0000 2 1.5223 .3940 .0000 igs14_%Y%m A E + G28 1 .9995 .0014 .0047 2 .9995 .0014 .0047 igs14_%Y%m A E + G29 1 .7918 .0109 -.0045 2 .7918 .0109 -.0045 igs14_%Y%m A E + G30 1 1.5221 .3940 .0000 2 1.5221 .3940 .0000 igs14_%Y%m A E + G31 1 .9125 -.0008 .0058 2 .9125 -.0008 .0058 igs14_%Y%m A E + G32 1 1.5348 .3940 .0000 2 1.5348 .3940 .0000 igs14_%Y%m A E + R01 1 2.3069 -.5450 .0000 2 2.3069 -.5450 .0000 igs14_%Y%m A E + R02 1 2.4137 -.5450 .0000 2 2.4137 -.5450 .0000 igs14_%Y%m A E + R03 1 2.5172 -.5450 .0000 2 2.5172 -.5450 .0000 igs14_%Y%m A E + R04 1 2.4500 -.5450 .0000 2 2.4500 -.5450 .0000 igs14_%Y%m A E + R05 1 2.4500 -.5450 .0000 2 2.4500 -.5450 .0000 igs14_%Y%m A E + R06 1 2.4114 -.5450 .0000 2 2.4114 -.5450 .0000 igs14_%Y%m A E + R07 1 2.6001 -.5450 .0000 2 2.6001 -.5450 .0000 igs14_%Y%m A E + R08 1 2.3238 -.5450 .0000 2 2.3238 -.5450 .0000 igs14_%Y%m A E + R09 1 2.0830 .0000 .0000 2 2.0830 .0000 .0000 igs14_%Y%m A E + R10 1 2.3251 -.5450 .0000 2 2.3251 -.5450 .0000 igs14_%Y%m A E + R11 1 2.4000 -.5450 .0000 2 2.4000 -.5450 .0000 igs14_%Y%m A E + R12 1 2.4500 -.5450 .0000 2 2.4500 -.5450 .0000 igs14_%Y%m A E + R13 1 2.3751 -.5450 .0000 2 2.3751 -.5450 .0000 igs14_%Y%m A E + R14 1 2.4500 -.5450 .0000 2 2.4500 -.5450 .0000 igs14_%Y%m A E + R15 1 2.4500 -.5450 .0000 2 2.4500 -.5450 .0000 igs14_%Y%m A E + R16 1 2.3610 -.5450 .0000 2 2.3610 -.5450 .0000 igs14_%Y%m A E + R17 1 2.3941 -.5450 .0000 2 2.3941 -.5450 .0000 igs14_%Y%m A E + R18 1 2.4902 -.5450 .0000 2 2.4902 -.5450 .0000 igs14_%Y%m A E + R19 1 2.4438 -.5450 .0000 2 2.4438 -.5450 .0000 igs14_%Y%m A E + R20 1 2.4085 -.5450 .0000 2 2.4085 -.5450 .0000 igs14_%Y%m A E + R21 1 2.3989 -.5450 .0000 2 2.3989 -.5450 .0000 igs14_%Y%m A E + R22 1 2.4612 -.5450 .0000 2 2.4612 -.5450 .0000 igs14_%Y%m A E + R23 1 2.2619 -.5450 .0000 2 2.2619 -.5450 .0000 igs14_%Y%m A E + R24 1 2.4500 -.5450 .0000 2 2.4500 -.5450 .0000 igs14_%Y%m A E + J01 1 3.1993 -.0011 .0016 2 2.9943 -.0011 .0016 igs14_%Y%m A E +-SATELLITE/PHASE_CENTER +*------------------------------------------------------------------------------- ++SOLUTION/EPOCHS + ABPO A ---- P 20:244:00000 20:244:86100 20:244:43200 + AGGO A ---- P 20:244:00000 20:244:86100 20:244:43200 + ALGO A ---- P 20:244:00000 20:244:86100 20:244:43200 + ALIC A ---- P 20:244:00000 20:244:86100 20:244:43200 + AMU2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + AREG A ---- P 20:244:00000 20:244:86100 20:244:43200 + AREQ A ---- P 20:244:00000 20:244:86100 20:244:43200 + ARTU A ---- P 20:244:00000 20:244:86100 20:244:43200 + ASCG A ---- P 20:244:00000 20:244:86100 20:244:43200 + BAKE A ---- P 20:244:00000 20:244:86100 20:244:43200 + BHR3 A ---- P 20:244:00000 20:244:86100 20:244:43200 + BILB A ---- P 20:244:00000 20:244:86100 20:244:43200 + BJFS A ---- P 20:244:00000 20:244:86100 20:244:43200 + BRMU A ---- P 20:244:00000 20:244:86100 20:244:43200 + BRST A ---- P 20:244:00000 20:244:86100 20:244:43200 + BRUX A ---- P 20:244:00000 20:244:86100 20:244:43200 + BUCU A ---- P 20:244:00000 20:244:86100 20:244:43200 + CAS1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + CCJ2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + CEBR A ---- P 20:244:00000 20:244:86100 20:244:43200 + CEDU A ---- P 20:244:00000 20:244:86100 20:244:43200 + CHAN A ---- P 20:244:00000 20:244:86100 20:244:43200 + CHPI A ---- P 20:244:00000 20:244:86100 20:244:43200 + CHUR A ---- P 20:244:00000 20:244:86100 20:244:43200 + CPNM A ---- P 20:244:00000 20:244:86100 20:244:43200 + CPVG A ---- P 20:244:00000 20:244:86100 20:244:43200 + CRO1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + CUSV A ---- P 20:244:00000 20:244:86100 20:244:43200 + CZTG A ---- P 20:244:00000 20:244:86100 20:244:43200 + DAEJ A ---- P 20:244:00000 20:244:86100 20:244:43200 + DAKR A ---- P 20:244:00000 20:244:86100 20:244:43200 + DAV1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + DEAR A ---- P 20:244:00000 20:244:86100 20:244:43200 + DGAR A ---- P 20:244:00000 20:244:86100 20:244:43200 + DRAG A ---- P 20:244:00000 20:244:86100 20:244:43200 + DUBO A ---- P 20:244:00000 20:244:86100 20:244:43200 + DUND A ---- P 20:244:00000 20:244:86100 20:244:43200 + DYNG A ---- P 20:244:00000 20:244:86100 20:244:43200 + EUR2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + FALK A ---- P 20:244:00000 20:244:86100 20:244:43200 + GLPS A ---- P 20:244:00000 20:244:86100 20:244:43200 + GUUG A ---- P 20:244:00000 20:244:86100 20:244:43200 + HKSL A ---- P 20:244:00000 20:244:86100 20:244:43200 + HOB2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + HOFN A ---- P 20:244:00000 20:244:86100 20:244:43200 + HRAG A ---- P 20:244:00000 20:244:86100 20:244:43200 + IISC A ---- P 20:244:00000 20:244:86100 20:244:43200 + IQAL A ---- P 20:244:00000 20:244:86100 20:244:43200 + IRKJ A ---- P 20:244:00000 20:244:86100 20:244:43200 + JCTW A ---- P 20:244:00000 20:244:86100 20:244:43200 + JPLM A ---- P 20:244:00000 20:244:86100 20:244:43200 + KARR A ---- P 20:244:00000 20:244:86100 20:244:43200 + KAT1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + KIRI A ---- P 20:244:00000 20:244:86100 20:244:43200 + KIRU A ---- P 20:244:00000 20:244:86100 20:244:43200 + KITG A ---- P 20:244:00000 20:244:86100 20:244:43200 + KOKB A ---- P 20:244:00000 20:244:86100 20:244:43200 + KOKV A ---- P 20:244:00000 20:244:86100 20:244:43200 + KOUR A ---- P 20:244:00000 20:244:86100 20:244:43200 + LAUT A ---- P 20:244:00000 20:244:86100 20:244:43200 + LHAZ A ---- P 20:244:00000 20:244:86100 20:244:43200 + LOVJ A ---- P 20:244:00000 20:244:86100 20:244:43200 + MAC1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + MAJU A ---- P 20:244:00000 20:244:86100 20:244:43200 + MARS A ---- P 20:244:00000 20:244:86100 20:244:43200 + MAS1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + MATE A ---- P 20:244:00000 20:244:86100 20:244:43200 + MAW1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + MBAR A ---- P 20:244:00000 20:244:86100 20:244:43200 + MCM4 A ---- P 20:244:00000 20:244:86100 20:244:43200 + MELI A ---- P 20:244:00000 20:244:86100 20:244:43200 + MGUE A ---- P 20:244:00000 20:244:86100 20:244:43200 + MIKL A ---- P 20:244:00000 20:244:86100 20:244:43200 + MKEA A ---- P 20:244:00000 20:244:86100 20:244:43200 + MOBK A ---- P 20:244:00000 20:244:86100 20:244:43200 + MORP A ---- P 20:244:00000 20:244:86100 20:244:43200 + NICO A ---- P 20:244:00000 20:244:86100 20:244:43200 + NIUM A ---- P 20:244:00000 20:244:86100 20:244:43200 + NKLG A ---- P 20:244:00000 20:244:86100 20:244:43200 + NNOR A ---- P 20:244:00000 20:244:86100 20:244:43200 + NOVM A ---- P 20:244:00000 20:244:86100 20:244:43200 + NTKA A ---- P 20:244:00000 20:244:86100 20:244:43200 + NTUS A ---- P 20:244:00000 20:244:86100 20:244:43200 + NYA1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + OWMG A ---- P 20:244:00000 20:244:86100 20:244:43200 + PALM A ---- P 20:244:00000 20:244:86100 20:244:43200 + PETS A ---- P 20:244:00000 20:244:86100 20:244:43200 + PFRR A ---- P 20:244:00000 20:244:86100 20:244:43200 + PICL A ---- P 20:244:00000 20:244:86100 20:244:43200 + PIE1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + PNGM A ---- P 20:244:00000 20:244:86100 20:244:43200 + POL2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + PRDS A ---- P 20:244:00000 20:244:86100 20:244:43200 + QUIN A ---- P 20:244:00000 20:244:86100 20:244:43200 + REUN A ---- P 20:244:00000 20:244:86100 20:244:43200 + REYK A ---- P 20:244:00000 20:244:86100 20:244:43200 + RIGA A ---- P 20:244:00000 20:244:86100 20:244:43200 + RIOP A ---- P 20:244:00000 20:244:86100 20:244:43200 + SAVO A ---- P 20:244:00000 20:244:86100 20:244:43200 + SBOK A ---- P 20:244:00000 20:244:86100 20:244:43200 + SCH2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + SCRZ A ---- P 20:244:00000 20:244:86100 20:244:43200 + SEY2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + SFER A ---- P 20:244:00000 20:244:86100 20:244:43200 + SGOC A ---- P 20:244:00000 20:244:86100 20:244:43200 + SOLO A ---- P 20:244:00000 20:244:86100 20:244:43200 + SSIA A ---- P 20:244:00000 20:244:86100 20:244:43200 + STFU A ---- P 20:244:00000 20:244:86100 20:244:43200 + STHL A ---- P 20:244:00000 20:244:86100 20:244:43200 + STJ2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + STK2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + STR1 A ---- P 20:244:00000 20:244:86100 20:244:43200 + THTG A ---- P 20:244:00000 20:244:86100 20:244:43200 + THU2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + TIXG A ---- P 20:244:00000 20:244:86100 20:244:43200 + TKSC A ---- P 20:244:00000 20:244:86100 20:244:43200 + TONG A ---- P 20:244:00000 20:244:86100 20:244:43200 + TOW2 A ---- P 20:244:00000 20:244:86100 20:244:43200 + TUKT A ---- P 20:244:00000 20:244:86100 20:244:43200 + TWTF A ---- P 20:244:00000 20:244:86100 20:244:43200 + ULDI A ---- P 20:244:00000 20:244:86100 20:244:43200 + USN8 A ---- P 20:244:00000 20:244:86100 20:244:43200 + VACS A ---- P 20:244:00000 20:244:86100 20:244:43200 + WARK A ---- P 20:244:00000 20:244:86100 20:244:43200 + WHIT A ---- P 20:244:00000 20:244:86100 20:244:43200 + WILL A ---- P 20:244:00000 20:244:86100 20:244:43200 + WROC A ---- P 20:244:00000 20:244:86100 20:244:43200 + XMIS A ---- P 20:244:00000 20:244:86100 20:244:43200 + YAKT A ---- P 20:244:00000 20:244:86100 20:244:43200 + YEL3 A ---- P 20:244:00000 20:244:86100 20:244:43200 + YSSK A ---- P 20:244:00000 20:244:86100 20:244:43200 + ZAMB A ---- P 20:244:00000 20:244:86100 20:244:43200 + ZECK A ---- P 20:244:00000 20:244:86100 20:244:43200 +-SOLUTION/EPOCHS +*------------------------------------------------------------------------------- ++SOLUTION/APRIORI +-SOLUTION/APRIORI +*------------------------------------------------------------------------------- ++SOLUTION/ESTIMATE + 1 STAX ABPO A ---- 20:244:00000 m 1 4.09721653707627e+06 1.96429e-03 + 2 STAY ABPO A ---- 20:244:00000 m 1 4.42911922281325e+06 2.10326e-03 + 3 STAZ ABPO A ---- 20:244:00000 m 1 -2.06577117089687e+06 1.34984e-03 + 4 STAX AGGO A ---- 20:243:75600 m 1 2.76512087312203e+06 1.48795e-03 + 5 STAY AGGO A ---- 20:243:75600 m 1 -4.44924840456180e+06 1.53010e-03 + 6 STAZ AGGO A ---- 20:243:75600 m 1 -3.62640368283103e+06 1.33182e-03 + 7 STAX ALGO A ---- 20:244:00000 m 0 9.18129122575808e+05 9.63736e-04 + 8 STAY ALGO A ---- 20:244:00000 m 0 -4.34607133229190e+06 1.05070e-03 + 9 STAZ ALGO A ---- 20:244:00000 m 0 4.56197791339339e+06 1.13024e-03 + 10 STAX ALIC A ---- 20:244:00000 m 1 -4.05205275964926e+06 1.25266e-03 + 11 STAY ALIC A ---- 20:244:00000 m 1 4.21283597494402e+06 1.33430e-03 + 12 STAZ ALIC A ---- 20:244:00000 m 1 -2.54510455015295e+06 1.15327e-03 + 13 STAX AMU2 A ---- 20:243:75600 m 1 5.31451432909263e+01 1.60036e-03 + 14 STAY AMU2 A ---- 20:243:75600 m 1 -1.97705026634663e+02 1.51740e-03 + 15 STAZ AMU2 A ---- 20:243:75600 m 1 -6.35956919192009e+06 4.96263e-03 + 16 STAX AREG A ---- 20:243:75600 m 1 1.94281643237003e+06 1.92645e-03 + 17 STAY AREG A ---- 20:243:75600 m 1 -5.80407717929008e+06 2.47561e-03 + 18 STAZ AREG A ---- 20:243:75600 m 1 -1.79688435425612e+06 1.45413e-03 + 19 STAX AREQ A ---- 20:244:00000 m 1 1.94282628781207e+06 1.53720e-03 + 20 STAY AREQ A ---- 20:244:00000 m 1 -5.80407036517997e+06 2.25554e-03 + 21 STAZ AREQ A ---- 20:244:00000 m 1 -1.79689411423997e+06 1.34994e-03 + 22 STAX ARTU A ---- 20:244:00000 m 1 1.84395634509567e+06 9.85823e-04 + 23 STAY ARTU A ---- 20:244:00000 m 1 3.01620326005750e+06 9.73348e-04 + 24 STAZ ARTU A ---- 20:244:00000 m 1 5.29126180040993e+06 1.24242e-03 + 25 STAX ASCG A ---- 20:243:75600 m 1 6.12115155171938e+06 1.71357e-03 + 26 STAY ASCG A ---- 20:243:75600 m 1 -1.56397894725494e+06 1.25060e-03 + 27 STAZ ASCG A ---- 20:243:75600 m 1 -8.72615304311080e+05 1.04329e-03 + 28 STAX BAKE A ---- 20:243:75600 m 1 -2.89834302132628e+05 9.98820e-04 + 29 STAY BAKE A ---- 20:243:75600 m 1 -2.75650117938633e+06 1.27310e-03 + 30 STAZ BAKE A ---- 20:243:75600 m 1 5.72516240101552e+06 2.04478e-03 + 31 STAX BHR3 A ---- 20:243:75600 m 1 3.63391012552735e+06 1.29663e-03 + 32 STAY BHR3 A ---- 20:243:75600 m 1 4.42527789192260e+06 1.37870e-03 + 33 STAZ BHR3 A ---- 20:243:75600 m 1 2.79986331794525e+06 1.18356e-03 + 34 STAX BILB A ---- 20:243:75600 m 1 -2.32308110367750e+06 1.68426e-03 + 35 STAY BILB A ---- 20:243:75600 m 1 5.59721809004125e+05 1.17270e-03 + 36 STAZ BILB A ---- 20:243:75600 m 1 5.89411091542067e+06 2.86138e-03 + 37 STAX BJFS A ---- 20:244:00000 m 0 -2.14874457919911e+06 1.03934e-03 + 38 STAY BJFS A ---- 20:244:00000 m 0 4.42664116893665e+06 1.13210e-03 + 39 STAZ BJFS A ---- 20:244:00000 m 0 4.04465579945032e+06 1.11386e-03 + 40 STAX BRMU A ---- 20:244:00000 m 0 2.30470326409750e+06 1.04908e-03 + 41 STAY BRMU A ---- 20:244:00000 m 0 -4.87481717444079e+06 1.10923e-03 + 42 STAZ BRMU A ---- 20:244:00000 m 0 3.39518705334743e+06 1.10216e-03 + 43 STAX BRST A ---- 20:244:00000 m 0 4.23116239889076e+06 1.01183e-03 + 44 STAY BRST A ---- 20:244:00000 m 0 -3.32746407063799e+05 8.83835e-04 + 45 STAZ BRST A ---- 20:244:00000 m 0 4.74513108625137e+06 1.07167e-03 + 46 STAX BRUX A ---- 20:244:00000 m 1 4.02788137209921e+06 1.52077e-03 + 47 STAY BRUX A ---- 20:244:00000 m 1 3.06998753268212e+05 1.15338e-03 + 48 STAZ BRUX A ---- 20:244:00000 m 1 4.91949903083274e+06 1.74765e-03 + 49 STAX BUCU A ---- 20:244:00000 m 0 4.09376060855364e+06 1.05808e-03 + 50 STAY BUCU A ---- 20:244:00000 m 0 2.00779408336812e+06 9.27080e-04 + 51 STAZ BUCU A ---- 20:244:00000 m 0 4.44513013289455e+06 1.08588e-03 + 52 STAX CAS1 A ---- 20:244:00000 m 1 -9.01776132878912e+05 2.29063e-03 + 53 STAY CAS1 A ---- 20:244:00000 m 1 2.40938322972198e+06 1.93436e-03 + 54 STAZ CAS1 A ---- 20:244:00000 m 1 -5.81674855988862e+06 3.22129e-03 + 55 STAX CCJ2 A ---- 20:243:75600 m 1 -4.49060512525100e+06 1.35573e-03 + 56 STAY CCJ2 A ---- 20:243:75600 m 1 3.48389505323521e+06 1.29347e-03 + 57 STAZ CCJ2 A ---- 20:243:75600 m 1 2.88492832642480e+06 1.19199e-03 + 58 STAX CEBR A ---- 20:243:75600 m 1 4.84666481815462e+06 1.84246e-03 + 59 STAY CEBR A ---- 20:243:75600 m 1 -3.70194991194532e+05 1.22434e-03 + 60 STAZ CEBR A ---- 20:243:75600 m 1 4.11692964393442e+06 1.70714e-03 + 61 STAX CEDU A ---- 20:244:00000 m 1 -3.75347323196648e+06 1.72116e-03 + 62 STAY CEDU A ---- 20:244:00000 m 1 3.91274103141520e+06 1.85513e-03 + 63 STAZ CEDU A ---- 20:244:00000 m 1 -3.34795965845175e+06 1.55760e-03 + 64 STAX CHAN A ---- 20:244:00000 m 1 -2.67442767952564e+06 1.26666e-03 + 65 STAY CHAN A ---- 20:244:00000 m 1 3.75714307335764e+06 1.50212e-03 + 66 STAZ CHAN A ---- 20:244:00000 m 1 4.39152151082316e+06 1.52824e-03 + 67 STAX CHPI A ---- 20:244:00000 m 1 4.16461390569981e+06 2.17070e-03 + 68 STAY CHPI A ---- 20:244:00000 m 1 -4.16245699157181e+06 2.08367e-03 + 69 STAZ CHPI A ---- 20:244:00000 m 1 -2.44502863115614e+06 1.51042e-03 + 70 STAX CHUR A ---- 20:244:00000 m 1 -2.36439158400110e+05 9.72741e-04 + 71 STAY CHUR A ---- 20:244:00000 m 1 -3.30761696751385e+06 1.23533e-03 + 72 STAZ CHUR A ---- 20:244:00000 m 1 5.43004934834796e+06 1.59430e-03 + 73 STAX CPNM A ---- 20:243:75600 m 1 -1.02087457101925e+06 1.48933e-03 + 74 STAY CPNM A ---- 20:243:75600 m 1 6.18376308740881e+06 3.75681e-03 + 75 STAZ CPNM A ---- 20:243:75600 m 1 1.17909615914879e+06 1.33418e-03 + 76 STAX CPVG A ---- 20:243:75600 m 1 5.62688343533104e+06 1.38269e-03 + 77 STAY CPVG A ---- 20:243:75600 m 1 -2.38093233628196e+06 1.11377e-03 + 78 STAZ CPVG A ---- 20:243:75600 m 1 1.82448399221532e+06 1.06108e-03 + 79 STAX CRO1 A ---- 20:244:00000 m 0 2.60777133139245e+06 1.72997e-03 + 80 STAY CRO1 A ---- 20:244:00000 m 0 -5.48807655654317e+06 2.25823e-03 + 81 STAZ CRO1 A ---- 20:244:00000 m 0 1.93276797577323e+06 1.50553e-03 + 82 STAX CUSV A ---- 20:243:75600 m 1 -1.13291500473151e+06 1.25939e-03 + 83 STAY CUSV A ---- 20:243:75600 m 1 6.09252854465714e+06 2.20773e-03 + 84 STAZ CUSV A ---- 20:243:75600 m 1 1.50463316648797e+06 1.17249e-03 + 85 STAX CZTG A ---- 20:243:75600 m 1 2.71999480431510e+06 1.28809e-03 + 86 STAY CZTG A ---- 20:243:75600 m 1 3.46339266017138e+06 1.40040e-03 + 87 STAZ CZTG A ---- 20:243:75600 m 1 -4.59861295748047e+06 1.31082e-03 + 88 STAX DAEJ A ---- 20:244:00000 m 1 -3.12004243932520e+06 1.28793e-03 + 89 STAY DAEJ A ---- 20:244:00000 m 1 4.08461468245434e+06 1.40552e-03 + 90 STAZ DAEJ A ---- 20:244:00000 m 1 3.76402678144623e+06 1.30820e-03 + 91 STAX DAKR A ---- 20:243:75600 m 1 5.88653346230893e+06 5.31672e-03 + 92 STAY DAKR A ---- 20:243:75600 m 1 -1.84918142251866e+06 2.04349e-03 + 93 STAZ DAKR A ---- 20:243:75600 m 1 1.61030057196778e+06 1.87760e-03 + 94 STAX DAV1 A ---- 20:244:00000 m 0 4.86854590466989e+05 2.00563e-03 + 95 STAY DAV1 A ---- 20:244:00000 m 0 2.28509916565437e+06 1.84986e-03 + 96 STAZ DAV1 A ---- 20:244:00000 m 0 -5.91495571496311e+06 2.29729e-03 + 97 STAX DEAR A ---- 20:243:75600 m 1 5.01761732529354e+06 1.79859e-03 + 98 STAY DEAR A ---- 20:243:75600 m 1 2.23321503366970e+06 1.39473e-03 + 99 STAZ DEAR A ---- 20:243:75600 m 1 -3.23469600960165e+06 1.34053e-03 + 100 STAX DGAR A ---- 20:244:00000 m 1 1.91626874514038e+06 1.83532e-03 + 101 STAY DGAR A ---- 20:244:00000 m 1 6.02997775032567e+06 2.39103e-03 + 102 STAZ DGAR A ---- 20:244:00000 m 1 -8.01719399366697e+05 1.17096e-03 + 103 STAX DRAG A ---- 20:244:00000 m 1 4.43298034380776e+06 1.27129e-03 + 104 STAY DRAG A ---- 20:244:00000 m 1 3.14943231168928e+06 1.09017e-03 + 105 STAZ DRAG A ---- 20:244:00000 m 1 3.32211075238116e+06 1.12988e-03 + 106 STAX DUBO A ---- 20:244:00000 m 1 -4.17603974629313e+05 9.73650e-04 + 107 STAY DUBO A ---- 20:244:00000 m 1 -4.06452984251072e+06 1.22223e-03 + 108 STAZ DUBO A ---- 20:244:00000 m 1 4.88143213078236e+06 1.39871e-03 + 109 STAX DUND A ---- 20:243:75600 m 1 -4.38812079643260e+06 1.35442e-03 + 110 STAY DUND A ---- 20:243:75600 m 1 7.26672036223992e+05 1.25056e-03 + 111 STAZ DUND A ---- 20:243:75600 m 1 -4.55653314040019e+06 1.31988e-03 + 112 STAX DYNG A ---- 20:243:75600 m 1 4.59522004926175e+06 1.15108e-03 + 113 STAY DYNG A ---- 20:243:75600 m 1 2.03943419129435e+06 9.66698e-04 + 114 STAZ DYNG A ---- 20:243:75600 m 1 3.91262590641127e+06 1.11238e-03 + 115 STAX EUR2 A ---- 20:243:75600 m 1 7.88044350543300e+04 9.36822e-04 + 116 STAY EUR2 A ---- 20:243:75600 m 1 -1.10959058140290e+06 1.00038e-03 + 117 STAZ EUR2 A ---- 20:243:75600 m 1 6.25935714067436e+06 2.14688e-03 + 118 STAX FALK A ---- 20:244:00000 m 1 2.10681139603357e+06 1.78966e-03 + 119 STAY FALK A ---- 20:244:00000 m 1 -3.35517075189751e+06 1.94771e-03 + 120 STAZ FALK A ---- 20:244:00000 m 1 -4.98178621876806e+06 2.16798e-03 + 121 STAX GLPS A ---- 20:244:00000 m 1 -3.38008679444301e+04 1.40699e-03 + 122 STAY GLPS A ---- 20:244:00000 m 1 -6.37751652237397e+06 2.72179e-03 + 123 STAZ GLPS A ---- 20:244:00000 m 1 -8.21542257125992e+04 1.26556e-03 + 124 STAX GUUG A ---- 20:244:00000 m 1 -5.07046516766384e+06 1.58160e-03 + 125 STAY GUUG A ---- 20:244:00000 m 1 3.57646029791640e+06 1.43427e-03 + 126 STAZ GUUG A ---- 20:244:00000 m 1 1.47209383371602e+06 1.18308e-03 + 127 STAX HKSL A ---- 20:243:75600 m 1 -2.39338310000231e+06 1.12407e-03 + 128 STAY HKSL A ---- 20:243:75600 m 1 5.39386095272781e+06 1.33648e-03 + 129 STAZ HKSL A ---- 20:243:75600 m 1 2.41259217276802e+06 1.10200e-03 + 130 STAX HOB2 A ---- 20:244:00000 m 0 -3.95007227277156e+06 1.45866e-03 + 131 STAY HOB2 A ---- 20:244:00000 m 0 2.52241536997261e+06 1.36956e-03 + 132 STAZ HOB2 A ---- 20:244:00000 m 0 -4.31163738766551e+06 1.42817e-03 + 133 STAX HOFN A ---- 20:244:00000 m 1 2.67968993159356e+06 1.02643e-03 + 134 STAY HOFN A ---- 20:244:00000 m 1 -7.27950978383187e+05 8.92493e-04 + 135 STAZ HOFN A ---- 20:244:00000 m 1 5.72278957794012e+06 1.33329e-03 + 136 STAX HRAG A ---- 20:243:75600 m 1 5.08541803042866e+06 1.43456e-03 + 137 STAY HRAG A ---- 20:243:75600 m 1 2.66822659465148e+06 1.28860e-03 + 138 STAZ HRAG A ---- 20:243:75600 m 1 -2.76875799174898e+06 1.12516e-03 + 139 STAX IISC A ---- 20:244:00000 m 1 1.33793578036742e+06 2.05127e-03 + 140 STAY IISC A ---- 20:244:00000 m 1 6.07031710648471e+06 3.24669e-03 + 141 STAZ IISC A ---- 20:244:00000 m 1 1.42787732141615e+06 1.41638e-03 + 142 STAX IQAL A ---- 20:244:00000 m 1 1.03600081359333e+06 1.05088e-03 + 143 STAY IQAL A ---- 20:244:00000 m 1 -2.63145560930948e+06 1.30264e-03 + 144 STAZ IQAL A ---- 20:244:00000 m 1 5.69781976712566e+06 2.03254e-03 + 145 STAX IRKJ A ---- 20:243:75600 m 1 -9.68328986134334e+05 1.01221e-03 + 146 STAY IRKJ A ---- 20:243:75600 m 1 3.79442650785751e+06 1.21969e-03 + 147 STAZ IRKJ A ---- 20:243:75600 m 1 5.01816719238053e+06 1.35536e-03 + 148 STAX JCTW A ---- 20:243:75600 m 1 5.02356449092868e+06 1.45959e-03 + 149 STAY JCTW A ---- 20:243:75600 m 1 1.67779583602065e+06 1.27260e-03 + 150 STAZ JCTW A ---- 20:243:75600 m 1 -3.54202580499221e+06 1.18710e-03 + 151 STAX JPLM A ---- 20:243:75600 m 1 -2.49330489409651e+06 1.50541e-03 + 152 STAY JPLM A ---- 20:243:75600 m 1 -4.65521487797275e+06 1.91987e-03 + 153 STAZ JPLM A ---- 20:243:75600 m 1 3.56549763935344e+06 1.66024e-03 + 154 STAX KARR A ---- 20:244:00000 m 1 -2.71383330127190e+06 1.20253e-03 + 155 STAY KARR A ---- 20:244:00000 m 1 5.30393511561327e+06 1.44640e-03 + 156 STAZ KARR A ---- 20:244:00000 m 1 -2.26951371974010e+06 1.12174e-03 + 157 STAX KAT1 A ---- 20:244:00000 m 0 -4.14741384451645e+06 1.55331e-03 + 158 STAY KAT1 A ---- 20:244:00000 m 0 4.58146258147283e+06 1.67979e-03 + 159 STAZ KAT1 A ---- 20:244:00000 m 0 -1.57335904600816e+06 1.20882e-03 + 160 STAX KIRI A ---- 20:244:00000 m 1 -6.32782232217795e+06 1.64224e-03 + 161 STAY KIRI A ---- 20:244:00000 m 1 7.85605221798494e+05 1.29865e-03 + 162 STAZ KIRI A ---- 20:244:00000 m 1 1.49769571465141e+05 1.18321e-03 + 163 STAX KIRU A ---- 20:244:00000 m 1 2.25142052611418e+06 1.77243e-03 + 164 STAY KIRU A ---- 20:244:00000 m 1 8.62817393704899e+05 1.54143e-03 + 165 STAZ KIRU A ---- 20:244:00000 m 1 5.88547682835381e+06 2.95636e-03 + 166 STAX KITG A ---- 20:243:75600 m 1 1.94487962374241e+06 1.35509e-03 + 167 STAY KITG A ---- 20:243:75600 m 1 4.55678382352252e+06 1.76431e-03 + 168 STAZ KITG A ---- 20:243:75600 m 1 4.00420596214582e+06 1.57961e-03 + 169 STAX KOKB A ---- 20:244:00000 m 1 -5.54383834795938e+06 2.15761e-03 + 170 STAY KOKB A ---- 20:244:00000 m 1 -2.05458576935226e+06 1.65392e-03 + 171 STAZ KOKB A ---- 20:244:00000 m 1 2.38781046834563e+06 1.54341e-03 + 172 STAX KOKV A ---- 20:244:00000 m 1 -5.54383834404617e+06 2.59593e-03 + 173 STAY KOKV A ---- 20:244:00000 m 1 -2.05458576409688e+06 1.70374e-03 + 174 STAZ KOKV A ---- 20:244:00000 m 1 2.38781046139903e+06 1.70412e-03 + 175 STAX KOUR A ---- 20:244:00000 m 1 3.83959132112819e+06 2.26879e-03 + 176 STAY KOUR A ---- 20:244:00000 m 1 -5.05956759875132e+06 2.33281e-03 + 177 STAZ KOUR A ---- 20:244:00000 m 1 5.79957229990181e+05 1.22131e-03 + 178 STAX LAUT A ---- 20:244:00000 m 1 -6.07519468603592e+06 2.70886e-03 + 179 STAY LAUT A ---- 20:244:00000 m 1 2.70923743560171e+05 1.58485e-03 + 180 STAZ LAUT A ---- 20:244:00000 m 1 -1.91718903134473e+06 1.53551e-03 + 181 STAX LHAZ A ---- 20:244:00000 m 1 -1.06942167635039e+05 1.04853e-03 + 182 STAY LHAZ A ---- 20:244:00000 m 1 5.54926976676719e+06 1.41355e-03 + 183 STAZ LHAZ A ---- 20:244:00000 m 1 3.13921522394195e+06 1.12812e-03 + 184 STAX LOVJ A ---- 20:243:75600 m 1 1.98143978763552e+06 1.76308e-03 + 185 STAY LOVJ A ---- 20:243:75600 m 1 1.36771315917226e+06 1.36284e-03 + 186 STAZ LOVJ A ---- 20:243:75600 m 1 5.88695657071528e+06 5.30126e-03 + 187 STAX MAC1 A ---- 20:244:00000 m 1 -3.46403891825236e+06 1.35991e-03 + 188 STAY MAC1 A ---- 20:244:00000 m 1 1.33417324482311e+06 1.26802e-03 + 189 STAZ MAC1 A ---- 20:244:00000 m 1 -5.16922389926014e+06 1.43142e-03 + 190 STAX MAJU A ---- 20:243:75600 m 1 -6.25757212149356e+06 2.51383e-03 + 191 STAY MAJU A ---- 20:243:75600 m 1 9.50333521236862e+05 1.99961e-03 + 192 STAZ MAJU A ---- 20:243:75600 m 1 7.85215620351095e+05 1.30472e-03 + 193 STAX MARS A ---- 20:243:75600 m 1 4.63053255805536e+06 1.18645e-03 + 194 STAY MARS A ---- 20:243:75600 m 1 4.33946606860361e+05 9.08489e-04 + 195 STAZ MARS A ---- 20:243:75600 m 1 4.35014290422556e+06 1.14352e-03 + 196 STAX MAS1 A ---- 20:244:00000 m 1 5.43919214556357e+06 2.14070e-03 + 197 STAY MAS1 A ---- 20:244:00000 m 1 -1.52205520213493e+06 1.42002e-03 + 198 STAZ MAS1 A ---- 20:244:00000 m 1 2.95345507413519e+06 1.53835e-03 + 199 STAX MATE A ---- 20:244:00000 m 1 4.64194927358364e+06 1.17956e-03 + 200 STAY MATE A ---- 20:244:00000 m 1 1.39304572198670e+06 9.42541e-04 + 201 STAZ MATE A ---- 20:244:00000 m 1 4.13328769796567e+06 1.14340e-03 + 202 STAX MAW1 A ---- 20:244:00000 m 1 1.11128722327410e+06 3.20657e-03 + 203 STAY MAW1 A ---- 20:244:00000 m 1 2.16891120161338e+06 4.02935e-03 + 204 STAZ MAW1 A ---- 20:244:00000 m 1 -5.87449361247364e+06 6.50592e-03 + 205 STAX MBAR A ---- 20:244:00000 m 0 5.48295111643716e+06 2.06056e-03 + 206 STAY MBAR A ---- 20:244:00000 m 0 3.26044287777236e+06 1.62530e-03 + 207 STAZ MBAR A ---- 20:244:00000 m 0 -6.65196066183496e+04 1.15976e-03 + 208 STAX MCM4 A ---- 20:244:00000 m 0 -1.31170303092068e+06 1.55210e-03 + 209 STAY MCM4 A ---- 20:244:00000 m 0 3.10814815242459e+05 1.54292e-03 + 210 STAZ MCM4 A ---- 20:244:00000 m 0 -6.21325514227343e+06 2.61771e-03 + 211 STAX MELI A ---- 20:243:75600 m 1 5.20562805477818e+06 1.18566e-03 + 212 STAY MELI A ---- 20:243:75600 m 1 -2.68410648824463e+05 9.37116e-04 + 213 STAZ MELI A ---- 20:243:75600 m 1 3.66342861644977e+06 1.09979e-03 + 214 STAX MGUE A ---- 20:243:75600 m 1 1.82332780473963e+06 1.82098e-03 + 215 STAY MGUE A ---- 20:243:75600 m 1 -4.85035255232840e+06 2.16957e-03 + 216 STAZ MGUE A ---- 20:243:75600 m 1 -3.70908543715484e+06 1.83717e-03 + 217 STAX MIKL A ---- 20:243:75600 m 1 3.69855368153506e+06 1.05659e-03 + 218 STAY MIKL A ---- 20:243:75600 m 1 2.30867625608655e+06 9.41056e-04 + 219 STAZ MIKL A ---- 20:243:75600 m 1 4.63976964276323e+06 1.12489e-03 + 220 STAX MKEA A ---- 20:244:00000 m 1 -5.46410542984447e+06 2.50264e-03 + 221 STAY MKEA A ---- 20:244:00000 m 1 -2.49516540941113e+06 2.04470e-03 + 222 STAZ MKEA A ---- 20:244:00000 m 1 2.14829168479909e+06 1.61549e-03 + 223 STAX MOBK A ---- 20:243:75600 m 1 2.93642432059397e+06 1.15330e-03 + 224 STAY MOBK A ---- 20:243:75600 m 1 2.17837424083431e+06 1.03788e-03 + 225 STAZ MOBK A ---- 20:243:75600 m 1 5.20885854864817e+06 1.48184e-03 + 226 STAX MORP A ---- 20:243:75600 m 1 3.64566762688703e+06 1.31364e-03 + 227 STAY MORP A ---- 20:243:75600 m 1 -1.07276991166640e+05 9.06425e-04 + 228 STAZ MORP A ---- 20:243:75600 m 1 5.21505368083645e+06 1.49023e-03 + 229 STAX NICO A ---- 20:244:00000 m 0 4.35941543687604e+06 1.07801e-03 + 230 STAY NICO A ---- 20:244:00000 m 0 2.87411724338673e+06 9.70164e-04 + 231 STAZ NICO A ---- 20:244:00000 m 0 3.65077802621226e+06 1.04180e-03 + 232 STAX NIUM A ---- 20:244:00000 m 1 -5.93716093450532e+06 1.63329e-03 + 233 STAY NIUM A ---- 20:244:00000 m 1 -1.05467513002500e+06 1.30805e-03 + 234 STAZ NIUM A ---- 20:244:00000 m 1 -2.07138597099681e+06 1.30164e-03 + 235 STAX NKLG A ---- 20:244:00000 m 0 6.28738570357629e+06 1.96640e-03 + 236 STAY NKLG A ---- 20:244:00000 m 0 1.07157487882177e+06 1.93389e-03 + 237 STAZ NKLG A ---- 20:244:00000 m 0 3.91332043304030e+04 1.10998e-03 + 238 STAX NNOR A ---- 20:244:00000 m 0 -2.41415241718818e+06 1.45374e-03 + 239 STAY NNOR A ---- 20:244:00000 m 0 4.90777864428810e+06 1.77525e-03 + 240 STAZ NNOR A ---- 20:244:00000 m 0 -3.27064417267214e+06 1.41282e-03 + 241 STAX NOVM A ---- 20:243:75600 m 1 4.52260693871654e+05 9.90924e-04 + 242 STAY NOVM A ---- 20:243:75600 m 1 3.63587761134051e+06 1.13630e-03 + 243 STAZ NOVM A ---- 20:243:75600 m 1 5.20345333962742e+06 1.34399e-03 + 244 STAX NTKA A ---- 20:243:75600 m 1 -2.47082048068984e+06 1.03761e-03 + 245 STAY NTKA A ---- 20:243:75600 m 1 -3.32494815021574e+06 1.12464e-03 + 246 STAZ NTKA A ---- 20:243:75600 m 1 4.83352089290618e+06 1.30906e-03 + 247 STAX NTUS A ---- 20:243:75600 m 1 -1.50802324693891e+06 1.15709e-03 + 248 STAY NTUS A ---- 20:243:75600 m 1 6.19557654805623e+06 1.51993e-03 + 249 STAZ NTUS A ---- 20:243:75600 m 1 1.48799317608331e+05 1.02455e-03 + 250 STAX NYA1 A ---- 20:244:00000 m 1 1.20243361523303e+06 2.89291e-03 + 251 STAY NYA1 A ---- 20:244:00000 m 1 2.52632404131778e+05 2.83576e-03 + 252 STAZ NYA1 A ---- 20:244:00000 m 1 6.23777276420359e+06 5.78288e-03 + 253 STAX OWMG A ---- 20:243:75600 m 1 -4.58439428347625e+06 1.47629e-03 + 254 STAY OWMG A ---- 20:243:75600 m 1 -2.90931594560963e+05 1.26551e-03 + 255 STAZ OWMG A ---- 20:243:75600 m 1 -4.41004788781889e+06 1.39013e-03 + 256 STAX PALM A ---- 20:243:75600 m 1 1.19267217435299e+06 1.73015e-03 + 257 STAY PALM A ---- 20:243:75600 m 1 -2.45088770042141e+06 2.19988e-03 + 258 STAZ PALM A ---- 20:243:75600 m 1 -5.74709604498242e+06 3.39023e-03 + 259 STAX PETS A ---- 20:243:75600 m 1 -3.58082836849176e+06 1.47300e-03 + 260 STAY PETS A ---- 20:243:75600 m 1 1.39969824984107e+06 1.26705e-03 + 261 STAZ PETS A ---- 20:243:75600 m 1 5.07218500888009e+06 1.82718e-03 + 262 STAX PFRR A ---- 20:243:75600 m 1 -2.26813039313757e+06 1.07405e-03 + 263 STAY PFRR A ---- 20:243:75600 m 1 -1.44868614635918e+06 1.05916e-03 + 264 STAZ PFRR A ---- 20:243:75600 m 1 5.76369601289626e+06 1.55972e-03 + 265 STAX PICL A ---- 20:243:75600 m 1 -1.12533603240120e+04 9.53575e-04 + 266 STAY PICL A ---- 20:243:75600 m 1 -3.98058678729800e+06 1.10805e-03 + 267 STAZ PICL A ---- 20:243:75600 m 1 4.96721030806199e+06 1.23804e-03 + 268 STAX PIE1 A ---- 20:244:00000 m 0 -1.64091712426985e+06 1.30880e-03 + 269 STAY PIE1 A ---- 20:244:00000 m 0 -5.01478118890921e+06 1.66205e-03 + 270 STAZ PIE1 A ---- 20:244:00000 m 0 3.57544700652407e+06 1.44201e-03 + 271 STAX PNGM A ---- 20:243:75600 m 1 -5.36794258212871e+06 1.40830e-03 + 272 STAY PNGM A ---- 20:243:75600 m 1 3.43743211617958e+06 1.33659e-03 + 273 STAZ PNGM A ---- 20:243:75600 m 1 -2.25885563416641e+05 1.11781e-03 + 274 STAX POL2 A ---- 20:244:00000 m 0 1.23997094044394e+06 1.25085e-03 + 275 STAY POL2 A ---- 20:244:00000 m 0 4.53079016736345e+06 1.89826e-03 + 276 STAZ POL2 A ---- 20:244:00000 m 0 4.30257887128301e+06 1.76364e-03 + 277 STAX PRDS A ---- 20:244:00000 m 0 -1.65960324249876e+06 9.68701e-04 + 278 STAY PRDS A ---- 20:244:00000 m 0 -3.67672578989247e+06 1.06584e-03 + 279 STAZ PRDS A ---- 20:244:00000 m 0 4.92549344536717e+06 1.18051e-03 + 280 STAX QUIN A ---- 20:244:00000 m 1 -2.51723167271184e+06 1.43863e-03 + 281 STAY QUIN A ---- 20:244:00000 m 1 -4.19859530635089e+06 1.83317e-03 + 282 STAZ QUIN A ---- 20:244:00000 m 1 4.07653149986675e+06 1.87524e-03 + 283 STAX REUN A ---- 20:244:00000 m 0 3.36409891067205e+06 1.58174e-03 + 284 STAY REUN A ---- 20:244:00000 m 0 4.90794467756632e+06 1.75618e-03 + 285 STAZ REUN A ---- 20:244:00000 m 0 -2.29346668325342e+06 1.26173e-03 + 286 STAX REYK A ---- 20:243:75600 m 1 2.58738397499970e+06 1.04178e-03 + 287 STAY REYK A ---- 20:243:75600 m 1 -1.04303356778543e+06 9.10950e-04 + 288 STAZ REYK A ---- 20:243:75600 m 1 5.71656415446036e+06 1.38778e-03 + 289 STAX RIGA A ---- 20:244:00000 m 0 3.18389891850943e+06 9.83075e-04 + 290 STAY RIGA A ---- 20:244:00000 m 0 1.42147871506805e+06 8.88726e-04 + 291 STAZ RIGA A ---- 20:244:00000 m 0 5.32281092793980e+06 1.18667e-03 + 292 STAX RIOP A ---- 20:243:75600 m 1 1.25514494369589e+06 1.34342e-03 + 293 STAY RIOP A ---- 20:243:75600 m 1 -6.25360944749661e+06 2.02204e-03 + 294 STAZ RIOP A ---- 20:243:75600 m 1 -1.82569688788330e+05 1.18111e-03 + 295 STAX SAVO A ---- 20:244:00000 m 0 4.87028374932937e+06 1.36925e-03 + 296 STAY SAVO A ---- 20:244:00000 m 0 -3.86460537911252e+06 1.30224e-03 + 297 STAZ SAVO A ---- 20:244:00000 m 0 -1.41887236231022e+06 1.08917e-03 + 298 STAX SBOK A ---- 20:243:75600 m 1 5.27949207586707e+06 1.90927e-03 + 299 STAY SBOK A ---- 20:243:75600 m 1 1.70311453076875e+06 1.36499e-03 + 300 STAZ SBOK A ---- 20:243:75600 m 1 -3.13909279068802e+06 1.35019e-03 + 301 STAX SCH2 A ---- 20:244:00000 m 0 1.44863652078669e+06 9.48108e-04 + 302 STAY SCH2 A ---- 20:244:00000 m 0 -3.38524380726187e+06 9.97204e-04 + 303 STAZ SCH2 A ---- 20:244:00000 m 0 5.19104725681342e+06 1.16134e-03 + 304 STAX SCRZ A ---- 20:243:75600 m 1 2.74300591768490e+06 1.46658e-03 + 305 STAY SCRZ A ---- 20:243:75600 m 1 -5.42074528317336e+06 1.69133e-03 + 306 STAZ SCRZ A ---- 20:243:75600 m 1 -1.93711698538658e+06 1.20721e-03 + 307 STAX SEY2 A ---- 20:243:75600 m 1 3.60286726456509e+06 3.19852e-03 + 308 STAY SEY2 A ---- 20:243:75600 m 1 5.23817946931272e+06 4.00117e-03 + 309 STAZ SEY2 A ---- 20:243:75600 m 1 -5.16268023710739e+05 1.43536e-03 + 310 STAX SFER A ---- 20:244:00000 m 1 5.10551888206754e+06 1.65208e-03 + 311 STAY SFER A ---- 20:244:00000 m 1 -5.55145612124564e+05 1.02127e-03 + 312 STAZ SFER A ---- 20:244:00000 m 1 3.76980359826153e+06 1.39715e-03 + 313 STAX SGOC A ---- 20:243:75600 m 1 1.11327965317018e+06 1.14602e-03 + 314 STAY SGOC A ---- 20:243:75600 m 1 6.23364432881190e+06 1.49473e-03 + 315 STAZ SGOC A ---- 20:243:75600 m 1 7.60277249471367e+05 1.00366e-03 + 316 STAX SOLO A ---- 20:243:75600 m 1 -5.91134014074507e+06 1.93421e-03 + 317 STAY SOLO A ---- 20:243:75600 m 1 2.15688777826539e+06 1.42676e-03 + 318 STAZ SOLO A ---- 20:243:75600 m 1 -1.03866355987731e+06 1.23038e-03 + 319 STAX SSIA A ---- 20:243:75600 m 1 9.55671035069559e+04 1.19539e-03 + 320 STAY SSIA A ---- 20:243:75600 m 1 -6.19778556755502e+06 1.62363e-03 + 321 STAZ SSIA A ---- 20:243:75600 m 1 1.50059060533324e+06 1.17101e-03 + 322 STAX STFU A ---- 20:243:75600 m 1 -2.70040440960151e+06 1.11501e-03 + 323 STAY STFU A ---- 20:243:75600 m 1 -4.29260525109693e+06 1.24489e-03 + 324 STAZ STFU A ---- 20:243:75600 m 1 3.85513753202237e+06 1.28813e-03 + 325 STAX STHL A ---- 20:244:00000 m 1 6.10481726118024e+06 2.78473e-03 + 326 STAY STHL A ---- 20:244:00000 m 1 -6.05827790622139e+05 1.42704e-03 + 327 STAZ STHL A ---- 20:244:00000 m 1 -1.74073867343562e+06 1.41136e-03 + 328 STAX STJ2 A ---- 20:243:75600 m 1 2.61259790436098e+06 1.02299e-03 + 329 STAY STJ2 A ---- 20:243:75600 m 1 -3.42683279826530e+06 1.02051e-03 + 330 STAZ STJ2 A ---- 20:243:75600 m 1 4.68675721849325e+06 1.18620e-03 + 331 STAX STK2 A ---- 20:243:75600 m 1 -3.64216147559698e+06 1.13949e-03 + 332 STAY STK2 A ---- 20:243:75600 m 1 2.86148787434735e+06 1.14839e-03 + 333 STAZ STK2 A ---- 20:243:75600 m 1 4.37035122100288e+06 1.25605e-03 + 334 STAX STR1 A ---- 20:243:75600 m 1 -4.46710322701162e+06 1.81911e-03 + 335 STAY STR1 A ---- 20:243:75600 m 1 2.68303947524523e+06 1.56414e-03 + 336 STAZ STR1 A ---- 20:243:75600 m 1 -3.66694872437635e+06 1.58194e-03 + 337 STAX THTG A ---- 20:243:75600 m 1 -5.24641535433858e+06 2.58242e-03 + 338 STAY THTG A ---- 20:243:75600 m 1 -3.07726054501523e+06 2.01611e-03 + 339 STAZ THTG A ---- 20:243:75600 m 1 -1.91384174515228e+06 1.58652e-03 + 340 STAX THU2 A ---- 20:243:75600 m 1 5.38093196414831e+05 9.42949e-04 + 341 STAY THU2 A ---- 20:243:75600 m 1 -1.38908813108968e+06 9.84827e-04 + 342 STAZ THU2 A ---- 20:243:75600 m 1 6.18097935476569e+06 2.11969e-03 + 343 STAX TIXG A ---- 20:243:75600 m 1 -1.26487351162599e+06 1.02576e-03 + 344 STAY TIXG A ---- 20:243:75600 m 1 1.56945575562909e+06 1.07337e-03 + 345 STAZ TIXG A ---- 20:243:75600 m 1 6.03100339273237e+06 1.84052e-03 + 346 STAX TKSC A ---- 20:243:75600 m 1 -3.96126342313571e+06 1.40091e-03 + 347 STAY TKSC A ---- 20:243:75600 m 1 3.30891368014186e+06 1.35173e-03 + 348 STAZ TKSC A ---- 20:243:75600 m 1 3.73456453659987e+06 1.37014e-03 + 349 STAX TONG A ---- 20:243:75600 m 1 -5.93030352406007e+06 4.23236e-03 + 350 STAY TONG A ---- 20:243:75600 m 1 -5.00148797425757e+05 3.11828e-03 + 351 STAZ TONG A ---- 20:243:75600 m 1 -2.28636628266622e+06 2.14188e-03 + 352 STAX TOW2 A ---- 20:244:00000 m 0 -5.05458341472631e+06 1.64331e-03 + 353 STAY TOW2 A ---- 20:244:00000 m 0 3.27550409974856e+06 1.46415e-03 + 354 STAZ TOW2 A ---- 20:244:00000 m 0 -2.09153843279784e+06 1.27563e-03 + 355 STAX TUKT A ---- 20:243:75600 m 1 -1.53209163623791e+06 1.26651e-03 + 356 STAY TUKT A ---- 20:243:75600 m 1 -1.64329177834462e+06 1.27050e-03 + 357 STAZ TUKT A ---- 20:243:75600 m 1 5.94931584517404e+06 2.85943e-03 + 358 STAX TWTF A ---- 20:243:75600 m 1 -2.99442864110201e+06 1.78753e-03 + 359 STAY TWTF A ---- 20:243:75600 m 1 4.95130908375307e+06 2.35840e-03 + 360 STAZ TWTF A ---- 20:243:75600 m 1 2.67449671184863e+06 1.62709e-03 + 361 STAX ULDI A ---- 20:243:75600 m 1 4.79668089362386e+06 1.65022e-03 + 362 STAY ULDI A ---- 20:243:75600 m 1 2.93031170927587e+06 1.42331e-03 + 363 STAZ ULDI A ---- 20:243:75600 m 1 -3.00543561968792e+06 1.24948e-03 + 364 STAX USN8 A ---- 20:243:75600 m 1 1.11216205184745e+06 1.37801e-03 + 365 STAY USN8 A ---- 20:243:75600 m 1 -4.84285468091985e+06 2.02510e-03 + 366 STAZ USN8 A ---- 20:243:75600 m 1 3.98549708497257e+06 1.76300e-03 + 367 STAX VACS A ---- 20:244:00000 m 1 3.21594689544052e+06 1.87724e-03 + 368 STAY VACS A ---- 20:244:00000 m 1 5.04744977647605e+06 2.36361e-03 + 369 STAZ VACS A ---- 20:244:00000 m 1 -2.19871815557964e+06 1.42250e-03 + 370 STAX WARK A ---- 20:243:75600 m 1 -5.11533351074780e+06 1.65880e-03 + 371 STAY WARK A ---- 20:243:75600 m 1 4.77886874453881e+05 1.27156e-03 + 372 STAZ WARK A ---- 20:243:75600 m 1 -3.76714710073041e+06 1.41534e-03 + 373 STAX WHIT A ---- 20:244:00000 m 1 -2.21833822581066e+06 1.00858e-03 + 374 STAY WHIT A ---- 20:244:00000 m 1 -2.20120513124730e+06 1.06398e-03 + 375 STAZ WHIT A ---- 20:244:00000 m 1 5.54305747662456e+06 1.37181e-03 + 376 STAX WILL A ---- 20:244:00000 m 0 -2.08425842633752e+06 9.84947e-04 + 377 STAY WILL A ---- 20:244:00000 m 0 -3.31387301319888e+06 1.06867e-03 + 378 STAZ WILL A ---- 20:244:00000 m 0 5.01985295978696e+06 1.21017e-03 + 379 STAX WROC A ---- 20:243:75600 m 1 3.83575103671934e+06 1.03695e-03 + 380 STAY WROC A ---- 20:243:75600 m 1 1.17725020449943e+06 8.92779e-04 + 381 STAZ WROC A ---- 20:243:75600 m 1 4.94160539055747e+06 1.13010e-03 + 382 STAX XMIS A ---- 20:244:00000 m 1 -1.69634478243851e+06 1.17350e-03 + 383 STAY XMIS A ---- 20:244:00000 m 1 6.03959000973898e+06 1.54755e-03 + 384 STAZ XMIS A ---- 20:244:00000 m 1 -1.14927504910747e+06 1.06017e-03 + 385 STAX YAKT A ---- 20:244:00000 m 0 -1.91499929065155e+06 9.78091e-04 + 386 STAY YAKT A ---- 20:244:00000 m 0 2.30824145447797e+06 1.00735e-03 + 387 STAZ YAKT A ---- 20:244:00000 m 0 5.61022549043808e+06 1.20031e-03 + 388 STAX YEL3 A ---- 20:243:75600 m 1 -1.22449473369127e+06 1.03556e-03 + 389 STAY YEL3 A ---- 20:243:75600 m 1 -2.68924115431037e+06 1.19475e-03 + 390 STAZ YEL3 A ---- 20:243:75600 m 1 5.63361682747129e+06 1.76398e-03 + 391 STAX YSSK A ---- 20:244:00000 m 1 -3.46532120430388e+06 1.09334e-03 + 392 STAY YSSK A ---- 20:244:00000 m 1 2.63826929429200e+06 1.10589e-03 + 393 STAZ YSSK A ---- 20:244:00000 m 1 4.64408530411068e+06 1.21626e-03 + 394 STAX ZAMB A ---- 20:244:00000 m 1 5.41535295399980e+06 2.77104e-03 + 395 STAY ZAMB A ---- 20:244:00000 m 1 2.91721021161991e+06 1.96160e-03 + 396 STAZ ZAMB A ---- 20:244:00000 m 1 -1.68588859788622e+06 1.45323e-03 + 397 STAX ZECK A ---- 20:244:00000 m 0 3.45117436200877e+06 1.02106e-03 + 398 STAY ZECK A ---- 20:244:00000 m 0 3.06033566351161e+06 9.50285e-04 + 399 STAZ ZECK A ---- 20:244:00000 m 0 4.39195579455645e+06 1.07117e-03 + 400 XPO ---- - ---- 20:244:43200 mas 1 2.16338886240885e+05 2.68348e+01 + 401 YPO ---- - ---- 20:244:43200 mas 1 3.61132472836304e+05 2.57866e+01 + 402 UT ---- - ---- 20:244:43200 ms 0 -1.85924799999999e+06 1.00000e-02 + 403 XPOR ---- - ---- 20:244:43200 ma/d 1 -9.41436741760124e+01 2.03755e+00 + 404 YPOR ---- - ---- 20:244:43200 ma/d 1 -9.00700436476757e+01 1.89295e+00 + 405 LOD ---- - ---- 20:244:43200 ms 1 -1.09444944128249e+03 2.33149e+00 +-SOLUTION/ESTIMATE +*------------------------------------------------------------------------------- +%ENDSNX diff --git a/src/test/resources/sinex/corrupted-header.snx b/src/test/resources/sinex/corrupted-header.snx new file mode 100644 index 0000000000..dcc25b044e --- /dev/null +++ b/src/test/resources/sinex/corrupted-header.snx @@ -0,0 +1,11 @@ +%=SNX 2.02 20:246:43??? 20:244:00000 20:244:86100 P 405 2 S E +*------------------------------------------------------------------------------- ++FILE/REFERENCE + DESCRIPTION POSITION SOLUTION + OUTPUT + CONTACT + SOFTWARE MADOCA v.0.9.3 + INPUT STATION RINEX DATA +-FILE/REFERENCE +*------------------------------------------------------------------------------- +%ENDSNX diff --git a/src/test/resources/sinex/issue1150.snx b/src/test/resources/sinex/issue1150.snx new file mode 100644 index 0000000000..b74b184c5c --- /dev/null +++ b/src/test/resources/sinex/issue1150.snx @@ -0,0 +1,27 @@ +%=SNX 2.02 JCT 20:111:61200 JCT 20:041:00000 20:111:61200 L 00549 0 X ++FILE/REFERENCE + DESCRIPTION UNE 200420 ILRS eccentricities file + OUTPUT ILRS SINEX Formatted Eccentricity File + CONTACT epavlis@UMBC.edu + SOFTWARE JCET's eccCDDIS2snx + HARDWARE Apple MacBook Pro + INPUT Official Eccentricity file from CDDIS (110707) + INPUT and previous version slrecc.191030.ILRS.une.snx + INPUT Numerous emails/reports from Van Husson’s review +-FILE/REFERENCE ++INPUT/HISTORY + =SNX 2.02 JCT 20:111:61200 JCT 68:041:00000 20:111:61200 L 00549 0 X +-INPUT/HISTORY ++SITE/ID +*Code PT __DOMES__ T _STATION DESCRIPTION__ APPROX_LON_ APPROX_LAT_ _APP_H_ CDP-SOD_ + 1148 A --- L Ondrejov OND. FIXED 14 47 08.1 49 54 49.3 592.6 11480901 + 7035 A 40436M003 L Otay Mount TLRS-3 243 9 32.9 34 22 55.2 2200.0 70351301 71200101 + 7120 A 40445M002 L Haleakala MOBLAS-1 203 44 38.2 20 42 27.4 3069.6 71200101 +-SITE/ID ++SITE/ECCENTRICITY +*SITE PT SOLN T DATA_START__ DATA_END____ UNE UP______ NORTH___ EAST____ CDP-SOD_ + 1148 A 1 L 00:000:00000 20:095:86399 UNE 0.0000 0.0000 0.0000 11480901 + 7035 A 1 L 20:076:00000 00:000:00000 UNE 2.5870 0.0060 0.0170 70351301 + 7120 A 1 L 00:000:00000 00:000:00000 UNE 3.6020 -0.0130 0.0090 71200101 +-SITE/ECCENTRICITY +%ENDSNX \ No newline at end of file diff --git a/src/test/resources/sp3/example-d-1.sp3 b/src/test/resources/sp3/example-d-1.sp3 index 9b916d29f4..df55a2b1ed 100644 --- a/src/test/resources/sp3/example-d-1.sp3 +++ b/src/test/resources/sp3/example-d-1.sp3 @@ -44,6 +44,7 @@ PS37 -34534.904566 24164.610955 29.812840 0.299420 PS38 -12548.655240 -40249.910397 -3.521920 -0.027787 * 2013 4 3 0 15 0.00000000 PG01 0.00000 0.000000 0.000000 0.000000 +PG02 -22399.401440 13799.162332 1599.367722 425.400822 * 2013 4 3 23 45 0.00000000 PG01 4340.761149 -17469.395805 -19521.652181 13.021579 PG02 -22187.015530 13877.264416 2583.141886 425.527461 diff --git a/src/test/resources/sp3/example-d-3.sp3 b/src/test/resources/sp3/example-d-3.sp3 index d8c92df0e7..cd5395b8f1 100644 --- a/src/test/resources/sp3/example-d-3.sp3 +++ b/src/test/resources/sp3/example-d-3.sp3 @@ -1,6 +1,6 @@ -#dV2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX +#dP2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX ## 1126 259200.00000000 900.00000000 52129 0.0000000000000 -+ 26 G01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 ++ 26 I01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 + G23G24G25G26G27G28G29G30G31 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/src/test/resources/sp3/example-d-4.sp3 b/src/test/resources/sp3/example-d-4.sp3 index f6a477f16b..9bf0418133 100644 --- a/src/test/resources/sp3/example-d-4.sp3 +++ b/src/test/resources/sp3/example-d-4.sp3 @@ -1,6 +1,6 @@ -#dV2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX +#dP2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX ## 1126 259200.00000000 900.00000000 52129 0.0000000000000 -+ 26 G01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 ++ 26 S01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 + G23G24G25G26G27G28G29G30G31 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/src/test/resources/sp3/exceeded-sat-count.sp3 b/src/test/resources/sp3/exceeded-sat-count.sp3 new file mode 100644 index 0000000000..0ea4eda1c8 --- /dev/null +++ b/src/test/resources/sp3/exceeded-sat-count.sp3 @@ -0,0 +1,60 @@ +#cP2013 4 3 0 0 0.00000000 3 ORBIT WGS84 BCT MGEX +## 1734 259200.00000000 900.00000000 56385 0.0000000000000 ++ 140 G01G02G03G04G05G06G07G08G09G10G11G12G13G14G15G16G17 ++ G18G19G20G21G22G23G24G25G26G27G28G29G30G31G32R01R02 ++ R03R04R05R06R07R08R09R10R11R12R13R14R15R16R17R18R19 ++ R20R21R22R23R24E01E02E03E04E05E06E07E08E09E10E11E12 ++ E13E14E15E16E17E18E19E20E21E22E23E24E25E26E27E28E29 ++ E30C01C02C03C04C05C06C07C08C09C10C11C12C13C14C15C16 ++ C17C18C19C20C21C22C23C24C25C26C27C28C29C30C31C32C33 ++ C34C35J01J02J03I01I02I03I04I05I06I07S20S24S27S28S29 ++ S33S35S37S38 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* Note: This is a simulated file, meant to illustrate what an SP3-d header +/* might look like with more than 85 satellites. Source for GPS and SBAS satel- +/* lite positions: BRDM0930.13N. G=GPS,R=GLONASS,E=Galileo,C=BeiDou,J=QZSS, +/* I=IRNSS,S=SBAS. For definitions of SBAS satellites, refer to the website: +/* http://igs.org/mgex/status-SBAS +* 2013 4 3 0 0 0.00000000 +PG01 5783.206741 -18133.044484 -18510.756016 12.734450 +PG02 -22412.401440 13712.162332 528.367722 425.364822 +PG03 10114.112309 -17446.189044 16665.051308 189.049475 +PG04 -24002.325710 4250.313148 -11163.577756 179.333612 +PG05 -15087.153141 8034.886396 20331.626539 -390.251167 +PG06 13855.140409 -11053.269706 19768.346019 289.556712 +PS28 5169.591020 41849.037979 17.421140 0.170452 +PS29 -32240.432088 27155.480094 -12.156456 0.101500 +PS33 -5555.490565 -41739.269117 2093.250932 -0.199099 +PS35 -28749.533445 -30836.454809 -4.729472 -0.008333 +PS37 -34534.904566 24164.610955 29.812840 0.299420 +PS38 -12548.655240 -40249.910397 -3.521920 -0.027787 +* 2013 4 3 0 15 0.00000000 +PG01 0.00000 0.000000 0.000000 0.000000 +* 2013 4 3 23 45 0.00000000 +PG01 4340.761149 -17469.395805 -19521.652181 13.021579 +PG02 -22187.015530 13877.264416 2583.141886 425.527461 +PG03 9785.535610 -18824.396329 15333.698561 189.465625 +PG04 -24642.374460 4816.578416 -9365.337848 180.261632 +PG05 -13667.233808 8977.038381 20922.734874 -390.371011 +PG06 13696.828033 -12657.020030 18869.219517 288.240920 +PS28 5172.029472 41849.075534 17.353884 0.346319 +PS29 -32238.862503 27158.965634 -13.223188 0.179982 +PS33 -5561.856798 -41736.409969 2123.557039 0.009913 +PS35 -28751.361488 -30835.144592 -6.745702 -0.006819 +PS37 -34533.867549 24170.548454 28.300343 -0.223601 +PS38 -12549.851291 -40249.647004 -4.471664 -0.003125 +EOF \ No newline at end of file diff --git a/src/test/resources/sp3/gbm19500_after_drop.sp3 b/src/test/resources/sp3/gbm19500_after_drop.sp3 new file mode 100644 index 0000000000..89cdc68aa6 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_after_drop.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 5 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 5 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 10 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_after_no_drop.sp3 b/src/test/resources/sp3/gbm19500_after_no_drop.sp3 new file mode 100644 index 0000000000..f4e24bcece --- /dev/null +++ b/src/test/resources/sp3/gbm19500_after_no_drop.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_large_gap.sp3 b/src/test/resources/sp3/gbm19500_large_gap.sp3 new file mode 100644 index 0000000000..696e4b2884 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_large_gap.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 20 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 20 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 25 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_truncated.sp3 b/src/test/resources/sp3/gbm19500_truncated.sp3 index f4f0580a79..11d6940972 100644 --- a/src/test/resources/sp3/gbm19500_truncated.sp3 +++ b/src/test/resources/sp3/gbm19500_truncated.sp3 @@ -1,4 +1,4 @@ -#cP2017 5 21 0 0 0.00000000 2 u+U UNDEF FIT GFZ +#dP2017 5 21 0 0 0.00000000 2 u+U UNDEF FIT GFZ ## 1950 0.00000000 300.00000000 57894 0.0000000000000 + 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 + E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 diff --git a/src/test/resources/sp3/gbm19500_wrong_agency.sp3 b/src/test/resources/sp3/gbm19500_wrong_agency.sp3 new file mode 100644 index 0000000000..60591962e0 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_agency.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT IGS +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_coordinate_system.sp3 b/src/test/resources/sp3/gbm19500_wrong_coordinate_system.sp3 new file mode 100644 index 0000000000..e0fd11ad80 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_coordinate_system.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U IGS97 FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_data_used.sp3 b/src/test/resources/sp3/gbm19500_wrong_data_used.sp3 new file mode 100644 index 0000000000..01726ee7de --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_data_used.sp3 @@ -0,0 +1,201 @@ +#cP2017 5 21 0 10 0.00000000 2 v+V UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_derivatives.sp3 b/src/test/resources/sp3/gbm19500_wrong_derivatives.sp3 new file mode 100644 index 0000000000..e4bceb1cca --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_derivatives.sp3 @@ -0,0 +1,375 @@ +#dV2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +VC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +VC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +VC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +VC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +VC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +VC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +VC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +VC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +VC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +VC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +VC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +VC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +VC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +VC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +VE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +VE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +VE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +VE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +VE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +VE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +VE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +VE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +VE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +VE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +VE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +VE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +VE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +VE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +VE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +VE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +VE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +VG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +VG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +VG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +VG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +VG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +VG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +VG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +VG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +VG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +VG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +VG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +VG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +VG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +VG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +VG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +VG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +VG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +VG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +VG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +VG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +VG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +VG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +VG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +VG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +VG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +VG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +VG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +VG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +VG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +VG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +VG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +VG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +VJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +VR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +VR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +VR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +VR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +VR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +VR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +VR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +VR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +VR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +VR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +VR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +VR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +VR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +VR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +VR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +VR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +VR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +VR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +VR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +VR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +VR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +VR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +VR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +VC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +VC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +VC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +VC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +VC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +VC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +VC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +VC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +VC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +VC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +VC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +VC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +VC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +VC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +VE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +VE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +VE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +VE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +VE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +VE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +VE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +VE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +VE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +VE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +VE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +VE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +VE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +VE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +VE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +VE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +VE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +VG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +VG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +VG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +VG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +VG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +VG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +VG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +VG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +VG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +VG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +VG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +VG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +VG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +VG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +VG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +VG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +VG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +VG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +VG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +VG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +VG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +VG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +VG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +VG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +VG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +VG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +VG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +VG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +VG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +VG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +VG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +VG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +VJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +VR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +VR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +VR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +VR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +VR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +VR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +VR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +VR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +VR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +VR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +VR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +VR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +VR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +VR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +VR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +VR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +VR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +VR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +VR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +VR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +VR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +VR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +VR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_frame.sp3 b/src/test/resources/sp3/gbm19500_wrong_frame.sp3 new file mode 100644 index 0000000000..f4e24bcece --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_frame.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_mu.sp3 b/src/test/resources/sp3/gbm19500_wrong_mu.sp3 new file mode 100644 index 0000000000..f4e24bcece --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_mu.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_orbit_type.sp3 b/src/test/resources/sp3/gbm19500_wrong_orbit_type.sp3 new file mode 100644 index 0000000000..333a418649 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_orbit_type.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF EXT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_satellite_count.sp3 b/src/test/resources/sp3/gbm19500_wrong_satellite_count.sp3 new file mode 100644 index 0000000000..0f6c6ab7b3 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_satellite_count.sp3 @@ -0,0 +1,199 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 86 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_satellite_list.sp3 b/src/test/resources/sp3/gbm19500_wrong_satellite_list.sp3 new file mode 100644 index 0000000000..e80665f123 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_satellite_list.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E31 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE31 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE31 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_time_system.sp3 b/src/test/resources/sp3/gbm19500_wrong_time_system.sp3 new file mode 100644 index 0000000000..76a89ef92d --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_time_system.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c M cc TAI ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/gbm19500_wrong_type.sp3 b/src/test/resources/sp3/gbm19500_wrong_type.sp3 new file mode 100644 index 0000000000..5d01eec687 --- /dev/null +++ b/src/test/resources/sp3/gbm19500_wrong_type.sp3 @@ -0,0 +1,201 @@ +#dP2017 5 21 0 10 0.00000000 2 u+U UNDEF FIT GFZ +## 1950 0.00000000 300.00000000 57894 0.0000000000000 ++ 87 C01C02C03C04C05C06C07C08C09C10C11C12C13C14E01E02E03 ++ E04E05E07E08E09E11E12E14E18E19E22E24E26E30G01G02G03 ++ G04G05G06G07G08G09G10G11G12G13G14G15G16G17G18G19G20 ++ G21G22G23G24G25G26G27G28G29G30G31G32J01R01R02R03R04 ++ R05R06R07R08R09R10R11R13R14R15R16R17R18R19R20R21R22 ++ R23R24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +++ 10 10 10 10 10 5 10 8 9 10 6 7 8 6 6 6 5 +++ 9 8 8 8 5 6 5 8 7 9 6 6 5 7 6 7 6 +++ 0 6 5 5 6 6 5 5 8 8 5 7 6 5 5 6 7 +++ 5 7 5 7 7 6 8 5 6 6 6 6 10 8 8 9 7 +++ 10 5 7 7 6 8 7 8 10 7 9 6 7 6 10 6 7 +++ 5 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c L cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* PCV:IGS14_1935 OL/AL:FES2004 NONE YN CLK:CoN ORB:CoN +/* GeoForschungsZentrum Potsdam +/* +/* +* 2017 5 21 0 10 0.00000000 +PC01 -32270.379197 27149.799414 392.736199 24.301529 +PC02 4378.635709 41920.951265 -1039.506334 191.533137 +PC03 -14756.984645 39467.065996 -492.923035 310.108787 +PC04 -39631.314653 14441.396824 -202.083732 -187.684486 +PC05 21843.586276 36030.448964 -1402.224255 -469.668096 +PC06 -23335.767574 33523.540877 9998.291736 -123.003954 +PC07 -8174.925021 34051.278547 23847.612120 71.832912 +PC08 -17233.134567 17737.383428 -34125.659569 -812.776255 +PC09 -11576.258639 33175.097486 23226.361558 -408.512103 +PC10 3160.900202 40371.136327 12413.090696 -17.696106 +PC11 -9837.116285 14957.096741 -21381.709232 172.457623 +PC12 -25964.365630 7024.448846 -7570.144316 240.498612 +PC13 -160.160145 24312.909018 -34428.252371 893.507782 +PC14 -10968.151638 25578.506208 1547.507654 537.835702 +PE01 -11585.860965 14086.072809 23309.427161 -72.847809 +PE02 11586.174911 -14093.724177 -23314.213190 -4.584820 +PE03 22192.543437 -19301.971109 3326.104256 111.352724 +PE04 -9098.647243 24110.497807 14564.607869 188.974525 +PE05 22266.767359 -3179.132081 19254.796817 496.650780 +PE07 -9299.330657 -14783.160646 -23884.371683 233.972932 +PE08 9019.186563 -24167.109551 -14504.518912 6826.959081 +PE09 9273.645419 14677.269081 23989.136102 7122.435589 +PE11 19881.590926 15622.398544 -15411.429032 1731.087117 +PE12 22.394466 16959.832384 -24255.620734 1540.874930 +PE14 17442.112755 -15028.184188 12745.714371 6782.199769 +PE18 -5068.364260 20291.613479 -22558.323274 7094.846658 +PE19 -22668.805003 18338.285797 -5051.304606 10.113304 +PE22 20175.426469 -8891.397487 19744.900985 5302.374285 +PE24 13156.813423 24948.654538 -8970.798488 2172.284357 +PE26 -28999.340753 -5102.375264 -3018.166277 7290.197085 +PE30 -1128.558511 -27819.727208 -10068.615135 6799.901079 +PG01 21935.324397 12692.692472 8291.289757 54.935602 +PG02 -12189.154633 -21072.660044 11504.506398 415.535472 +PG03 14515.138151 4808.985234 21717.745380 -99.438831 +PG04 6661.357077 22431.853437 13457.397276 -18.963099 +PG05 -4202.501017 -23099.782879 -12301.552198 -45.087831 +PG06 2156.919520 -17026.531244 20288.961006 353.573225 +PG07 19122.325655 616.546082 -18429.505121 349.192296 +PG08 16684.676283 8334.590269 -18895.413019 -60.408788 +PG09 25362.219407 -7904.268477 320.902084 367.518660 +PG10 -10754.145296 22908.322416 -7839.318044 -48.437273 +PG11 24112.613730 10893.318794 -2476.101764 -689.520210 +PG12 -11154.256858 -11411.055650 21112.476635 383.173766 +PG13 -8980.554968 -13197.618110 -21336.225646 -82.504501 +PG14 -8503.446232 15975.743033 19655.283733 -71.441400 +PG15 -20456.179929 -5235.252538 -16493.193583 -350.264907 +PG16 1178.377155 23395.240670 -12245.149327 33.616338 +PG17 17301.360194 -14397.870133 14332.127437 -178.668819 +PG18 -14320.792894 15968.669522 -14952.596168 616.439882 +PG19 10638.472305 -15194.700072 18844.785002 -502.896334 +PG20 -14153.073983 -9844.330162 -20350.010104 473.017248 +PG21 -15646.543601 5766.570372 -19916.552636 -505.825733 +PG22 11757.770123 14915.148210 18795.228909 3.604151 +PG23 24415.336730 708.813772 11195.795001 -212.133396 +PG24 -22089.149546 -13899.755198 5494.521380 -35.104958 +PG25 -15527.488889 4941.359564 20774.454654 -390.704643 +PG26 -4219.528953 26105.898928 -1904.234136 -501.994444 +PG27 4432.016063 14253.459742 -22020.707573 261.214876 +PG28 13246.729742 -20499.628171 -9844.577340 614.409037 +PG29 -26005.451233 4299.009511 3113.450134 602.560061 +PG30 10303.460820 -11609.329170 -21552.804723 156.348855 +PG31 -943.077743 17944.581464 19493.156906 199.854424 +PG32 -15840.481748 15672.220066 14436.613495 -419.139897 +PJ01 -20945.324042 32347.632445 -12427.897180 -253.651955 +PR01 16580.003749 -8663.120478 17341.717396 10.677878 +PR02 15160.331946 -20544.492829 20.194926 251.118099 +PR03 4451.281290 -19127.945339 -16256.404499 100.679460 +PR04 -9118.577576 -6120.239676 -23001.453369 216.089552 +PR05 -16861.260866 9852.289250 -16415.228506 -112.554224 +PR06 -14491.996592 20952.154746 920.630895 110.743829 +PR07 -4109.660068 18928.346817 16619.185271 -4.624175 +PR08 8760.871515 6663.348388 23067.537716 -26.848945 +PR09 -4752.767888 14317.740486 20596.786482 18.487276 +PR10 7152.039107 23811.322331 5862.365934 14.100043 +PR11 13584.940264 18251.857839 -11566.402050 -1.668029 +PR13 2918.152750 -16010.798804 -19662.625002 0.419128 +PR14 -8047.089400 -23812.635953 -4127.829921 493.763191 +PR15 -13062.827426 -19882.853336 9337.329521 2.502651 +PR16 -12440.575032 -2239.915642 22146.647734 65.138429 +PR17 -4423.248487 -11110.411308 22542.716783 44.490863 +PR18 -20220.680402 -8436.762378 13012.714297 151.607703 +PR19 -25037.618565 -1271.528615 -4685.261294 -181.240111 +PR20 -14308.857135 7143.120539 -19844.248946 -290.821718 +PR21 4386.637017 11013.567611 -22582.860413 116.696196 +PR22 21229.326016 8003.228160 -11795.995566 237.361819 +PR23 24552.470459 -242.899447 6925.437998 86.875825 +PR24 14255.003398 -7404.226476 19798.454176 -150.812771 +* 2017 5 21 0 15 0.00000000 +PC01 -32269.678709 27150.011137 415.203864 24.314278 +PC02 4378.313564 41920.967453 -1050.797602 191.525212 +PC03 -14757.997924 39466.912826 -474.293734 310.124297 +PC04 -39631.064633 14442.190033 -186.687917 -187.676513 +PC05 21842.788642 36030.359121 -1418.334157 -469.665220 +PC06 -23150.040962 33851.855788 9277.824545 -122.898339 +PC07 -8001.504031 33723.658589 24362.831262 71.829751 +PC08 -17664.795594 17766.010340 -33892.166607 -812.768425 +PC09 -11568.332138 33558.163085 22665.570631 -408.527950 +PC10 3449.221602 40131.425329 13094.617611 -17.701819 +PC11 -10563.648812 15004.962335 -21001.055603 172.454656 +PC12 -26209.997335 7027.759333 -6675.397455 240.495538 +PC13 -535.755091 24174.447356 -34524.804153 893.502802 +PC14 -11023.094027 25592.042168 620.592290 537.853721 +PE01 -11774.113366 13403.743040 23615.235323 -72.850406 +PE02 11774.278410 -13411.794143 -23620.180394 -4.585068 +PE03 22107.628075 -19226.693115 4212.260000 111.351266 +PE04 -9388.362068 24424.136696 13839.325909 188.972589 +PE05 21856.950111 -2759.305465 19781.894180 496.648464 +PE07 -8804.451360 -15302.337217 -23744.640198 233.970985 +PE08 9301.506195 -24486.203334 -13773.130149 6826.956170 +PE09 8784.440114 15203.717544 23844.990764 7122.432671 +PE11 19424.890947 15492.947249 -16108.125696 1731.089626 +PE12 -689.869905 16790.531195 -24362.739873 1540.900215 +PE14 18082.736713 -15117.609135 12110.454176 6782.197674 +PE18 -5587.761400 20335.621006 -22210.526682 7094.837835 +PE19 -22544.931232 18227.601083 -5928.827725 10.113404 +PE22 20749.435505 -8770.778966 19196.597438 5302.439029 +PE24 13273.090479 25182.187778 -8103.444263 2172.403786 +PE26 -29080.487408 -5095.464016 -2114.728938 7290.183934 +PE30 -1080.881584 -27504.644781 -10905.564960 6799.891821 +PG01 22111.925287 12945.713500 7403.072983 54.935741 +PG02 -11813.553206 -20856.380089 12288.948953 415.532949 +PG03 14334.513546 5615.176973 21645.046508 -99.438268 +PG04 6317.131580 22065.407143 14209.876753 -18.963322 +PG05 -4030.063914 -23527.357369 -11512.463892 -45.087037 +PG06 2753.539300 -16527.286720 20627.616210 353.574704 +PG07 19602.605679 1124.702293 -17911.238131 349.190919 +PG08 16003.253174 8600.380274 -19355.932010 -60.408886 +PG09 25361.472183 -7808.160203 1267.936737 367.521030 +PG10 -10768.618835 22580.437946 -8721.297312 -48.434269 +PG11 23978.304520 10993.949650 -3379.304948 -689.520937 +PG12 -10923.329834 -12183.460268 20807.707060 383.173691 +PG13 -8191.157335 -13371.203358 -21547.318703 -82.504643 +PG14 -9217.198978 16106.298627 19210.977960 -71.441767 +PG15 -19896.598939 -5528.397283 -17066.738968 -350.265320 +PG16 996.909284 23808.502735 -11416.798413 33.616333 +PG17 17811.404667 -14482.425618 13579.328549 -178.668344 +PG18 -14306.261336 15343.366674 -15589.476747 616.440242 +PG19 11350.161748 -15316.167703 18339.863435 -502.896023 +PG20 -14028.419183 -10599.065161 -20053.142744 473.017364 +PG21 -16241.024798 5219.425994 -19604.712063 -505.825426 +PG22 11584.608010 15592.667067 18339.595719 3.600522 +PG23 24032.950694 911.979396 11984.409662 -212.133319 +PG24 -22178.731796 -14094.598580 4582.859470 -35.105159 +PG25 -15378.713944 4147.259128 21061.140716 -390.706615 +PG26 -4317.309795 26141.584157 -953.370099 -501.993123 +PG27 3638.382172 14507.977768 -21993.484447 261.216829 +PG28 13179.549559 -20082.670380 -10702.543076 614.409902 +PG29 -25889.272878 4201.675994 4067.243180 602.558962 +PG30 10882.362583 -11024.637812 -21579.282486 156.348556 +PG31 -1509.841659 17452.741938 19912.595623 199.853607 +PG32 -16370.528347 15776.262889 13711.360141 -419.142998 +PJ01 -20751.358886 32187.852020 -12958.035543 -253.644506 +PR01 16140.922188 -8084.571381 18022.648436 10.677939 +PR02 15106.515234 -20557.384930 1090.511720 251.118699 +PR03 4805.715912 -19678.857040 -15482.653584 100.679827 +PR04 -8566.808833 -6889.334254 -22996.484874 216.089918 +PR05 -16442.496857 9304.502530 -17143.359158 -112.555433 +PR06 -14468.224372 20988.473038 -147.125311 110.744357 +PR07 -4471.637866 19487.279436 15859.805575 -4.624505 +PR08 8211.078150 7428.497074 23036.056755 -26.849182 +PR09 -5069.009063 13524.122807 21051.194644 18.487949 +PR10 7106.508664 23544.241810 6897.956348 14.100047 +PR11 13806.409574 18652.914828 -10628.161133 -1.668253 +PR13 3229.572960 -15253.996221 -20208.851071 0.418712 +PR14 -8028.105030 -23612.754491 -5186.300713 493.764558 +PR15 -13265.378336 -20190.068529 8341.872212 2.501948 +PR16 -12829.917914 -3056.842422 21827.862866 65.138836 +PR17 -3499.982851 -10996.236572 22759.258192 44.491522 +PR18 -19675.959623 -8328.085659 13890.976412 151.608042 +PR19 -25215.338815 -1228.169854 -3623.327864 -181.240726 +PR20 -15112.211345 7076.403436 -19262.730103 -290.822128 +PR21 3460.328062 10901.445825 -22797.591007 116.696633 +PR22 20736.110264 7891.057478 -12709.278666 237.361800 +PR23 24821.415216 -276.020037 5889.384587 86.876510 +PR24 15057.742904 -7351.470808 19216.611950 -150.812862 +EOF diff --git a/src/test/resources/sp3/inconsistent-sampling-dates.sp3 b/src/test/resources/sp3/inconsistent-sampling-dates.sp3 new file mode 100644 index 0000000000..190ec7f048 --- /dev/null +++ b/src/test/resources/sp3/inconsistent-sampling-dates.sp3 @@ -0,0 +1,46 @@ +#aP1994 12 17 0 0 0.00000000 3 d ITR92 FIT NGS +## 779 518400.00000000 900.00000000 49703 0.0000000000000 ++ 25 1 2 4 5 6 7 9 12 14 15 16 17 18 19 20 21 22 ++ 23 24 25 26 27 28 29 31 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 6 5 5 5 5 5 5 5 6 5 5 5 5 6 5 5 +++ 5 5 6 5 5 5 5 5 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 1994 12 17 0 0 0.00000000 +P 1 16258.524750 -3529.015750 -20611.427050 -62.540600 +P 2 -21998.652100 -8922.093550 -12229.824050 -131.326200 +P 4 -26019.547600 4809.810900 -2508.578200 3.544600 +P 5 7014.950200 21130.960300 -14387.334650 79.692800 +P 28 13204.937750 -20485.533400 10794.787000 55.200800 +P 29 -1638.431050 -24391.479200 10455.312650 3.690300 +P 31 6265.255800 -25687.986950 -753.359000 70.830800 +* 1994 12 17 0 15 0.00000000 +P 1 15716.820135 -1169.850490 -21281.578766 -62.542746 +P 2 -22813.261065 -9927.616864 -9816.490189 -131.328686 +P 28 13416.746195 -22186.753441 6248.864499 55.385492 +P 29 -2745.269113 -22169.709690 14469.340453 3.718873 +P 31 5629.986510 -25241.323751 -5659.769347 71.118497 +* 1994 12 17 23 46 0.00000000 +P 1 16708.907949 -5150.972262 -19904.291167 -62.727331 +P 2 -21321.617042 -8048.187511 -13856.581227 -131.555527 +P 3 0.000000 0.000000 0.000000 0.000000 +P 4 -26107.382526 5010.736034 -422.963345 3.672587 +P 5 7932.078481 21838.230749 -12767.671968 79.888744 +P 28 13308.321924 -21306.183480 8935.290694 55.387446 +P 29 -2059.774801 -23532.083663 12229.852140 3.719337 +P 31 6034.395625 -25605.621951 -2843.783172 71.121661 +EOF diff --git a/src/test/resources/sp3/issue1014-days-increment.sp3 b/src/test/resources/sp3/issue1014-days-increment.sp3 new file mode 100644 index 0000000000..a1802fcc1a --- /dev/null +++ b/src/test/resources/sp3/issue1014-days-increment.sp3 @@ -0,0 +1,209 @@ +#cV2021 12 30 23 0 0.00000000 62 SLR ITRF14 FIT JCET +## 2190 0.00000000 120.00000000 59574 0.0000000000000 ++ 1 L51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c L cc UTC ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +%/* ilrsb.orb.lageos1.220101.v70.sp3 Reference TRF: SLRF2014 +%/* Input orbits: ASI v70 ESA v70 NSGF v70 GFZ v70 +%/* JCET v70 NSGF v70 +%/* Combination details in README_CC.ilrsb +* 2021 12 30 23 0 0.00000000 +PL51 -995.887942 8952.769585 8401.422583 +VL51 28940.073413 37676.113254 -36820.339352 +* 2021 12 30 23 2 0.00000000 +PL51 -643.432020 9388.193621 7946.901882 +VL51 29779.763092 34873.878274 -38913.826381 +* 2021 12 30 23 4 0.00000000 +PL51 -281.730348 9789.267872 7467.966206 +VL51 30480.291412 31952.957814 -40888.457413 +* 2021 12 30 23 6 0.00000000 +PL51 87.521694 10154.634067 6966.078729 +VL51 31037.518933 28924.413918 -42738.140987 +* 2021 12 30 23 8 0.00000000 +PL51 462.582717 10483.069101 6442.773573 +VL51 31447.919865 25799.704288 -44457.146926 +* 2021 12 30 23 10 0.00000000 +PL51 841.672811 10773.489518 5899.651355 +VL51 31708.602206 22590.639868 -46040.125352 +* 2021 12 30 23 12 0.00000000 +PL51 1222.981240 11024.955484 5338.374525 +VL51 31817.323260 19309.339736 -47482.124152 +* 2021 12 30 23 14 0.00000000 +PL51 1604.674314 11236.674236 4760.662497 +VL51 31772.502120 15968.187713 -48778.603835 +* 2021 12 30 23 16 0.00000000 +PL51 1984.903402 11408.002966 4168.286601 +VL51 31573.230414 12579.785141 -49925.453393 +* 2021 12 30 23 18 0.00000000 +PL51 2361.813048 11538.451154 3563.064849 +VL51 31219.281228 9156.904399 -50919.006117 +* 2021 12 30 23 20 0.00000000 +PL51 2733.549186 11627.682325 2946.856540 +VL51 30711.112850 5712.438954 -51756.049994 +* 2021 12 30 23 22 0.00000000 +PL51 3098.267373 11675.515216 2321.556712 +VL51 30049.871272 2259.355453 -52433.841291 +* 2021 12 30 23 24 0.00000000 +PL51 3454.141053 11681.924348 1689.090469 +VL51 29237.391106 -1189.356367 -52950.115283 +* 2021 12 30 23 26 0.00000000 +PL51 3799.369786 11647.040011 1051.407176 +VL51 28276.189876 -4620.733848 -53303.094431 +* 2021 12 30 23 28 0.00000000 +PL51 4132.187416 11571.147632 410.474559 +VL51 27169.462330 -8021.891568 -53491.497282 +* 2021 12 30 23 30 0.00000000 +PL51 4450.870146 11454.686546 -231.727282 +VL51 25921.072126 -11380.071861 -53514.544078 +* 2021 12 30 23 32 0.00000000 +PL51 4753.744484 11298.248169 -873.211928 +VL51 24535.537190 -14682.694187 -53371.962013 +* 2021 12 30 23 34 0.00000000 +PL51 5039.195013 11102.573562 -1511.992749 +VL51 23018.016118 -17917.406101 -53063.988461 +* 2021 12 30 23 36 0.00000000 +PL51 5305.671966 10868.550413 -2146.089033 +VL51 21374.288958 -21072.130811 -52591.373475 +* 2021 12 30 23 38 0.00000000 +PL51 5551.698567 10597.209431 -2773.532140 +VL51 19610.736082 -24135.115399 -51955.378853 +* 2021 12 30 23 40 0.00000000 +PL51 5775.878085 10289.720174 -3392.371636 +VL51 17734.314842 -27094.979838 -51157.776101 +* 2021 12 30 23 42 0.00000000 +PL51 5976.900603 9947.386315 -4000.681399 +VL51 15752.531740 -29940.759128 -50200.845266 +* 2021 12 30 23 44 0.00000000 +PL51 6153.549437 9571.640368 -4596.565685 +VL51 13673.413831 -32661.951083 -49087.368304 +* 2021 12 30 23 46 0.00000000 +PL51 6304.707192 9164.037904 -5178.165112 +VL51 11505.476784 -35248.556292 -47820.622980 +* 2021 12 30 23 48 0.00000000 +PL51 6429.361420 8726.251254 -5743.662567 +VL51 9257.690007 -37691.120143 -46404.373951 +* 2021 12 30 23 50 0.00000000 +PL51 6526.609847 8260.062752 -6291.288988 +VL51 6939.440750 -39980.770036 -44842.863428 +* 2021 12 30 23 52 0.00000000 +PL51 6595.665161 7767.357525 -6819.329030 +VL51 4560.494743 -42109.252106 -43140.800026 +* 2021 12 30 23 54 0.00000000 +PL51 6635.859306 7250.115863 -7326.126567 +VL51 2130.955962 -44068.964916 -41303.343216 +* 2021 12 30 23 56 0.00000000 +PL51 6646.647292 6710.405209 -7810.090039 +VL51 -338.775923 -45852.989385 -39336.091047 +* 2021 12 30 23 58 0.00000000 +PL51 6627.610483 6150.371787 -8269.697598 +VL51 -2838.049656 -47455.118848 -37245.061824 +* 2021 12 31 0 0 0.00000000 +PL51 6578.459330 5572.231927 -8703.502054 +VL51 -5356.007694 -48869.881161 -35036.676469 +* 2022 1 0 0 2 0.00000000 +PL51 6499.035610 4978.263048 -9110.135595 +VL51 -7881.633197 -50092.564035 -32717.740919 +* 2022 1 0 0 4 0.00000000 +PL51 6389.313975 4370.794537 -9488.314264 +VL51 -10403.797055 -51119.231402 -30295.421935 +* 2022 1 0 0 6 0.00000000 +PL51 6249.403090 3752.198221 -9836.842184 +VL51 -12911.306527 -51946.741074 -27777.228050 +* 2022 1 0 0 8 0.00000000 +PL51 6079.546056 3124.878833 -10154.615507 +VL51 -15392.955312 -52572.754992 -25170.985032 +* 2022 1 0 0 10 0.00000000 +PL51 5880.120298 2491.264279 -10440.626072 +VL51 -17837.571008 -52995.749815 -22484.811249 +* 2022 1 0 0 12 0.00000000 +PL51 5651.636849 1853.795833 -10693.964773 +VL51 -20234.065425 -53215.021228 -19727.092983 +* 2022 1 0 0 14 0.00000000 +PL51 5394.739042 1214.918289 -10913.824607 +VL51 -22571.485176 -53230.687275 -16906.456434 +* 2022 1 0 0 16 0.00000000 +PL51 5110.200613 577.070105 -11099.503400 +VL51 -24839.058396 -53043.685370 -14031.742780 +* 2022 1 0 0 18 0.00000000 +PL51 4798.923213 -57.326406 -11250.406204 +VL51 -27026.245434 -52655.768332 -11111.976379 +* 2022 1 0 0 20 0.00000000 +PL51 4461.933344 -685.874819 -11366.047342 +VL51 -29122.785400 -52069.495771 -8156.340167 +* 2022 1 0 0 22 0.00000000 +PL51 4100.378730 -1306.214201 -11446.052111 +VL51 -31118.743379 -51288.221198 -5174.142159 +* 2022 1 0 0 24 0.00000000 +PL51 3715.524129 -1916.028568 -11490.158121 +VL51 -33004.556200 -50316.079213 -2174.787784 +* 2022 1 0 0 26 0.00000000 +PL51 3308.746605 -2513.056125 -11498.216274 +VL51 -34771.076286 -49157.964800 832.251491 +* 2022 1 0 0 28 0.00000000 +PL51 2881.530281 -3095.098281 -11470.191371 +VL51 -36409.615094 -47819.511803 3837.469031 +* 2022 1 0 0 30 0.00000000 +PL51 2435.460590 -3660.028365 -11406.162350 +VL51 -37911.983031 -46307.068944 6831.353980 +* 2022 1 0 0 32 0.00000000 +PL51 1972.218046 -4205.800037 -11306.322147 +VL51 -39270.529034 -44627.671140 9804.422736 +* 2022 1 0 0 34 0.00000000 +PL51 1493.571560 -4730.455341 -11170.977180 +VL51 -40478.177027 -42789.008837 12747.249975 +* 2022 1 0 0 36 0.00000000 +PL51 1001.371336 -5232.132366 -11000.546468 +VL51 -41528.460131 -40799.393133 15650.500125 +* 2022 1 0 0 38 0.00000000 +PL51 497.541361 -5709.072488 -10795.560360 +VL51 -42415.552501 -38667.721094 18504.958709 +* 2022 1 0 0 40 0.00000000 +PL51 -15.928459 -6159.627158 -10556.658903 +VL51 -43134.299024 -36403.435046 21301.561863 +* 2022 1 0 0 42 0.00000000 +PL51 -536.990503 -6582.264208 -10284.589845 +VL51 -43680.239430 -34016.481939 24031.428319 +* 2022 1 0 0 44 0.00000000 +PL51 -1063.547935 -6975.573639 -9980.206270 +VL51 -44049.634388 -31517.269252 26685.888980 +* 2022 1 0 0 46 0.00000000 +PL51 -1593.463453 -7338.272885 -9644.463879 +VL51 -44239.483174 -28916.620213 29256.514741 +* 2022 1 0 0 48 0.00000000 +PL51 -2124.568273 -7669.211507 -9278.417928 +VL51 -44247.541142 -26225.725144 31735.146146 +* 2022 1 0 0 50 0.00000000 +PL51 -2654.671281 -7967.375314 -8883.219830 +VL51 -44072.333295 -23456.093072 34113.921802 +* 2022 1 0 0 52 0.00000000 +PL51 -3181.568331 -8231.889880 -8460.113424 +VL51 -43713.164193 -20619.501084 36385.302809 +* 2022 1 0 0 54 0.00000000 +PL51 -3703.051639 -8462.023460 -8010.430940 +VL51 -43170.123510 -17727.942921 38542.100126 +* 2022 1 0 0 56 0.00000000 +PL51 -4216.919230 -8657.189266 -7535.588659 +VL51 -42444.090194 -14793.575967 40577.498795 +* 2022 1 0 0 58 0.00000000 +PL51 -4720.984398 -8816.947119 -7037.082289 +VL51 -41536.729872 -11828.668803 42485.079283 +* 2022 1 0 0 60 0.00000000 +PL51 -5213.085134 -8941.004460 -6516.482075 +VL51 -40450.491163 -8845.546767 44258.841883 +* 2022 1 0 1 2 0.00000000 +PL51 -5691.093473 -9029.216710 -5975.427658 +VL51 -39188.598237 -5856.539265 45893.223756 +EOF diff --git a/src/test/resources/sp3/issue895-second-digits.sp3 b/src/test/resources/sp3/issue895-second-digits.sp3 index 0baa29cba3..7dbff4bcd1 100644 --- a/src/test/resources/sp3/issue895-second-digits.sp3 +++ b/src/test/resources/sp3/issue895-second-digits.sp3 @@ -1,4 +1,4 @@ -#cV2016 7 3 0 0 0.00000000 1 SLR SLR08 FIT GFZ +#cV2016 7 3 0 0 0.12340000 1 SLR SLR08 FIT GFZ ## 1904 0.00000000 120.00000000 57572 0.0000000000000 + 1 L51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/src/test/resources/sp3/too-long-comment-a.sp3 b/src/test/resources/sp3/too-long-comment-a.sp3 new file mode 100644 index 0000000000..ecabea0226 --- /dev/null +++ b/src/test/resources/sp3/too-long-comment-a.sp3 @@ -0,0 +1,46 @@ +#aP1994 12 17 0 0 0.00000000 3 d ITR92 FIT NGS +## 779 518400.00000000 900.00000000 49703 0.0000000000000 ++ 25 1 2 4 5 6 7 9 12 14 15 16 17 18 19 20 21 22 ++ 23 24 25 26 27 28 29 31 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 6 5 5 5 5 5 5 5 6 5 5 5 5 6 5 5 +++ 5 5 6 5 5 5 5 5 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 1994 12 17 0 0 0.00000000 +P 1 16258.524750 -3529.015750 -20611.427050 -62.540600 +P 2 -21998.652100 -8922.093550 -12229.824050 -131.326200 +P 4 -26019.547600 4809.810900 -2508.578200 3.544600 +P 5 7014.950200 21130.960300 -14387.334650 79.692800 +P 28 13204.937750 -20485.533400 10794.787000 55.200800 +P 29 -1638.431050 -24391.479200 10455.312650 3.690300 +P 31 6265.255800 -25687.986950 -753.359000 70.830800 +* 1994 12 17 0 15 0.00000000 +P 1 15716.820135 -1169.850490 -21281.578766 -62.542746 +P 2 -22813.261065 -9927.616864 -9816.490189 -131.328686 +P 28 13416.746195 -22186.753441 6248.864499 55.385492 +P 29 -2745.269113 -22169.709690 14469.340453 3.718873 +P 31 5629.986510 -25241.323751 -5659.769347 71.118497 +* 1994 12 17 23 45 0.00000000 +P 1 16708.907949 -5150.972262 -19904.291167 -62.727331 +P 2 -21321.617042 -8048.187511 -13856.581227 -131.555527 +P 3 0.000000 0.000000 0.000000 0.000000 +P 4 -26107.382526 5010.736034 -422.963345 3.672587 +P 5 7932.078481 21838.230749 -12767.671968 79.888744 +P 28 13308.321924 -21306.183480 8935.290694 55.387446 +P 29 -2059.774801 -23532.083663 12229.852140 3.719337 +P 31 6034.395625 -25605.621951 -2843.783172 71.121661 +EOF diff --git a/src/test/resources/sp3/too-long-comment-d.sp3 b/src/test/resources/sp3/too-long-comment-d.sp3 new file mode 100644 index 0000000000..8a1986f13f --- /dev/null +++ b/src/test/resources/sp3/too-long-comment-d.sp3 @@ -0,0 +1,25 @@ +#dP2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX +## 1126 259200.00000000 900.00000000 52129 0.0000000000000 ++ 26 I01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 ++ G23G24G25G26G27G28G29G30G31 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 8 7 8 6 7 7 7 7 7 7 7 7 8 8 7 9 +++ 9 8 6 8 7 7 6 7 7 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c I cc UTC ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* AN EXAMPLE ULTRA RAPID ORBIT, IRNSS ONLY. +/* NOTE THE “PREDICTED DATA” FLAGS FOR THE LAST EPOCH (IN COLUMNS 76 and 80). +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 2001 8 8 0 0 0.00000000 +PI01 -11044.805800 -10475.672350 21929.418200 189.163300 18 18 18 219 +EOF diff --git a/src/test/resources/sp3/too-many-comments-a.sp3 b/src/test/resources/sp3/too-many-comments-a.sp3 new file mode 100644 index 0000000000..05816910d2 --- /dev/null +++ b/src/test/resources/sp3/too-many-comments-a.sp3 @@ -0,0 +1,47 @@ +#aP1994 12 17 0 0 0.00000000 3 d ITR92 FIT NGS +## 779 518400.00000000 900.00000000 49703 0.0000000000000 ++ 25 1 2 4 5 6 7 9 12 14 15 16 17 18 19 20 21 22 ++ 23 24 25 26 27 28 29 31 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 6 5 5 5 5 5 5 5 6 5 5 5 5 6 5 5 +++ 5 5 6 5 5 5 5 5 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 1994 12 17 0 0 0.00000000 +P 1 16258.524750 -3529.015750 -20611.427050 -62.540600 +P 2 -21998.652100 -8922.093550 -12229.824050 -131.326200 +P 4 -26019.547600 4809.810900 -2508.578200 3.544600 +P 5 7014.950200 21130.960300 -14387.334650 79.692800 +P 28 13204.937750 -20485.533400 10794.787000 55.200800 +P 29 -1638.431050 -24391.479200 10455.312650 3.690300 +P 31 6265.255800 -25687.986950 -753.359000 70.830800 +* 1994 12 17 0 15 0.00000000 +P 1 15716.820135 -1169.850490 -21281.578766 -62.542746 +P 2 -22813.261065 -9927.616864 -9816.490189 -131.328686 +P 28 13416.746195 -22186.753441 6248.864499 55.385492 +P 29 -2745.269113 -22169.709690 14469.340453 3.718873 +P 31 5629.986510 -25241.323751 -5659.769347 71.118497 +* 1994 12 17 23 45 0.00000000 +P 1 16708.907949 -5150.972262 -19904.291167 -62.727331 +P 2 -21321.617042 -8048.187511 -13856.581227 -131.555527 +P 3 0.000000 0.000000 0.000000 0.000000 +P 4 -26107.382526 5010.736034 -422.963345 3.672587 +P 5 7932.078481 21838.230749 -12767.671968 79.888744 +P 28 13308.321924 -21306.183480 8935.290694 55.387446 +P 29 -2059.774801 -23532.083663 12229.852140 3.719337 +P 31 6034.395625 -25605.621951 -2843.783172 71.121661 +EOF diff --git a/src/test/resources/sp3/unsupported-version.sp3 b/src/test/resources/sp3/unsupported-version.sp3 index 7c3f919d31..2281ed93b1 100644 --- a/src/test/resources/sp3/unsupported-version.sp3 +++ b/src/test/resources/sp3/unsupported-version.sp3 @@ -10,7 +10,7 @@ ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -@@ G cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c G cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc %c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc %f 1.2500000 1.025000000 0.00000000000 0.000000000000000 %f 0.0000000 0.000000000 0.00000000000 0.000000000000000 diff --git a/src/test/resources/sp3/wrong-clock-base-a.sp3 b/src/test/resources/sp3/wrong-clock-base-a.sp3 new file mode 100644 index 0000000000..c65ba5ebfa --- /dev/null +++ b/src/test/resources/sp3/wrong-clock-base-a.sp3 @@ -0,0 +1,46 @@ +#aP1994 12 17 0 0 0.00000000 3 d ITR92 FIT NGS +## 779 518400.00000000 900.00000000 49703 0.0000000000000 ++ 25 1 2 4 5 6 7 9 12 14 15 16 17 18 19 20 21 22 ++ 23 24 25 26 27 28 29 31 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 6 5 5 5 5 5 5 5 6 5 5 5 5 6 5 5 +++ 5 5 6 5 5 5 5 5 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 0.0000000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 1994 12 17 0 0 0.00000000 +P 1 16258.524750 -3529.015750 -20611.427050 -62.540600 +P 2 -21998.652100 -8922.093550 -12229.824050 -131.326200 +P 4 -26019.547600 4809.810900 -2508.578200 3.544600 +P 5 7014.950200 21130.960300 -14387.334650 79.692800 +P 28 13204.937750 -20485.533400 10794.787000 55.200800 +P 29 -1638.431050 -24391.479200 10455.312650 3.690300 +P 31 6265.255800 -25687.986950 -753.359000 70.830800 +* 1994 12 17 0 15 0.00000000 +P 1 15716.820135 -1169.850490 -21281.578766 -62.542746 +P 2 -22813.261065 -9927.616864 -9816.490189 -131.328686 +P 28 13416.746195 -22186.753441 6248.864499 55.385492 +P 29 -2745.269113 -22169.709690 14469.340453 3.718873 +P 31 5629.986510 -25241.323751 -5659.769347 71.118497 +* 1994 12 17 23 45 0.00000000 +P 1 16708.907949 -5150.972262 -19904.291167 -62.727331 +P 2 -21321.617042 -8048.187511 -13856.581227 -131.555527 +P 3 0.000000 0.000000 0.000000 0.000000 +P 4 -26107.382526 5010.736034 -422.963345 3.672587 +P 5 7932.078481 21838.230749 -12767.671968 79.888744 +P 28 13308.321924 -21306.183480 8935.290694 55.387446 +P 29 -2059.774801 -23532.083663 12229.852140 3.719337 +P 31 6034.395625 -25605.621951 -2843.783172 71.121661 +EOF diff --git a/src/test/resources/sp3/wrong-clock-base-d.sp3 b/src/test/resources/sp3/wrong-clock-base-d.sp3 new file mode 100644 index 0000000000..de7a1562fb --- /dev/null +++ b/src/test/resources/sp3/wrong-clock-base-d.sp3 @@ -0,0 +1,25 @@ +#dP2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX +## 1126 259200.00000000 900.00000000 52129 0.0000000000000 ++ 26 I01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 ++ G23G24G25G26G27G28G29G30G31 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 8 7 8 6 7 7 7 7 7 7 7 7 8 8 7 9 +++ 9 8 6 8 7 7 6 7 7 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c I cc UTC ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 0.000000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* AN EXAMPLE ULTRA RAPID ORBIT, IRNSS ONLY. +/* NOTE THE “PREDICTED DATA” FLAGS FOR THE LAST EPOCH (IN COLUMNS 76 and 80). +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 2001 8 8 0 0 0.00000000 +PI01 -11044.805800 -10475.672350 21929.418200 189.163300 18 18 18 219 +EOF diff --git a/src/test/resources/sp3/wrong-pos-vel-base-a.sp3 b/src/test/resources/sp3/wrong-pos-vel-base-a.sp3 new file mode 100644 index 0000000000..79aa9032cc --- /dev/null +++ b/src/test/resources/sp3/wrong-pos-vel-base-a.sp3 @@ -0,0 +1,46 @@ +#aP1994 12 17 0 0 0.00000000 3 d ITR92 FIT NGS +## 779 518400.00000000 900.00000000 49703 0.0000000000000 ++ 25 1 2 4 5 6 7 9 12 14 15 16 17 18 19 20 21 22 ++ 23 24 25 26 27 28 29 31 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 6 5 5 5 5 5 5 5 6 5 5 5 5 6 5 5 +++ 5 5 6 5 5 5 5 5 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 1.2500000 0.000000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 1994 12 17 0 0 0.00000000 +P 1 16258.524750 -3529.015750 -20611.427050 -62.540600 +P 2 -21998.652100 -8922.093550 -12229.824050 -131.326200 +P 4 -26019.547600 4809.810900 -2508.578200 3.544600 +P 5 7014.950200 21130.960300 -14387.334650 79.692800 +P 28 13204.937750 -20485.533400 10794.787000 55.200800 +P 29 -1638.431050 -24391.479200 10455.312650 3.690300 +P 31 6265.255800 -25687.986950 -753.359000 70.830800 +* 1994 12 17 0 15 0.00000000 +P 1 15716.820135 -1169.850490 -21281.578766 -62.542746 +P 2 -22813.261065 -9927.616864 -9816.490189 -131.328686 +P 28 13416.746195 -22186.753441 6248.864499 55.385492 +P 29 -2745.269113 -22169.709690 14469.340453 3.718873 +P 31 5629.986510 -25241.323751 -5659.769347 71.118497 +* 1994 12 17 23 45 0.00000000 +P 1 16708.907949 -5150.972262 -19904.291167 -62.727331 +P 2 -21321.617042 -8048.187511 -13856.581227 -131.555527 +P 3 0.000000 0.000000 0.000000 0.000000 +P 4 -26107.382526 5010.736034 -422.963345 3.672587 +P 5 7932.078481 21838.230749 -12767.671968 79.888744 +P 28 13308.321924 -21306.183480 8935.290694 55.387446 +P 29 -2059.774801 -23532.083663 12229.852140 3.719337 +P 31 6034.395625 -25605.621951 -2843.783172 71.121661 +EOF diff --git a/src/test/resources/sp3/wrong-pos-vel-base-d.sp3 b/src/test/resources/sp3/wrong-pos-vel-base-d.sp3 new file mode 100644 index 0000000000..9308ad6002 --- /dev/null +++ b/src/test/resources/sp3/wrong-pos-vel-base-d.sp3 @@ -0,0 +1,25 @@ +#dP2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM MGEX +## 1126 259200.00000000 900.00000000 52129 0.0000000000000 ++ 26 I01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 ++ G23G24G25G26G27G28G29G30G31 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 7 8 7 8 6 7 7 7 7 7 7 7 7 8 8 7 9 +++ 9 8 6 8 7 7 6 7 7 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +++ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +%c I cc UTC ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc +%f 0.0000000 1.025000000 0.00000000000 0.000000000000000 +%f 0.0000000 0.000000000 0.00000000000 0.000000000000000 +%i 0 0 0 0 0 0 0 0 0 +%i 0 0 0 0 0 0 0 0 0 +/* AN EXAMPLE ULTRA RAPID ORBIT, IRNSS ONLY. +/* NOTE THE “PREDICTED DATA” FLAGS FOR THE LAST EPOCH (IN COLUMNS 76 and 80). +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +* 2001 8 8 0 0 0.00000000 +PI01 -11044.805800 -10475.672350 21929.418200 189.163300 18 18 18 219 +EOF diff --git a/src/test/resources/stk/stk_02674_p.e b/src/test/resources/stk/stk_02674_p.e new file mode 100644 index 0000000000..30a6c90bdd --- /dev/null +++ b/src/test/resources/stk/stk_02674_p.e @@ -0,0 +1,40 @@ +stk.v.12.0 + +# WrittenBy STK_v12.2.0 + +BEGIN Ephemeris + + NumberOfEphemerisPoints 11 + + ScenarioEpoch 12 Jan 2007 00:00:00.000883 + +# Epoch in JDate format: 2454112.50000001021991 +# Epoch in YYDDD format: 07012.00000001021991 + + + InterpolationMethod Lagrange + + InterpolationSamplesM1 5 + + CentralBody Earth + + CoordinateSystem J2000 + +# Time of first point: 12 Jan 2007 00:00:00.000883000 UTCG = 2454112.50000001021991 JDate = 07012.00000001021991 YYDDD + + EphemerisTimePos + + 0.0000000000000000e+00 -4.2001828159554983e+06 -3.9105939267270239e+06 -4.5819301444368772e+06 + 6.0000000000000000e+01 -3.8641543651398388e+06 -4.1811384442211185e+06 -4.6384904619383067e+06 + 1.2000000000000000e+02 -3.5141462265661480e+06 -4.4365570058648754e+06 -4.6782248721729051e+06 + 1.8000000000000000e+02 -3.1514214711790215e+06 -4.6759211491462803e+06 -4.7009847013899973e+06 + 2.4000000000000000e+02 -2.7772858927748781e+06 -4.8983541175281964e+06 -4.7066768626843216e+06 + 3.0000000000000000e+02 -2.3930847437297828e+06 -5.1030332553920103e+06 -4.6952642374704480e+06 + 3.6000000000000000e+02 -2.0001994009168909e+06 -5.2891923627324430e+06 -4.6667659975418849e+06 + 4.2000000000000000e+02 -1.6000439503726398e+06 -5.4561240129014822e+06 -4.6212578653350323e+06 + 4.8000000000000000e+02 -1.1940616786186690e+06 -5.6031818349705962e+06 -4.5588723084814874e+06 + 5.4000000000000000e+02 -7.8372145812834124e+05 -5.7297827604123382e+06 -4.4797986631915309e+06 + 6.0000000000000000e+02 -3.7051401425713405e+05 -5.8354092318012062e+06 -4.3842831794793038e+06 + + +END Ephemeris diff --git a/src/test/resources/stk/stk_02674_pv.e b/src/test/resources/stk/stk_02674_pv.e new file mode 100644 index 0000000000..266bfd1520 --- /dev/null +++ b/src/test/resources/stk/stk_02674_pv.e @@ -0,0 +1,40 @@ +stk.v.12.0 + +# WrittenBy STK_v12.2.0 + +BEGIN Ephemeris + + NumberOfEphemerisPoints 11 + + ScenarioEpoch 12 Jan 2007 00:00:00.000883 + +# Epoch in JDate format: 2454112.50000001021991 +# Epoch in YYDDD format: 07012.00000001021991 + + + InterpolationMethod Lagrange + + InterpolationSamplesM1 5 + + CentralBody Earth + + CoordinateSystem J2000 + +# Time of first point: 12 Jan 2007 00:00:00.000883000 UTCG = 2454112.50000001021991 JDate = 07012.00000001021991 YYDDD + + EphemerisTimePosVel + + 0.0000000000000000e+00 -4.2001828159554983e+06 -3.9105939267270239e+06 -4.5819301444368772e+06 5.4770282903204152e+03 -4.6296785954320931e+03 -1.0817325337227874e+03 + 6.0000000000000000e+01 -3.8641543651398388e+06 -4.1811384442211185e+06 -4.6384904619383067e+06 5.7202901814599727e+03 -4.3855912442681747e+03 -8.0287100854531127e+02 + 1.2000000000000000e+02 -3.5141462265661480e+06 -4.4365570058648754e+06 -4.6782248721729051e+06 5.9428768124875878e+03 -4.1256141594350274e+03 -5.2107176536950249e+02 + 1.8000000000000000e+02 -3.1514214711790215e+06 -4.6759211491462803e+06 -4.7009847013899973e+06 6.1440495799890250e+03 -3.8505887646286237e+03 -2.3725719698217702e+02 + 2.4000000000000000e+02 -2.7772858927748781e+06 -4.8983541175281964e+06 -4.7066768626843216e+06 6.3231236746481936e+03 -3.5613966825084581e+03 4.7643476529094748e+01 + 3.0000000000000000e+02 -2.3930847437297828e+06 -5.1030332553920103e+06 -4.6952642374704480e+06 6.4794691416813621e+03 -3.2589590411954355e+03 3.3269516839203544e+02 + 3.6000000000000000e+02 -2.0001994009168909e+06 -5.2891923627324430e+06 -4.6667659975418849e+06 6.6125121299598577e+03 -2.9442358523333974e+03 6.1695791256223322e+02 + 4.2000000000000000e+02 -1.6000439503726398e+06 -5.4561240129014822e+06 -4.6212578653350323e+06 6.7217363429989700e+03 -2.6182254322686313e+03 8.9948790418355736e+02 + 4.8000000000000000e+02 -1.1940616786186690e+06 -5.6031818349705962e+06 -4.5588723084814874e+06 6.8066847009913317e+03 -2.2819638354053445e+03 1.1793386194932716e+03 + 5.4000000000000000e+02 -7.8372145812834124e+05 -5.7297827604123382e+06 -4.4797986631915309e+06 6.8669612187982284e+03 -1.9365242664470932e+03 1.4555620414920620e+03 + 6.0000000000000000e+02 -3.7051401425713405e+05 -5.8354092318012062e+06 -4.3842831794793038e+06 6.9022331002597584e+03 -1.5830164360578574e+03 1.7272100173740068e+03 + + +END Ephemeris diff --git a/src/test/resources/stk/stk_02674_pva.e b/src/test/resources/stk/stk_02674_pva.e new file mode 100644 index 0000000000..66cab66933 --- /dev/null +++ b/src/test/resources/stk/stk_02674_pva.e @@ -0,0 +1,40 @@ +stk.v.12.0 + +# WrittenBy STK_v12.2.0 + +BEGIN Ephemeris + + NumberOfEphemerisPoints 11 + + ScenarioEpoch 12 Jan 2007 00:00:00.000883 + +# Epoch in JDate format: 2454112.50000001021991 +# Epoch in YYDDD format: 07012.00000001021991 + + + InterpolationMethod Lagrange + + InterpolationSamplesM1 5 + + CentralBody Earth + + CoordinateSystem J2000 + +# Time of first point: 12 Jan 2007 00:00:00.000883000 UTCG = 2454112.50000001021991 JDate = 07012.00000001021991 YYDDD + + EphemerisTimePosVelAcc + + 0.0000000000000000e+00 -4.2001828159554983e+06 -3.9105939267270239e+06 -4.5819301444368772e+06 5.4770282903204152e+03 -4.6296785954320931e+03 -1.0817325337227874e+03 4.2195714111001097e+00 3.9335215635670262e+00 4.6186527996456137e+00 + 6.0000000000000000e+01 -3.8641543651398388e+06 -4.1811384442211185e+06 -4.6384904619383067e+06 5.7202901814599727e+03 -4.3855912442681747e+03 -8.0287100854531127e+02 3.8813238897796021e+00 4.2050245819749197e+00 4.6751324119553370e+00 + 1.2000000000000000e+02 -3.5141462265661480e+06 -4.4365570058648754e+06 -4.6782248721729051e+06 5.9428768124875878e+03 -4.1256141594350274e+03 -5.2107176536950249e+02 3.5303305336928767e+00 4.4628374109037416e+00 4.7162999364865614e+00 + 1.8000000000000000e+02 -3.1514214711790215e+06 -4.6759211491462803e+06 -4.7009847013899973e+06 6.1440495799890250e+03 -3.8505887646286237e+03 -2.3725719698217702e+02 3.1674801781812176e+00 4.7062838904765441e+00 4.7420337532252930e+00 + 2.4000000000000000e+02 -2.7772858927748781e+06 -4.8983541175281964e+06 -4.7066768626843216e+06 6.3231236746481936e+03 -3.5613966825084581e+03 4.7643476529094748e+01 2.7936778731411600e+00 4.9347001492926781e+00 4.7522281739502823e+00 + 3.0000000000000000e+02 -2.3930847437297828e+06 -5.1030332553920103e+06 -4.6952642374704480e+06 6.4794691416813621e+03 -3.2589590411954355e+03 3.3269516839203544e+02 2.4098478990321057e+00 5.1474331925129251e+00 4.7467936751027020e+00 + 3.6000000000000000e+02 -2.0001994009168909e+06 -5.2891923627324430e+06 -4.6667659975418849e+06 6.6125121299598577e+03 -2.9442358523333974e+03 6.1695791256223322e+02 2.0169370341439179e+00 5.3438399430653583e+00 4.7256575662492413e+00 + 4.2000000000000000e+02 -1.6000439503726398e+06 -5.4561240129014822e+06 -4.6212578653350323e+06 6.7217363429989700e+03 -2.6182254322686313e+03 8.9948790418355736e+02 1.6159180082757245e+00 5.5232867784407000e+00 4.6887650976960371e+00 + 4.8000000000000000e+02 -1.1940616786186690e+06 -5.6031818349705962e+06 -4.5588723084814874e+06 6.8066847009913317e+03 -2.2819638354053445e+03 1.1793386194932716e+03 1.2077930739920737e+00 5.6851496034914462e+00 4.6360810081129742e+00 + 5.4000000000000000e+02 -7.8372145812834124e+05 -5.7297827604123382e+06 -4.4797986631915309e+06 6.8669612187982284e+03 -1.9365242664470932e+03 1.4555620414920620e+03 7.9359762202175610e-01 5.8288144970348430e+00 4.5675915088373591e+00 + 6.0000000000000000e+02 -3.7051401425713405e+05 -5.8354092318012062e+06 -4.3842831794793038e+06 6.9022331002597584e+03 -1.5830164360578574e+03 1.7272100173740068e+03 3.7440376244610168e-01 5.9536789668958612e+00 4.4833066969906747e+00 + + +END Ephemeris diff --git a/src/test/resources/stk/stk_impulsive_maneuver.e b/src/test/resources/stk/stk_impulsive_maneuver.e new file mode 100644 index 0000000000..e74f9b84d6 --- /dev/null +++ b/src/test/resources/stk/stk_impulsive_maneuver.e @@ -0,0 +1,49 @@ +stk.v.12.0 + +# WrittenBy STK_v12.2.0 + +BEGIN Ephemeris + + NumberOfEphemerisPoints 12 + + ScenarioEpoch 12 Jan 2007 00:00:00.000883 + +# Epoch in JDate format: 2454112.50000001021991 +# Epoch in YYDDD format: 07012.00000001021991 + + + InterpolationMethod Lagrange + + InterpolationSamplesM1 4 + + CentralBody Earth + + CoordinateSystem J2000 + + BEGIN SegmentBoundaryTimes + + 0.0000000000000000e+00 + 3.0000000000000000e+02 + + END SegmentBoundaryTimes + +# Time of first point: 12 Jan 2007 00:00:00.000883000 UTCG = 2454112.50000001021991 JDate = 07012.00000001021991 YYDDD + + EphemerisTimePosVel + + 0.0000000000000000e+00 6.9999999999999553e+06 -1.2559721622877134e-03 -7.9186900106575397e-01 4.1824064674153972e-04 6.7895303333415395e+03 3.6864141134955967e+03 + 6.0000000000000000e+01 6.9853436721346313e+06 4.0708747687269317e+05 2.2102922871869511e+05 -4.8834963369038530e+02 6.7753160807054328e+03 3.6786747792111828e+03 + 1.3748500000000001e+02 6.9231759864317747e+06 9.3004192989999522e+05 5.0496564240522223e+05 -1.1152214926345828e+03 6.7150552999451957e+03 3.6458668940461002e+03 + 2.1502100000000002e+02 6.8126586995684709e+06 1.4468512041141947e+06 7.8555597291407420e+05 -1.7336220648769806e+03 6.6080543954969553e+03 3.5876138947106388e+03 + 2.9264700000000005e+02 6.6544848701341432e+06 1.9541667366877662e+06 1.0609769465757729e+06 -2.3389621011230829e+03 6.4551939845530078e+03 3.5043965884426007e+03 + 3.0000000000000000e+02 6.6370788283674866e+06 2.0015702515387577e+06 1.0867112801511162e+06 -2.3954153910685013e+03 6.4383927907853858e+03 3.4952501591096920e+03 + + 3.0000000000000000e+02 6.6370788283674866e+06 2.0015702515387577e+06 1.0867112801511162e+06 -2.4109546214707530e+03 6.4801591037886537e+03 3.5179240960554193e+03 + 3.6000000000000000e+02 6.4787418082331438e+06 2.3859551936109182e+06 1.2953780350291547e+06 -2.8648687835270284e+03 6.3283234273332882e+03 3.4352673416780117e+03 + 4.3768600000000004e+02 6.2339832433031155e+06 2.8687399803035557e+06 1.5574394502919207e+06 -3.4322131086904078e+03 6.0938398775676169e+03 3.3076231040054954e+03 + 5.1560300000000007e+02 5.9452530367898876e+06 3.3330604841317255e+06 1.8094476192010646e+06 -3.9741434186193869e+03 5.8179126787287878e+03 3.1574256359458673e+03 + 5.9381300000000010e+02 5.6141712623782167e+06 3.7759920490746046e+06 2.0498105646133840e+06 -4.4867950488299975e+03 5.5026682555512361e+03 2.9858359539013713e+03 + 6.0000000000000000e+02 5.5862903748328788e+06 3.8099552373593030e+06 2.0682393977866683e+06 -4.5259006372314416e+03 5.4761825144401137e+03 2.9714200584292998e+03 + + +END Ephemeris diff --git a/src/test/resources/stk/stk_invalid_coordinate_system.e b/src/test/resources/stk/stk_invalid_coordinate_system.e new file mode 100644 index 0000000000..e83d1dcc0a --- /dev/null +++ b/src/test/resources/stk/stk_invalid_coordinate_system.e @@ -0,0 +1,40 @@ +stk.v.12.0 + +# WrittenBy STK_v12.2.0 + +BEGIN Ephemeris + + NumberOfEphemerisPoints 11 + + ScenarioEpoch 12 Jan 2007 00:00:00.000883 + +# Epoch in JDate format: 2454112.50000001021991 +# Epoch in YYDDD format: 07012.00000001021991 + + + InterpolationMethod Lagrange + + InterpolationSamplesM1 5 + + CentralBody Earth + + CoordinateSystem ASDF + +# Time of first point: 12 Jan 2007 00:00:00.000883000 UTCG = 2454112.50000001021991 JDate = 07012.00000001021991 YYDDD + + EphemerisTimePos + + 0.0000000000000000e+00 -4.2001828159554983e+06 -3.9105939267270239e+06 -4.5819301444368772e+06 + 6.0000000000000000e+01 -3.8641543651398388e+06 -4.1811384442211185e+06 -4.6384904619383067e+06 + 1.2000000000000000e+02 -3.5141462265661480e+06 -4.4365570058648754e+06 -4.6782248721729051e+06 + 1.8000000000000000e+02 -3.1514214711790215e+06 -4.6759211491462803e+06 -4.7009847013899973e+06 + 2.4000000000000000e+02 -2.7772858927748781e+06 -4.8983541175281964e+06 -4.7066768626843216e+06 + 3.0000000000000000e+02 -2.3930847437297828e+06 -5.1030332553920103e+06 -4.6952642374704480e+06 + 3.6000000000000000e+02 -2.0001994009168909e+06 -5.2891923627324430e+06 -4.6667659975418849e+06 + 4.2000000000000000e+02 -1.6000439503726398e+06 -5.4561240129014822e+06 -4.6212578653350323e+06 + 4.8000000000000000e+02 -1.1940616786186690e+06 -5.6031818349705962e+06 -4.5588723084814874e+06 + 5.4000000000000000e+02 -7.8372145812834124e+05 -5.7297827604123382e+06 -4.4797986631915309e+06 + 6.0000000000000000e+02 -3.7051401425713405e+05 -5.8354092318012062e+06 -4.3842831794793038e+06 + + +END Ephemeris diff --git a/src/test/resources/zero-EOP/UTC-TAI.history b/src/test/resources/zero-EOP/UTC-TAI.history index 394bde0be4..6103c31ce2 100644 --- a/src/test/resources/zero-EOP/UTC-TAI.history +++ b/src/test/resources/zero-EOP/UTC-TAI.history @@ -45,5 +45,6 @@ 2006 Jan. 1.- 2009 Jan. 1 33s 2009 Jan. 1.- 2012 Jul 1 34s 2012 Jul 1 - 2015 Jul 1 35s - 2015 Jul 1 - 36s + 2015 Jul 1 - 2017 Jan 1 36s + 2017 Jan 1 - 37s ---------------------------------------------------------------------- diff --git a/src/test/resources/zero-EOP/eopc04_08_IAU2000.00 b/src/test/resources/zero-EOP/eopc04_08_IAU2000.00 index 2c3385c4bc..a21a68dad3 100644 --- a/src/test/resources/zero-EOP/eopc04_08_IAU2000.00 +++ b/src/test/resources/zero-EOP/eopc04_08_IAU2000.00 @@ -1,7 +1,8 @@ THIS IS A FAKE EOP FILE, WITH NULL MODEL FOR POLE AND DUT1 THIS FILE IS INTENTED FOR TEST PURPOSES ONLY! - + EOP (IERS) 05 C04 + Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err 2000 2 10 51584 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 2000 2 11 51585 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 2000 2 12 51586 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000 0.000000 0.000000 0.0000000 0.0000000 0.000000 0.000000

        The EOP xx C04 files retrieved from the ftp site - * ftp://ftp.iers.org/products/eop/long-term/ - * are recognized thanks to their base names, which must match one of the patterns - * {@code eopc04_##_IAU2000.##} or {@code eopc04_##.##} (or the same ending with .gz for - * gzip-compressed files) where # stands for a digit character.